public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v2 03/13] btrace, linux: add perf event buffer abstraction
  2014-11-20 10:47 [PATCH v2 00/13] record btrace: prepare for a new trace format Markus Metzger
                   ` (6 preceding siblings ...)
  2014-11-20 10:47 ` [PATCH v2 12/13] configure: check for libipt Markus Metzger
@ 2014-11-20 10:47 ` Markus Metzger
  2015-01-27 15:31   ` Pedro Alves
  2014-11-20 10:47 ` [PATCH v2 08/13] btrace: move and rename btrace-common Markus Metzger
                   ` (4 subsequent siblings)
  12 siblings, 1 reply; 25+ messages in thread
From: Markus Metzger @ 2014-11-20 10:47 UTC (permalink / raw)
  To: palves; +Cc: gdb-patches

Collect perf event buffer related fields from btrace_target_info into
a new struct perf_event_buffer.  Update functions that operated on the
buffer to take a struct perf_event_buffer pointer rather than a
btrace_target_info pointer.

2014-11-20  Markus Metzger <markus.t.metzger@intel.com>

	* nat/linux-btrace.h (perf_event_buffer): New.
	(btrace_target_info) <buffer, size, data_head>: Replace with ...
	<bts>: ... this.
	* nat/linux-btrace.c (perf_event_header, perf_event_mmap_size)
	(perf_event_buffer_size, perf_event_buffer_begin)
	(perf_event_buffer_end, linux_btrace_has_changed): Removed.
	Updated users.
	(perf_event_new_data): New.
---
 gdb/nat/linux-btrace.c | 113 +++++++++++++++++--------------------------------
 gdb/nat/linux-btrace.h |  34 +++++++++++----
 2 files changed, 63 insertions(+), 84 deletions(-)

diff --git a/gdb/nat/linux-btrace.c b/gdb/nat/linux-btrace.c
index 6ff797c..1976512 100644
--- a/gdb/nat/linux-btrace.c
+++ b/gdb/nat/linux-btrace.c
@@ -59,45 +59,12 @@ struct perf_event_sample
   struct perf_event_bts bts;
 };
 
-/* Get the perf_event header.  */
+/* Return non-zero if there is new data in PEVENT; zero otherwise.  */
 
-static inline volatile struct perf_event_mmap_page *
-perf_event_header (struct btrace_target_info* tinfo)
-{
-  return tinfo->buffer;
-}
-
-/* Get the size of the perf_event mmap buffer.  */
-
-static inline size_t
-perf_event_mmap_size (const struct btrace_target_info *tinfo)
-{
-  /* The branch trace buffer is preceded by a configuration page.  */
-  return (tinfo->size + 1) * PAGE_SIZE;
-}
-
-/* Get the size of the perf_event buffer.  */
-
-static inline size_t
-perf_event_buffer_size (struct btrace_target_info* tinfo)
-{
-  return tinfo->size * PAGE_SIZE;
-}
-
-/* Get the start address of the perf_event buffer.  */
-
-static inline const uint8_t *
-perf_event_buffer_begin (struct btrace_target_info* tinfo)
-{
-  return ((const uint8_t *) tinfo->buffer) + PAGE_SIZE;
-}
-
-/* Get the end address of the perf_event buffer.  */
-
-static inline const uint8_t *
-perf_event_buffer_end (struct btrace_target_info* tinfo)
+static int
+perf_event_new_data (const struct perf_event_buffer *pev)
 {
-  return perf_event_buffer_begin (tinfo) + perf_event_buffer_size (tinfo);
+  return *pev->data_head != pev->last_head;
 }
 
 /* Check whether an address is in the kernel.  */
@@ -162,11 +129,12 @@ perf_event_sample_ok (const struct perf_event_sample *sample)
 
 static VEC (btrace_block_s) *
 perf_event_read_bts (struct btrace_target_info* tinfo, const uint8_t *begin,
-		     const uint8_t *end, const uint8_t *start, size_t size)
+		     const uint8_t *end, const uint8_t *start,
+		     unsigned long long size)
 {
   VEC (btrace_block_s) *btrace = NULL;
   struct perf_event_sample sample;
-  size_t read = 0;
+  unsigned long long read = 0;
   struct btrace_block block = { 0, 0 };
   struct regcache *regcache;
 
@@ -432,6 +400,7 @@ linux_supports_btrace (struct target_ops *ops, enum btrace_format format)
 struct btrace_target_info *
 linux_enable_btrace (ptid_t ptid)
 {
+  struct perf_event_mmap_page *header;
   struct btrace_target_info *tinfo;
   int pid, pg;
 
@@ -467,15 +436,24 @@ linux_enable_btrace (ptid_t ptid)
   for (pg = 4; pg >= 0; --pg)
     {
       /* The number of pages we request needs to be a power of two.  */
-      tinfo->size = 1 << pg;
-      tinfo->buffer = mmap (NULL, perf_event_mmap_size (tinfo),
-			    PROT_READ, MAP_SHARED, tinfo->file, 0);
-      if (tinfo->buffer == MAP_FAILED)
-	continue;
-
-      return tinfo;
+      header = mmap (NULL, ((1 << pg) + 1) * PAGE_SIZE, PROT_READ, MAP_SHARED,
+		     tinfo->file, 0);
+      if (header != MAP_FAILED)
+	break;
     }
 
+  if (header == MAP_FAILED)
+    goto err_file;
+
+  tinfo->header = header;
+  tinfo->bts.mem = ((const uint8_t *) header) + PAGE_SIZE;
+  tinfo->bts.size = (1 << pg) * PAGE_SIZE;
+  tinfo->bts.data_head = &header->data_head;
+  tinfo->bts.last_head = 0;
+
+  return tinfo;
+
+ err_file:
   /* We were not able to allocate any buffer.  */
   close (tinfo->file);
 
@@ -489,29 +467,13 @@ linux_enable_btrace (ptid_t ptid)
 enum btrace_error
 linux_disable_btrace (struct btrace_target_info *tinfo)
 {
-  int errcode;
-
-  errno = 0;
-  errcode = munmap (tinfo->buffer, perf_event_mmap_size (tinfo));
-  if (errcode != 0)
-    return BTRACE_ERR_UNKNOWN;
-
+  munmap((void *) tinfo->header, tinfo->bts.size + PAGE_SIZE);
   close (tinfo->file);
   xfree (tinfo);
 
   return BTRACE_ERR_NONE;
 }
 
-/* Check whether the branch trace has changed.  */
-
-static int
-linux_btrace_has_changed (struct btrace_target_info *tinfo)
-{
-  volatile struct perf_event_mmap_page *header = perf_event_header (tinfo);
-
-  return header->data_head != tinfo->data_head;
-}
-
 /* Read branch trace data in BTS format for the thread given by TINFO into
    BTRACE using the TYPE reading method.  */
 
@@ -520,24 +482,25 @@ linux_read_bts (struct btrace_data_bts *btrace,
 		struct btrace_target_info *tinfo,
 		enum btrace_read_type type)
 {
-  volatile struct perf_event_mmap_page *header;
+  struct perf_event_buffer *pevent;
   const uint8_t *begin, *end, *start;
-  unsigned long data_head, data_tail, retries = 5;
-  size_t buffer_size, size;
+  unsigned long long data_head, data_tail, buffer_size, size;
+  size_t retries = 5;
+
+  pevent = &tinfo->bts;
 
   /* For delta reads, we return at least the partial last block containing
      the current PC.  */
-  if (type == BTRACE_READ_NEW && !linux_btrace_has_changed (tinfo))
+  if (type == BTRACE_READ_NEW && !perf_event_new_data (pevent))
     return BTRACE_ERR_NONE;
 
-  header = perf_event_header (tinfo);
-  buffer_size = perf_event_buffer_size (tinfo);
-  data_tail = tinfo->data_head;
+  buffer_size = pevent->size;
+  data_tail = pevent->last_head;
 
   /* We may need to retry reading the trace.  See below.  */
   while (retries--)
     {
-      data_head = header->data_head;
+      data_head = *pevent->data_head;
 
       /* Delete any leftover trace from the previous iteration.  */
       VEC_free (btrace_block_s, btrace->blocks);
@@ -569,13 +532,13 @@ linux_read_bts (struct btrace_data_bts *btrace,
 	}
 
       /* Data_head keeps growing; the buffer itself is circular.  */
-      begin = perf_event_buffer_begin (tinfo);
+      begin = pevent->mem;
       start = begin + data_head % buffer_size;
 
       if (data_head <= buffer_size)
 	end = start;
       else
-	end = perf_event_buffer_end (tinfo);
+	end = begin + pevent->size;
 
       btrace->blocks = perf_event_read_bts (tinfo, begin, end, start, size);
 
@@ -584,11 +547,11 @@ linux_read_bts (struct btrace_data_bts *btrace,
 	 kernel might be writing the last branch trace records.
 
 	 Let's check whether the data head moved while we read the trace.  */
-      if (data_head == header->data_head)
+      if (data_head == *pevent->data_head)
 	break;
     }
 
-  tinfo->data_head = data_head;
+  pevent->last_head = data_head;
 
   /* Prune the incomplete last block (i.e. the first one of inferior execution)
      if we're not doing a delta read.  There is no way of filling in its zeroed
diff --git a/gdb/nat/linux-btrace.h b/gdb/nat/linux-btrace.h
index fc03f4c..01ef934 100644
--- a/gdb/nat/linux-btrace.h
+++ b/gdb/nat/linux-btrace.h
@@ -32,6 +32,24 @@
 
 struct target_ops;
 
+#if HAVE_LINUX_PERF_EVENT_H
+/* A Linux perf event buffer.  */
+struct perf_event_buffer
+{
+  /* The mapped memory.  */
+  const uint8_t *mem;
+
+  /* The size of the mapped memory in bytes.  */
+  unsigned long long size;
+
+  /* A pointer to the data_head field for this buffer. */
+  volatile unsigned long long *data_head;
+
+  /* The data_head value from the last read.  */
+  unsigned long long last_head;
+};
+#endif /* HAVE_LINUX_PERF_EVENT_H */
+
 /* Branch trace target information per thread.  */
 struct btrace_target_info
 {
@@ -42,16 +60,14 @@ struct btrace_target_info
   /* The ptid of this thread.  */
   ptid_t ptid;
 
-  /* The mmap configuration mapping the branch trace perf_event buffer.
-
-     file      .. the file descriptor
-     buffer    .. the mmapped memory buffer
-     size      .. the buffer's size in pages without the configuration page
-     data_head .. the data head from the last read  */
+  /* The perf event file.  */
   int file;
-  void *buffer;
-  size_t size;
-  unsigned long data_head;
+
+  /* The perf event configuration page. */
+  volatile struct perf_event_mmap_page *header;
+
+  /* The BTS perf event buffer.  */
+  struct perf_event_buffer bts;
 #endif /* HAVE_LINUX_PERF_EVENT_H */
 
   /* The size of a pointer in bits for this thread.
-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH v2 11/13] record-btrace: indicate gaps
  2014-11-20 10:47 [PATCH v2 00/13] record btrace: prepare for a new trace format Markus Metzger
                   ` (4 preceding siblings ...)
  2014-11-20 10:47 ` [PATCH v2 10/13] btrace: use the new cpu identifier Markus Metzger
@ 2014-11-20 10:47 ` Markus Metzger
  2014-11-20 17:32   ` Metzger, Markus T
  2014-11-20 10:47 ` [PATCH v2 12/13] configure: check for libipt Markus Metzger
                   ` (6 subsequent siblings)
  12 siblings, 1 reply; 25+ messages in thread
From: Markus Metzger @ 2014-11-20 10:47 UTC (permalink / raw)
  To: palves; +Cc: gdb-patches

Indicate gaps in the trace due to decode errors.  Internally, a gap is
represented as a btrace function segment without instructions and with a
non-zero format-specific error code.

Show the gap when traversing the instruction or function call history.
Also indicate gaps in "info record".

It looks like this:

  (gdb) info record
  Active record target: record-btrace
  Recording format: Intel(R) Branch Trace Store.
  Buffer size: 64KB.
  Recorded 32 instructions in 5 functions (1 gaps) for thread 1 (process 7182).
  (gdb) record function-call-history /cli
  1	fib	inst 1,9	at src/fib.c:9,14
  2	  fib	inst 10,20	at src/fib.c:6,14
  3	[decode error (1): instruction overflow]
  4	fib	inst 21,28	at src/fib.c:11,14
  5	  fib	inst 29,33	at src/fib.c:6,9
  (gdb) record instruction-history 20,22
  20	   0x000000000040062f <fib+47>:	sub    $0x1,%rax
  [decode error (1): instruction overflow]
  21	   0x0000000000400613 <fib+19>:	add    $0x1,%rax
  22	   0x0000000000400617 <fib+23>:	mov    %rax,0x200a3a(%rip)
  (gdb)

Gaps are ignored during reverse execution and replay.

2014-11-20  Markus Metzger  <markus.t.metzger@intel.com>

	* btrace.c (ftrace_find_call): Skip gaps.
	(ftrace_new_gap): New.
	(ftrace_update_function): Create new function after gap.
	(btrace_compute_ftrace_bts): Create gap on error.
	(btrace_clear): Reset the number of gaps.
	(btrace_insn_get): Return NULL if the iterator points to a gap.
	(btrace_insn_number): Return zero if the iterator points to a gap.
	(btrace_insn_end): Assert that the last function is not empty.
	(btrace_insn_next, btrace_insn_prev, btrace_insn_cmp): Handle gaps.
	(btrace_find_insn_by_number): Assert that the found iterator does
	not point to a gap.
	(btrace_call_next, btrace_call_prev): Assert that the last function
	is not a gap.
	* btrace.h (btrace_bts_error): New.
	(btrace_function): Update comment.
	(btrace_function) <insn, insn_offset, number>: Update comment.
	(btrace_function) <errcode>: New.
	(btrace_thread_info) <ngaps>: New.
	(btrace_thread_info) <replay>: Update comment.
	(btrace_insn_get): Update comment.
	* record-btrace.c (btrace_ui_out_decode_error): New.
	(record_btrace_info): Print number of gaps.
	(btrace_insn_history, btrace_call_history): Call
	btrace_ui_out_decode_error for gaps.
	(record_btrace_step_thread): Skip gaps.

testsuite/
	* gdb.btrace/buffer-size.exp: Update "info record" output.
	* gdb.btrace/delta.exp: Update "info record" output.
	* gdb.btrace/enable.exp: Update "info record" output.
	* gdb.btrace/finish.exp: Update "info record" output.
	* gdb.btrace/instruction_history.exp: Update "info record" output.
	* gdb.btrace/next.exp: Update "info record" output.
	* gdb.btrace/nexti.exp: Update "info record" output.
	* gdb.btrace/step.exp: Update "info record" output.
	* gdb.btrace/stepi.exp: Update "info record" output.
	* gdb.btrace/nohist.exp: Update "info record" output.
---
 gdb/btrace.c                                     | 135 +++++++++++++++++--
 gdb/btrace.h                                     |  40 +++++-
 gdb/record-btrace.c                              | 160 ++++++++++++++++++-----
 gdb/testsuite/gdb.btrace/buffer-size.exp         |   4 +-
 gdb/testsuite/gdb.btrace/delta.exp               |   8 +-
 gdb/testsuite/gdb.btrace/enable.exp              |   2 +-
 gdb/testsuite/gdb.btrace/finish.exp              |   2 +-
 gdb/testsuite/gdb.btrace/instruction_history.exp |   2 +-
 gdb/testsuite/gdb.btrace/next.exp                |   4 +-
 gdb/testsuite/gdb.btrace/nexti.exp               |   4 +-
 gdb/testsuite/gdb.btrace/nohist.exp              |   2 +-
 gdb/testsuite/gdb.btrace/step.exp                |   4 +-
 gdb/testsuite/gdb.btrace/stepi.exp               |   4 +-
 13 files changed, 306 insertions(+), 65 deletions(-)

diff --git a/gdb/btrace.c b/gdb/btrace.c
index efd0572..91df7a0 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -336,8 +336,9 @@ ftrace_find_call (struct btrace_function *bfun)
     {
       struct btrace_insn *last;
 
-      /* 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);
 
@@ -446,6 +447,32 @@ ftrace_new_switch (struct btrace_function *prev,
   return bfun;
 }
 
+/* Add a new function segment for a gap in the trace due to a decode error.
+   PREV is the chronologically preceding function segment.
+   ERRCODE is the format-specific error code.  */
+
+static struct btrace_function *
+ftrace_new_gap (struct btrace_function *prev, int errcode)
+{
+  struct btrace_function *bfun;
+
+  /* We hijack prev if it was empty.  */
+  if (prev != NULL && prev->errcode == 0
+      && VEC_empty (btrace_insn_s, prev->insn))
+    bfun = prev;
+  else
+    bfun = ftrace_new_function (prev, NULL, NULL);
+
+  /* This is a gap in the trace.  The call stack will likely be wrong at this
+     point.  We keep the function level, though.  */
+  bfun->level = prev != NULL ? prev->level : 0;
+  bfun->errcode = errcode;
+
+  ftrace_debug (bfun, "new gap");
+
+  return bfun;
+}
+
 /* Update BFUN with respect to the instruction at PC.  This may create new
    function segments.
    Return the chronologically latest function segment, never NULL.  */
@@ -468,8 +495,8 @@ ftrace_update_function (struct btrace_function *bfun, CORE_ADDR pc)
   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)
+  /* If we didn't have a function or if we had a gap before, we create one.  */
+  if (bfun == NULL || bfun->errcode != 0)
     return ftrace_new_function (bfun, mfun, fun);
 
   /* Check the last instruction, if we have one.
@@ -597,13 +624,14 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
   struct btrace_thread_info *btinfo;
   struct btrace_function *begin, *end;
   struct gdbarch *gdbarch;
-  unsigned int blk;
+  unsigned int blk, ngaps;
   int level;
 
   gdbarch = target_gdbarch ();
   btinfo = &tp->btrace;
   begin = btinfo->begin;
   end = btinfo->end;
+  ngaps = btinfo->ngaps;
   level = begin != NULL ? -btinfo->level : INT_MAX;
   blk = VEC_length (btrace_block_s, btrace->blocks);
 
@@ -628,6 +656,11 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
 	    {
 	      warning (_("Recorded trace may be corrupted around %s."),
 		       core_addr_to_string_nz (pc));
+
+	      /* Indicate the gap in the trace.  */
+	      end = ftrace_new_gap (end, BDE_BTS_OVERFLOW);
+	      ngaps += 1;
+
 	      break;
 	    }
 
@@ -660,6 +693,11 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
 	    {
 	      warning (_("Recorded trace may be incomplete around %s."),
 		       core_addr_to_string_nz (pc));
+
+	      /* Indicate the gap in the trace.  */
+	      end = ftrace_new_gap (end, BDE_BTS_INSN_SIZE);
+	      ngaps += 1;
+
 	      break;
 	    }
 
@@ -678,6 +716,7 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
 
   btinfo->begin = begin;
   btinfo->end = end;
+  btinfo->ngaps = ngaps;
 
   /* LEVEL is the minimal function level of all btrace function segments.
      Define the global level offset to -LEVEL so all function levels are
@@ -1009,6 +1048,7 @@ btrace_clear (struct thread_info *tp)
 
   btinfo->begin = NULL;
   btinfo->end = NULL;
+  btinfo->ngaps = 0;
 
   btrace_clear_history (btinfo);
 }
@@ -1206,6 +1246,10 @@ btrace_insn_get (const struct btrace_insn_iterator *it)
   index = it->index;
   bfun = it->function;
 
+  /* 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);
   gdb_assert (0 < end);
@@ -1222,6 +1266,11 @@ btrace_insn_number (const struct btrace_insn_iterator *it)
   const struct btrace_function *bfun;
 
   bfun = it->function;
+
+  /* Return zero if the iterator points to a gap in the trace.  */
+  if (bfun->errcode != 0)
+    return 0;
+
   return bfun->insn_offset + it->index;
 }
 
@@ -1257,6 +1306,7 @@ btrace_insn_end (struct btrace_insn_iterator *it,
   /* 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.  */
   length = VEC_length (btrace_insn_s, bfun->insn);
+  gdb_assert (length > 0);
 
   it->function = bfun;
   it->index = length - 1;
@@ -1280,6 +1330,23 @@ 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)
+	{
+	  stride -= 1;
+	  steps += 1;
+
+	  bfun = bfun->flow.next;
+	  index = 0;
+
+	  /* There won't be a gap at the end since we will always add
+	     an entry for the current PC.  */
+	  gdb_assert (bfun != NULL);
+
+	  continue;
+	}
+
       gdb_assert (0 < end);
       gdb_assert (index < end);
 
@@ -1354,12 +1421,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);
+
       stride -= adv;
       index -= adv;
       steps += adv;
@@ -1386,6 +1461,37 @@ btrace_insn_cmp (const struct btrace_insn_iterator *lhs,
   lnum = btrace_insn_number (lhs);
   rnum = btrace_insn_number (rhs);
 
+  /* A gap has an instruction number of zero.  Things are getting more
+     complicated if gaps are involved.
+
+     We take the instruction number offset from the iterator's function.
+     This is the number of the first instruction after the gap.
+
+     This is OK as long as both lhs and rhs point to gaps.  If only one of
+     them does, we need to adjust the number based on the other's regular
+     instruction number.  Otherwise, a gap might compare equal to an
+     instruction.  */
+
+  if (lnum == 0 && rnum == 0)
+    {
+      lnum = lhs->function->insn_offset;
+      rnum = rhs->function->insn_offset;
+    }
+  else if (lnum == 0)
+    {
+      lnum = lhs->function->insn_offset;
+
+      if (lnum == rnum)
+	lnum -= 1;
+    }
+  else if (rnum == 0)
+    {
+      rnum = rhs->function->insn_offset;
+
+      if (rnum == lnum)
+	rnum -= 1;
+    }
+
   return (int) (lnum - rnum);
 }
 
@@ -1397,7 +1503,7 @@ btrace_find_insn_by_number (struct btrace_insn_iterator *it,
 			    unsigned int number)
 {
   const struct btrace_function *bfun;
-  unsigned int end;
+  unsigned int end, length;
 
   for (bfun = btinfo->end; bfun != NULL; bfun = bfun->flow.prev)
     if (bfun->insn_offset <= number)
@@ -1406,7 +1512,12 @@ btrace_find_insn_by_number (struct btrace_insn_iterator *it,
   if (bfun == NULL)
     return 0;
 
-  end = bfun->insn_offset + VEC_length (btrace_insn_s, bfun->insn);
+  /* Since we're searching backwards from the end, we will never find gaps;
+     we will already stop at the function segment succeeding a gap.  */
+  length = VEC_length (btrace_insn_s, bfun->insn);
+  gdb_assert (length > 0);
+
+  end = bfun->insn_offset + length;
   if (end <= number)
     return 0;
 
@@ -1508,6 +1619,9 @@ btrace_call_next (struct btrace_call_iterator *it, unsigned int stride)
 	  insns = VEC_length (btrace_insn_s, bfun->insn);
 	  if (insns == 1)
 	    steps -= 1;
+
+	  /* We won't have gaps at the end.  */
+	  gdb_assert (insns > 0);
 	}
 
       if (stride == steps)
@@ -1548,6 +1662,9 @@ btrace_call_prev (struct btrace_call_iterator *it, unsigned int stride)
       if (insns == 1)
 	bfun = bfun->flow.prev;
 
+      /* We won't have gaps at the end.  */
+      gdb_assert (insns > 0);
+
       if (bfun == NULL)
 	return 0;
 
diff --git a/gdb/btrace.h b/gdb/btrace.h
index 2082bc7..71be12e 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -86,12 +86,25 @@ enum btrace_function_flag
   BFUN_UP_LINKS_TO_TAILCALL = (1 << 1)
 };
 
+/* Decode errors for the BTS recording format.  */
+enum btrace_bts_error
+{
+  /* The instruction trace overflowed the end of the trace block.  */
+  BDE_BTS_OVERFLOW = 1,
+
+  /* The instruction size could not be determined.  */
+  BDE_BTS_INSN_SIZE
+};
+
 /* A branch trace function segment.
 
    This represents a function segment in a branch trace, i.e. a consecutive
    number of instructions belonging to the same function.
 
-   We do not allow function segments without any instructions.  */
+   In case of decode errors, we add an empty function segment to indicate
+   the gap in the trace.
+
+   We do not allow function segments without instructions otherwise.  */
 struct btrace_function
 {
   /* The full and minimal symbol for the function.  Both may be NULL.  */
@@ -110,14 +123,23 @@ struct btrace_function
   struct btrace_function *up;
 
   /* The instructions in this function segment.
-     The instruction vector will never be empty.  */
+     The instruction vector will be empty if the function segment
+     represents a decode error.  */
   VEC (btrace_insn_s) *insn;
 
+  /* The error code of a decode error that led to a gap.
+     Must be zero unless INSN is empty; non-zero otherwise.  */
+  int errcode;
+
   /* The instruction number offset for the first instruction in this
-     function segment.  */
+     function segment.
+     If INSN is empty this is the insn_offset of the succeding function
+     segment in control-flow order.  */
   unsigned int insn_offset;
 
-  /* The function number in control-flow order.  */
+  /* The function number in control-flow order.
+     If INSN is empty indicating a gap in the trace due to a decode error,
+     we still count the gap as a function.  */
   unsigned int number;
 
   /* The function level in a back trace across the entire branch trace.
@@ -223,6 +245,9 @@ struct btrace_thread_info
      becomes zero.  */
   int level;
 
+  /* The number of gaps in the trace.  */
+  unsigned int ngaps;
+
   /* A bit-vector of btrace_thread_flag.  */
   enum btrace_thread_flag flags;
 
@@ -232,7 +257,9 @@ struct btrace_thread_info
   /* The function call history iterator.  */
   struct btrace_call_history *call_history;
 
-  /* The current replay position.  NULL if not replaying.  */
+  /* The current replay position.  NULL if not replaying.
+     Gaps are skipped during replay, so REPLAY always points to a valid
+     instruction.  */
   struct btrace_insn_iterator *replay;
 };
 
@@ -270,7 +297,8 @@ extern void parse_xml_btrace (struct btrace_data *data, const char *xml);
 extern void parse_xml_btrace_conf (struct btrace_config *conf, const char *xml);
 
 /* Dereference a branch trace instruction iterator.  Return a pointer to the
-   instruction the iterator points to.  */
+   instruction the iterator points to.
+   May return NULL if the iterator points to a gap in the trace.  */
 extern const struct btrace_insn *
   btrace_insn_get (const struct btrace_insn_iterator *);
 
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index d9c1456..3075b8e 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -350,7 +350,7 @@ record_btrace_info (struct target_ops *self)
   struct btrace_thread_info *btinfo;
   const struct btrace_config *conf;
   struct thread_info *tp;
-  unsigned int insns, calls;
+  unsigned int insns, calls, gaps;
 
   DEBUG ("info");
 
@@ -368,6 +368,7 @@ record_btrace_info (struct target_ops *self)
 
   insns = 0;
   calls = 0;
+  gaps = 0;
 
   if (!btrace_is_empty (tp))
     {
@@ -379,19 +380,67 @@ record_btrace_info (struct target_ops *self)
       calls = btrace_call_number (&call);
 
       btrace_insn_end (&insn, btinfo);
-      btrace_insn_prev (&insn, 1);
       insns = btrace_insn_number (&insn);
+      /* The last instruction does not really belong to the trace.  */
+      insns -= 1;
+
+      gaps = btinfo->ngaps;
     }
 
-  printf_unfiltered (_("Recorded %u instructions in %u functions for thread "
-		       "%d (%s).\n"), insns, calls, tp->num,
-		     target_pid_to_str (tp->ptid));
+  printf_unfiltered (_("Recorded %u instructions in %u functions (%u gaps) "
+		       "for thread %d (%s).\n"), insns, calls, gaps,
+		     tp->num, target_pid_to_str (tp->ptid));
 
   if (btrace_is_replaying (tp))
     printf_unfiltered (_("Replay in progress.  At instruction %u.\n"),
 		       btrace_insn_number (btinfo->replay));
 }
 
+/* Print a decode error.  */
+
+static void
+btrace_ui_out_decode_error (struct ui_out *uiout, int errcode,
+			    enum btrace_format format)
+{
+  const char *errstr;
+  int is_error;
+
+  errstr = _("unknown");
+  is_error = 1;
+
+  switch (format)
+    {
+    default:
+      break;
+
+    case BTRACE_FORMAT_BTS:
+      switch (errcode)
+	{
+	default:
+	  break;
+
+	case BDE_BTS_OVERFLOW:
+	  errstr = _("instruction overflow");
+	  break;
+
+	case BDE_BTS_INSN_SIZE:
+	  errstr = _("unknown instruction");
+	  break;
+	}
+      break;
+    }
+
+  ui_out_text (uiout, _("["));
+  if (is_error)
+    {
+      ui_out_text (uiout, _("decode error ("));
+      ui_out_field_int (uiout, "errcode", errcode);
+      ui_out_text (uiout, _("): "));
+    }
+  ui_out_text (uiout, errstr);
+  ui_out_text (uiout, _("]\n"));
+}
+
 /* Print an unsigned int.  */
 
 static void
@@ -404,6 +453,7 @@ ui_out_field_uint (struct ui_out *uiout, const char *fld, unsigned int val)
 
 static void
 btrace_insn_history (struct ui_out *uiout,
+		     const struct btrace_thread_info *btinfo,
 		     const struct btrace_insn_iterator *begin,
 		     const struct btrace_insn_iterator *end, int flags)
 {
@@ -421,13 +471,30 @@ btrace_insn_history (struct ui_out *uiout,
 
       insn = btrace_insn_get (&it);
 
-      /* Print the instruction index.  */
-      ui_out_field_uint (uiout, "index", btrace_insn_number (&it));
-      ui_out_text (uiout, "\t");
+      /* A NULL instruction indicates a gap in the trace.  */
+      if (insn == NULL)
+	{
+	  const struct btrace_config *conf;
+
+	  conf = btrace_conf (btinfo);
 
-      /* Disassembly with '/m' flag may not produce the expected result.
-	 See PR gdb/11833.  */
-      gdb_disassembly (gdbarch, uiout, NULL, flags, 1, insn->pc, insn->pc + 1);
+	  /* We have trace so we must have a configuration.  */
+	  gdb_assert (conf != NULL);
+
+	  btrace_ui_out_decode_error (uiout, it.function->errcode,
+				      conf->format);
+	}
+      else
+	{
+	  /* Print the instruction index.  */
+	  ui_out_field_uint (uiout, "index", btrace_insn_number (&it));
+	  ui_out_text (uiout, "\t");
+
+	  /* Disassembly with '/m' flag may not produce the expected result.
+	     See PR gdb/11833.  */
+	  gdb_disassembly (gdbarch, uiout, NULL, flags, 1, insn->pc,
+			   insn->pc + 1);
+	}
     }
 }
 
@@ -504,7 +571,7 @@ record_btrace_insn_history (struct target_ops *self, int size, int flags)
     }
 
   if (covered > 0)
-    btrace_insn_history (uiout, &begin, &end, flags);
+    btrace_insn_history (uiout, btinfo, &begin, &end, flags);
   else
     {
       if (size < 0)
@@ -564,7 +631,7 @@ record_btrace_insn_history_range (struct target_ops *self,
       btrace_insn_next (&end, 1);
     }
 
-  btrace_insn_history (uiout, &begin, &end, flags);
+  btrace_insn_history (uiout, btinfo, &begin, &end, flags);
   btrace_set_insn_history (btinfo, &begin, &end);
 
   do_cleanups (uiout_cleanup);
@@ -705,6 +772,21 @@ btrace_call_history (struct ui_out *uiout,
       ui_out_field_uint (uiout, "index", bfun->number);
       ui_out_text (uiout, "\t");
 
+      /* Indicate gaps in the trace.  */
+      if (bfun->errcode != 0)
+	{
+	  const struct btrace_config *conf;
+
+	  conf = btrace_conf (btinfo);
+
+	  /* We have trace so we must have a configuration.  */
+	  gdb_assert (conf != NULL);
+
+	  btrace_ui_out_decode_error (uiout, bfun->errcode, conf->format);
+
+	  continue;
+	}
+
       if ((flags & RECORD_PRINT_INDENT_CALLS) != 0)
 	{
 	  int level = bfun->level + btinfo->level, i;
@@ -1713,9 +1795,13 @@ record_btrace_step_thread (struct thread_info *tp)
       if (replay == NULL)
 	return btrace_step_no_history ();
 
-      /* We are always able to step at least once.  */
-      steps = btrace_insn_next (replay, 1);
-      gdb_assert (steps == 1);
+      /* We are always able to step at least once - to the last instruction.
+	 Skip gaps during replay.  */
+      do
+	{
+	  steps = btrace_insn_next (replay, 1);
+	  gdb_assert (steps == 1);
+	} while (btrace_insn_get (replay) == NULL);
 
       /* Determine the end of the instruction trace.  */
       btrace_insn_end (&end, btinfo);
@@ -1731,10 +1817,14 @@ record_btrace_step_thread (struct thread_info *tp)
       if (replay == NULL)
 	replay = record_btrace_start_replaying (tp);
 
-      /* If we can't step any further, we reached the end of the history.  */
-      steps = btrace_insn_prev (replay, 1);
-      if (steps == 0)
-	return btrace_step_no_history ();
+      /* If we can't step any further, we reached the end of the history.
+	 Skip gaps during replay.  */
+      do
+	{
+	  steps = btrace_insn_prev (replay, 1);
+	  if (steps == 0)
+	    return btrace_step_no_history ();
+	} while (btrace_insn_get (replay) == NULL);
 
       return btrace_step_stopped ();
 
@@ -1753,9 +1843,15 @@ record_btrace_step_thread (struct thread_info *tp)
 	{
 	  const struct btrace_insn *insn;
 
-	  /* We are always able to step at least once.  */
-	  steps = btrace_insn_next (replay, 1);
-	  gdb_assert (steps == 1);
+	  /* We are always able to step at least once - to the last instruction.
+	     Skip gaps during replay.  */
+	  do
+	    {
+	      steps = btrace_insn_next (replay, 1);
+	      gdb_assert (steps == 1);
+
+	      insn = btrace_insn_get (replay);
+	    } while (insn == NULL);
 
 	  /* We stop replaying if we reached the end of the trace.  */
 	  if (btrace_insn_cmp (replay, &end) == 0)
@@ -1764,9 +1860,6 @@ record_btrace_step_thread (struct thread_info *tp)
 	      return btrace_step_no_history ();
 	    }
 
-	  insn = btrace_insn_get (replay);
-	  gdb_assert (insn);
-
 	  DEBUG ("stepping %d (%s) ... %s", tp->num,
 		 target_pid_to_str (tp->ptid),
 		 core_addr_to_string_nz (insn->pc));
@@ -1787,13 +1880,16 @@ record_btrace_step_thread (struct thread_info *tp)
 	{
 	  const struct btrace_insn *insn;
 
-	  /* If we can't step any further, we're done.  */
-	  steps = btrace_insn_prev (replay, 1);
-	  if (steps == 0)
-	    return btrace_step_no_history ();
+	  /* If we can't step any further, we reached the end of the history.
+	     Skip gaps during replay.  */
+	  do
+	    {
+	      steps = btrace_insn_prev (replay, 1);
+	      if (steps == 0)
+		return btrace_step_no_history ();
 
-	  insn = btrace_insn_get (replay);
-	  gdb_assert (insn);
+	      insn = btrace_insn_get (replay);
+	    } while (insn == NULL);
 
 	  DEBUG ("reverse-stepping %d (%s) ... %s", tp->num,
 		 target_pid_to_str (tp->ptid),
diff --git a/gdb/testsuite/gdb.btrace/buffer-size.exp b/gdb/testsuite/gdb.btrace/buffer-size.exp
index 9d36361..51d861c 100644
--- a/gdb/testsuite/gdb.btrace/buffer-size.exp
+++ b/gdb/testsuite/gdb.btrace/buffer-size.exp
@@ -39,7 +39,7 @@ gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
   "Recording format: Intel\\\(R\\\) Branch Trace Store\." \
   "Buffer size: 4kB\." \
-  "Recorded 0 instructions in 0 functions for \[^\\\r\\\n\]*" \
+  "Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
   ] "\r\n"] "info record with small bts buffer"
 gdb_test "record stop" ".*" "stop recording with small bts buffer"
 
@@ -52,6 +52,6 @@ gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
   "Recording format: Intel\\\(R\\\) Branch Trace Store\." \
   "Buffer size: .*\." \
-  "Recorded 0 instructions in 0 functions for \[^\\\r\\\n\]*" \
+  "Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
   ] "\r\n"] "info record with unlimited bts buffer"
 gdb_test "record stop" ".*" "stop recording with unlimited bts buffer"
diff --git a/gdb/testsuite/gdb.btrace/delta.exp b/gdb/testsuite/gdb.btrace/delta.exp
index 71ccf07..d594102 100644
--- a/gdb/testsuite/gdb.btrace/delta.exp
+++ b/gdb/testsuite/gdb.btrace/delta.exp
@@ -40,7 +40,7 @@ with_test_prefix "no trace" {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
     "Recording format: .*" \
-    "Recorded 0 instructions in 0 functions for .*" \
+    "Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for .*" \
     ] "\r\n"]
   gdb_test "record instruction-history" "No trace\."
   gdb_test "record function-call-history" "No trace\."
@@ -53,7 +53,7 @@ proc check_trace {} {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
     "Recording format: .*" \
-    "Recorded 1 instructions in 1 functions for .*" \
+    "Recorded 1 instructions in 1 functions \\\(0 gaps\\\) for .*" \
     ] "\r\n"]
   gdb_test "record instruction-history /f 1" \
     "1\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tmov *\\\$0x0,%eax\r"
@@ -74,7 +74,7 @@ gdb_test "reverse-stepi"
 gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
   "Recording format: .*" \
-  "Recorded 1 instructions in 1 functions for .*" \
+  "Recorded 1 instructions in 1 functions \\\(0 gaps\\\) for .*" \
   "Replay in progress\.  At instruction 1\." \
   ] "\r\n"] "reverse-stepi"
 
@@ -83,5 +83,5 @@ gdb_test "stepi"
 gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
   "Recording format: .*" \
-  "Recorded 1 instructions in 1 functions for .*" \
+  "Recorded 1 instructions in 1 functions \\\(0 gaps\\\) for .*" \
   ] "\r\n"] "and back"
diff --git a/gdb/testsuite/gdb.btrace/enable.exp b/gdb/testsuite/gdb.btrace/enable.exp
index 2926c0f..37203a3 100644
--- a/gdb/testsuite/gdb.btrace/enable.exp
+++ b/gdb/testsuite/gdb.btrace/enable.exp
@@ -58,7 +58,7 @@ gdb_test "record full" "The process is already being recorded\\.  Use \"record s
 # no trace recorded yet
 gdb_test "info record" "Active record target: record-btrace\r
 .*\r
-Recorded 0 instructions in 0 functions for thread 1.*\\." "info record without trace"
+Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for thread 1.*\\." "info record without trace"
 
 # stop btrace record
 gdb_test "record stop" "Process record is stopped and all execution logs are deleted\\." "record stop"
diff --git a/gdb/testsuite/gdb.btrace/finish.exp b/gdb/testsuite/gdb.btrace/finish.exp
index 593055b..c55c9de 100644
--- a/gdb/testsuite/gdb.btrace/finish.exp
+++ b/gdb/testsuite/gdb.btrace/finish.exp
@@ -38,7 +38,7 @@ proc check_replay_at { insn } {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
     "Recording format: .*" \
-    "Recorded 40 instructions in 16 functions for .*" \
+    "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
     "Replay in progress\.  At instruction $insn\." \
     ] "\r\n"]
 }
diff --git a/gdb/testsuite/gdb.btrace/instruction_history.exp b/gdb/testsuite/gdb.btrace/instruction_history.exp
index 5a77357..e61a297 100644
--- a/gdb/testsuite/gdb.btrace/instruction_history.exp
+++ b/gdb/testsuite/gdb.btrace/instruction_history.exp
@@ -50,7 +50,7 @@ gdb_continue_to_breakpoint "cont to $bp_location" ".*$srcfile2:$bp_location.*"
 set traced {}
 set testname "determine number of recorded instructions"
 gdb_test_multiple "info record" $testname {
-    -re "Active record target: record-btrace\r\n.*\r\nRecorded \(\[0-9\]*\) instructions in \(\[0-9\]*\) functions for thread 1 .*\\.\r\n$gdb_prompt $" {
+    -re "Active record target: record-btrace\r\n.*\r\nRecorded \(\[0-9\]*\) instructions in \(\[0-9\]*\) functions \\\(0 gaps\\\) for thread 1 .*\\.\r\n$gdb_prompt $" {
         set traced $expect_out(1,string)
         pass $testname
     }
diff --git a/gdb/testsuite/gdb.btrace/next.exp b/gdb/testsuite/gdb.btrace/next.exp
index 1bc8125..7bf7cc9 100644
--- a/gdb/testsuite/gdb.btrace/next.exp
+++ b/gdb/testsuite/gdb.btrace/next.exp
@@ -38,7 +38,7 @@ proc check_replay_at { insn } {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
     "Recording format: .*" \
-    "Recorded 40 instructions in 16 functions for .*" \
+    "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
     "Replay in progress\.  At instruction $insn\." \
     ] "\r\n"]
 }
@@ -57,7 +57,7 @@ gdb_test "next" ".*main\.3.*"
 gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
   "Recording format: .*" \
-  "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
+  "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
   ] "\r\n"] "next back"
 
 # let's go somewhere where we can step some more
diff --git a/gdb/testsuite/gdb.btrace/nexti.exp b/gdb/testsuite/gdb.btrace/nexti.exp
index a263607..2cdaf73 100644
--- a/gdb/testsuite/gdb.btrace/nexti.exp
+++ b/gdb/testsuite/gdb.btrace/nexti.exp
@@ -38,7 +38,7 @@ proc check_replay_at { insn } {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
     "Recording format: .*" \
-    "Recorded 40 instructions in 16 functions for .*" \
+    "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
     "Replay in progress\.  At instruction $insn\." \
     ] "\r\n"]
 }
@@ -57,7 +57,7 @@ gdb_test "nexti" ".*main\.3.*" "next, 1.5"
 gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
   "Recording format: .*" \
-  "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
+  "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
   ] "\r\n"] "nexti back"
 
 # let's go somewhere where we can step some more
diff --git a/gdb/testsuite/gdb.btrace/nohist.exp b/gdb/testsuite/gdb.btrace/nohist.exp
index 4c1d875..6925193 100644
--- a/gdb/testsuite/gdb.btrace/nohist.exp
+++ b/gdb/testsuite/gdb.btrace/nohist.exp
@@ -34,7 +34,7 @@ proc check_not_replaying {} {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
     "Recording format: .*" \
-	"Recorded 0 instructions in 0 functions for \[^\\\r\\\n\]*" \
+	"Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
     ] "\r\n"]
 }
 
diff --git a/gdb/testsuite/gdb.btrace/step.exp b/gdb/testsuite/gdb.btrace/step.exp
index 483166b..86ffa0a 100644
--- a/gdb/testsuite/gdb.btrace/step.exp
+++ b/gdb/testsuite/gdb.btrace/step.exp
@@ -38,7 +38,7 @@ proc check_replay_at { insn } {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
     "Recording format: .*" \
-    "Recorded 40 instructions in 16 functions for .*" \
+    "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
     "Replay in progress\.  At instruction $insn\." \
     ] "\r\n"]
 }
@@ -87,5 +87,5 @@ gdb_test "step" ".*main\.3.*"
 gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
   "Recording format: .*" \
-  "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
+  "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
   ] "\r\n"] "step to live"
diff --git a/gdb/testsuite/gdb.btrace/stepi.exp b/gdb/testsuite/gdb.btrace/stepi.exp
index cce41e6..5c9625c 100644
--- a/gdb/testsuite/gdb.btrace/stepi.exp
+++ b/gdb/testsuite/gdb.btrace/stepi.exp
@@ -36,7 +36,7 @@ proc check_replay_at { insn } {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
     "Recording format: .*" \
-    "Recorded 40 instructions in 16 functions for .*" \
+    "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
     "Replay in progress\.  At instruction $insn\." \
     ] "\r\n"]
 }
@@ -61,7 +61,7 @@ gdb_test "stepi" ".*main\.3.*"
 gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
   "Recording format: .*" \
-  "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
+  "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
   ] "\r\n"] "stepi to live"
 
 # let's step from a goto position somewhere in the middle
-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH v2 13/13] [wip] btrace: support Intel(R) Processor Trace
  2014-11-20 10:47 [PATCH v2 00/13] record btrace: prepare for a new trace format Markus Metzger
                   ` (11 preceding siblings ...)
  2014-11-20 10:47 ` [PATCH v2 01/13] btrace: add struct btrace_data Markus Metzger
@ 2014-11-20 10:47 ` Markus Metzger
  2014-11-20 16:32   ` Eli Zaretskii
  12 siblings, 1 reply; 25+ messages in thread
From: Markus Metzger @ 2014-11-20 10:47 UTC (permalink / raw)
  To: palves; +Cc: gdb-patches

Adds a new command "record btrace pt" to configure the kernel to use
Intel Processor Trace instead of Intel Branch Trace Strore.

The "record btrace" command chooses the tracing format automatically.

Intel Processor Trace support further requires kernel support

    https://lkml.org/lkml/2014/11/14/292

and a separate decoder library

    https://github.com/01org/processor-trace

2014-11-20  Markus Metzger <markus.t.metzger@intel.com>
---
 gdb/NEWS                     |  10 ++
 gdb/btrace.c                 | 419 ++++++++++++++++++++++++++++++++++++++++++-
 gdb/btrace.h                 |  19 ++
 gdb/doc/gdb.texinfo          | 120 ++++++++++++-
 gdb/features/btrace-conf.dtd |   7 +-
 gdb/features/btrace.dtd      |  16 +-
 gdb/gdbserver/linux-low.c    |  85 ++++++++-
 gdb/gdbserver/server.c       |  51 +++++-
 gdb/nat/linux-btrace.c       | 404 ++++++++++++++++++++++++++++++++++++++++-
 gdb/nat/linux-btrace.h       |  19 ++
 gdb/nat/x86-btrace.c         |  10 ++
 gdb/nat/x86-btrace.h         |  39 +++-
 gdb/record-btrace.c          | 129 +++++++++++++
 gdb/remote.c                 |  67 ++++++-
 14 files changed, 1362 insertions(+), 33 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index b19faf5..ed223c1 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -31,6 +31,10 @@ record btrace bts
 record bts
   Start branch trace recording using Intel(R) Branch Trace Store format.
 
+record btrace pt
+record pt
+  Start branch trace recording using Intel(R) Processor Trace format.
+
 * New options
 
 set|show record btrace bts buffer-size
@@ -72,6 +76,12 @@ SGI Irix-6.x				mips-*-irix6*
 VAX running (4.2 - 4.3 Reno) BSD 	vax-*-bsd*
 VAX running Ultrix 			vax-*-ultrix*
 
+set|show record btrace pt buffer-size
+  Set and show the size of the ring buffer used for branch tracing in
+  Intel(R) Processor Trace format.
+  The obtained size may differ from the requested size.  Use "info
+  record" to see the obtained buffer size.
+
 * New remote packets
 
 qXfer:btrace-conf:read
diff --git a/gdb/btrace.c b/gdb/btrace.c
index 91df7a0..caf18c6 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -31,6 +31,11 @@
 #include "filenames.h"
 #include "xml-support.h"
 #include "regcache.h"
+#include "rsp-low.h"
+
+#include <inttypes.h>
+
+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.  */
@@ -724,6 +729,252 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
   btinfo->level = -level;
 }
 
+#if defined (HAVE_LIBIPT)
+
+static enum btrace_insn_class
+pt_reclassify_insn (enum pt_insn_class iclass)
+{
+  switch (iclass)
+    {
+    case ptic_call:
+      return BTRACE_INSN_CALL;
+
+    case ptic_return:
+      return BTRACE_INSN_RETURN;
+
+    case ptic_jump:
+      return BTRACE_INSN_JUMP;
+
+    default:
+      return BTRACE_INSN_OTHER;
+    }
+}
+
+/* Add function branch trace using DECODER.  */
+
+static void
+ftrace_add_pt (struct pt_insn_decoder *decoder,
+	       struct btrace_function **pbegin,
+	       struct btrace_function **pend, int *plevel,
+	       unsigned int *ngaps)
+{
+  struct btrace_function *begin, *end, *upd;
+  uint64_t offset;
+  int errcode, nerrors;
+
+  begin = *pbegin;
+  end = *pend;
+  nerrors = 0;
+  for (;;)
+    {
+      struct btrace_insn btinsn;
+      struct pt_insn insn;
+
+      errcode = pt_insn_sync_forward (decoder);
+      if (errcode < 0)
+	{
+	  if (errcode != -pte_eos)
+	    warning (_("Failed to synchronize on the trace stream: %s."),
+		     pt_errstr (pt_errcode (errcode)));
+	  break;
+	}
+
+      memset (&btinsn, 0, sizeof (btinsn));
+      for (;;)
+	{
+	  errcode = pt_insn_next (decoder, &insn);
+	  if (errcode < 0)
+	    break;
+
+	  /* Look for gaps in the trace - unless we're at the beginning.  */
+	  if (begin != NULL)
+	    {
+	      /* 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.  */
+	      if (insn.enabled)
+		*pend = end = ftrace_new_gap (end, BDE_PT_DISABLED);
+
+	      /* Indicate trace overflows.  */
+	      if (insn.resynced)
+		*pend = end = ftrace_new_gap (end, BDE_PT_OVERFLOW);
+	    }
+
+	  upd = ftrace_update_function (end, insn.ip);
+	  if (upd != end)
+	    {
+	      *pend = end = upd;
+
+	      if (begin == NULL)
+		*pbegin = begin = upd;
+	    }
+
+	  /* Maintain the function level offset.  */
+	  *plevel = min (*plevel, end->level);
+
+	  btinsn.pc = (CORE_ADDR) insn.ip;
+	  btinsn.size = (gdb_byte) insn.size;
+	  btinsn.iclass = pt_reclassify_insn (insn.iclass);
+
+	  ftrace_update_insns (end, &btinsn);
+	  ftrace_update_lines (end, insn.ip);
+	}
+
+      if (errcode == -pte_eos)
+	break;
+
+      /* If the gap is at the very beginning, we ignore it - we will have
+	 less trace, but we won't have any holes in the trace.  */
+      if (begin == NULL)
+	continue;
+
+      pt_insn_get_offset (decoder, &offset);
+
+      warning (_("decode error near offset 0x%" PRIx64
+		 " near PC 0x%" PRIx64 ": %s."), offset, insn.ip,
+	       pt_errstr (pt_errcode (errcode)));
+
+      /* Indicate the gap in the trace.  */
+      *pend = end = ftrace_new_gap (end, errcode);
+      *ngaps += 1;
+    }
+
+  if (nerrors > 0)
+    warning (_("The trace may have gaps."));
+}
+
+/* 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, CORE_ADDR pc,
+			    void *context)
+{
+  volatile struct gdb_exception except;
+  int errcode;
+
+  TRY_CATCH (except, RETURN_MASK_ERROR)
+    {
+      errcode = target_read_code (pc, buffer, size);
+      if (errcode != 0)
+	return -pte_nomap;
+    }
+
+  if (except.reason != 0)
+    return -pte_nomap;
+
+  return size;
+}
+
+/* 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;
+    }
+}
+
+/* Compute the function branch trace from Intel(R) Processor Trace.  */
+
+static void
+btrace_compute_ftrace_pt (struct thread_info *tp,
+			  const struct btrace_data_pt *btrace)
+{
+  volatile struct gdb_exception except;
+  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;
+  level = btinfo->begin != NULL ? -btinfo->level : INT_MAX;
+
+  memset (&config, 0, sizeof(config));
+
+  config.size = sizeof (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 trace decoder: %s."),
+	   pt_errstr (pt_errcode (errcode)));
+
+  decoder = pt_insn_alloc_decoder (&config);
+  if (decoder == NULL)
+    error (_("Failed to allocate trace decoder."));
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      struct pt_image *image;
+
+      image = pt_insn_get_image(decoder);
+      if (image == NULL)
+	error (_("Failed to configure instruction decoder."));
+
+      errcode = pt_image_set_callback(image, btrace_pt_readmem_callback, NULL);
+      if (errcode < 0)
+	error (_("Failed to configure instruction decoder: %s."),
+	       pt_errstr (pt_errcode (errcode)));
+
+      ftrace_add_pt (decoder, &btinfo->begin, &btinfo->end, &level,
+		     &btinfo->ngaps);
+    }
+
+  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.  */
+  btinfo->level = -level;
+
+  /* Indicate a gap in the trace if we quit trace processing.  Errors were
+     already logged before.  */
+  if (except.reason == RETURN_QUIT && btinfo->end != NULL)
+    {
+      btinfo->end = ftrace_new_gap (btinfo->end, BDE_PT_USER_QUIT);
+      btinfo->ngaps++;
+    }
+
+  /* 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);
+
+  if (except.reason < 0)
+    throw_exception (except);
+}
+
+#else /* defined (HAVE_LIBIPT)  */
+
+static void
+btrace_compute_ftrace_pt (struct thread_info *tp,
+			  const struct btrace_data_pt *btrace)
+{
+  internal_error (__FILE__, __LINE__, _("Unexpected trace format."));
+}
+
+#endif /* defined (HAVE_LIBIPT)  */
+
 /* Compute the function branch trace from a block branch trace BTRACE for
    a thread given by BTINFO.  */
 
@@ -740,6 +991,10 @@ btrace_compute_ftrace (struct thread_info *tp, struct btrace_data *btrace)
     case BTRACE_FORMAT_BTS:
       btrace_compute_ftrace_bts (tp, &btrace->variant.bts);
       return;
+
+    case BTRACE_FORMAT_PT:
+      btrace_compute_ftrace_pt (tp, &btrace->variant.pt);
+      return;
     }
 
   internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
@@ -911,6 +1166,16 @@ btrace_stitch_bts (struct btrace_data_bts *btrace,
   return 0;
 }
 
+/* Stitch branch trace in Intel(R) Processor Trace format.  */
+
+static int
+btrace_stitch_pt (struct btrace_data_pt *btrace,
+		  const struct btrace_thread_info *btinfo)
+{
+  /* FIXME: support stitching.  */
+  return -1;
+}
+
 /* 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.
@@ -932,6 +1197,9 @@ btrace_stitch_trace (struct btrace_data *btrace,
 
     case BTRACE_FORMAT_BTS:
       return btrace_stitch_bts (&btrace->variant.bts, btinfo);
+
+    case BTRACE_FORMAT_PT:
+      return btrace_stitch_pt (&btrace->variant.pt, btinfo);
     }
 
   internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
@@ -1077,7 +1345,7 @@ check_xml_btrace_version (struct gdb_xml_parser *parser,
 {
   const char *version = xml_find_attribute (attributes, "version")->value;
 
-  if (strcmp (version, "1.0") != 0)
+  if (strcmp (version, "1.0") != 0 && strcmp (version, "2.0") != 0)
     gdb_xml_error (parser, _("Unsupported btrace version: \"%s\""), version);
 }
 
@@ -1116,12 +1384,131 @@ parse_xml_btrace_block (struct gdb_xml_parser *parser,
   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, unsigned long *psize)
+{
+  struct cleanup *cleanup;
+  gdb_byte *data, *bin;
+  unsigned long size;
+  size_t len;
+
+  len = strlen (body_text);
+  size = len / 2;
+
+  if ((size_t) size * 2 != len)
+    gdb_xml_error (parser, _("Bad raw data size."));
+
+  bin = data = 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 = xml_find_attribute (attributes, "vendor")->value;
+  family = xml_find_attribute (attributes, "family")->value;
+  model = xml_find_attribute (attributes, "model")->value;
+  stepping = xml_find_attribute (attributes, "stepping")->value;
+
+  btrace = 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 = 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 = 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 }
@@ -1130,6 +1517,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 }
 };
 
@@ -1187,9 +1576,33 @@ parse_xml_btrace_conf_bts (struct gdb_xml_parser *parser,
 
   size = xml_find_attribute (attributes, "size");
   if (size != NULL)
-    conf->bts.size = (unsigned int) * (ULONGEST *) size->value;
+    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 = 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 }
@@ -1198,6 +1611,8 @@ static const struct gdb_xml_attribute btrace_conf_bts_attributes[] = {
 static const struct gdb_xml_element btrace_conf_children[] = {
   { "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 }
 };
 
diff --git a/gdb/btrace.h b/gdb/btrace.h
index 71be12e..333a6d5 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -28,6 +28,10 @@
 
 #include "nat/x86-btrace.h"
 
+#if defined (HAVE_LIBIPT)
+#  include <intel-pt.h>
+#endif
+
 struct thread_info;
 struct btrace_function;
 
@@ -96,6 +100,21 @@ enum btrace_bts_error
   BDE_BTS_INSN_SIZE
 };
 
+/* Decode errors for the Intel Processor Trace recording format.  */
+enum btrace_pt_error
+{
+  /* The user cancelled trace processing.  */
+  BDE_PT_USER_QUIT = 1,
+
+  /* Tracing was temporarily disabled.  */
+  BDE_PT_DISABLED,
+
+  /* Trace recording overflowed.  */
+  BDE_PT_OVERFLOW
+
+  /* Negative numbers are used by the decoder library.  */
+};
+
 /* A branch trace function segment.
 
    This represents a function segment in a branch trace, i.e. a consecutive
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 0dcac9b..503acd0 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6338,12 +6338,16 @@ For architecture environments that support process record and replay,
 @kindex record full
 @kindex record btrace
 @kindex record btrace bts
+@kindex record btrace pt
 @kindex record bts
+@kindex record pt
 @kindex rec
 @kindex rec full
 @kindex rec btrace
 @kindex rec btrace bts
+@kindex rec btrace pt
 @kindex rec bts
+@kindex rec pt
 @item record @var{method}
 This command starts the process record and replay target.  The
 recording method can be specified as parameter.  Without a parameter
@@ -6371,6 +6375,20 @@ formats are available:
 Use Intel's Branch Trace Store (BTS) recording format.  In this format,
 the processor stores a from/to record for each executed branch in the
 btrace ring buffer.
+
+@item pt
+Use Intel's Processor Trace recording format.  In this format, the
+processor stores the execution trace in a compressed form that is
+afterwards decoded by @value{GDBN}.
+
+The trace can be recorded with very low overhead.  The compressed
+trace format also allows small trace buffers to already contain a big
+number of instructions compared to BTS.
+
+Decoding the recorded execution trace, on the other hand, is more
+expensive than decoding BTS trace.  This is mostly due to the
+increased number of instructions to process.  You should increase the
+buffer-size with care.
 @end table
 
 Not all recording formats may be available on all processors.
@@ -6381,9 +6399,6 @@ already running.  Therefore, you need first to start the process with
 the @kbd{run} or @kbd{start} commands, and then start the recording
 with the @kbd{record @var{method}} command.
 
-Both @code{record @var{method}} and @code{rec @var{method}} are
-aliases of @code{target record-@var{method}}.
-
 @cindex displaced stepping, and process record and replay
 Displaced stepping (@pxref{Maintenance Commands,, displaced stepping})
 will be automatically disabled when process record and replay target
@@ -6548,6 +6563,29 @@ also need longer to process the branch trace data before it can be used.
 Show the current setting of the requested ring buffer size for branch
 tracing in BTS format.
 
+@kindex set record btrace pt
+@item set record btrace pt buffer-size @var{size}
+@itemx set record btrace pt buffer-size unlimited
+Set the requested ring buffer size for branch tracing in Intel
+Processor Trace format.  Default is 16KB.
+
+If @var{size} is a positive number, then @value{GDBN} will try to
+allocate a buffer of at least @var{size} bytes for each new thread
+that uses the btrace recording method and the Intel Processor Trace
+format.  The actually obtained buffer size may differ from the
+requested @var{size}. Use the @code{info record} command to see the
+actual buffer size for each thread.
+
+If @var{limit} is @code{unlimited} or zero, @value{GDBN} will try to
+allocate a buffer of 4MB.
+
+Bigger buffers mean longer traces.  On the other hand, @value{GDBN} will
+also need longer to process the branch trace data before it can be used.
+
+@item show record btrace pt buffer-size @var{size}
+Show the current setting of the requested ring buffer size for branch
+tracing in Intel Processor Trace format.
+
 @kindex info record
 @item info record
 Show various statistics about the recording depending on the recording
@@ -6593,6 +6631,12 @@ For the @code{bts} recording format, it also shows:
 @item
 Size of the perf ring buffer.
 @end itemize
+
+For the @code{pt} recording format, it also shows:
+@itemize @bullet
+@item
+Size of the perf ring buffer.
+@end itemize
 @end table
 
 @kindex record delete
@@ -35490,11 +35534,21 @@ These are the currently defined stub features and their properties:
 @tab @samp{-}
 @tab Yes
 
+@item @samp{Qbtrace:pt}
+@tab Yes
+@tab @samp{-}
+@tab Yes
+
 @item @samp{Qbtrace-conf:bts:size}
 @tab Yes
 @tab @samp{-}
 @tab Yes
 
+@item @samp{Qbtrace-conf:pt:size}
+@tab Yes
+@tab @samp{-}
+@tab Yes
+
 @item @samp{QNonStop}
 @tab No
 @tab @samp{-}
@@ -35752,9 +35806,15 @@ The remote stub understands the @samp{Qbtrace:off} packet.
 @item Qbtrace:bts
 The remote stub understands the @samp{Qbtrace:bts} packet.
 
+@item Qbtrace:pt
+The remote stub understands the @samp{Qbtrace:pt} packet.
+
 @item Qbtrace-conf:bts:size
 The remote stub understands the @samp{Qbtrace-conf:bts:size} packet.
 
+@item Qbtrace-conf:pt:size
+The remote stub understands the @samp{Qbtrace-conf:pt:size} packet.
+
 @end table
 
 @item qSymbol::
@@ -36173,7 +36233,18 @@ A badly formed request or an error was encountered.
 @end table
 
 @item Qbtrace:bts
-Enable branch tracing for the current thread using bts tracing.
+Enable branch tracing for the current thread using Intel Branch Trace Store.
+
+Reply:
+@table @samp
+@item OK
+Branch tracing has been enabled.
+@item E.errtext
+A badly formed request or an error was encountered.
+@end table
+
+@item Qbtrace:pt
+Enable branch tracing for the current thread using Intel Processor Trace.
 
 Reply:
 @table @samp
@@ -36206,6 +36277,18 @@ The ring buffer size has been set.
 A badly formed request or an error was encountered.
 @end table
 
+@item Qbtrace-conf:pt:size=@var{value}
+Set the requested ring buffer size for new threads that use the
+btrace recording method in pt format.
+
+Reply:
+@table @samp
+@item OK
+The ring buffer size has been set.
+@item E.errtext
+A badly formed request or an error was encountered.
+@end table
+
 @end table
 
 @node Architecture-Specific Protocol Details
@@ -38717,12 +38800,24 @@ and ending at @var{end}:
 The formal DTD for the branch trace format is given below:
 
 @smallexample
-<!ELEMENT btrace  (block)* >
-<!ATTLIST btrace  version CDATA   #FIXED "1.0">
+<!ELEMENT btrace  (block* | pt) >
+<!ATTLIST btrace  version CDATA   #FIXED "2.0">
 
 <!ELEMENT block        EMPTY>
 <!ATTLIST block        begin  CDATA   #REQUIRED
                        end    CDATA   #REQUIRED>
+
+<!ELEMENT pt (pt-config?, raw?)>
+
+<!ELEMENT pt-config (cpu?)>
+
+<!ELEMENT cpu EMPTY>
+<!ATTLIST cpu vendor   CDATA #REQUIRED
+              family   CDATA #REQUIRED
+              model    CDATA #REQUIRED
+              stepping CDATA #REQUIRED>
+
+<!ELEMENT raw (#PCDATA)>
 @end smallexample
 
 @node Branch Trace Configuration Format
@@ -38743,6 +38838,12 @@ This thread uses the Branch Trace Store (BTS) format.
 @item size
 The size of the BTS ring buffer in bytes.
 @end table
+@item pt
+This thread uses the Intel Processor Trace (Intel PT) format.
+@table @code
+@item size
+The size of the Intel PT ring buffer in bytes.
+@end table
 @end table
 
 @value{GDBN} must be linked with the Expat library to support XML
@@ -38751,11 +38852,14 @@ branch trace configuration discovery.  @xref{Expat}.
 The formal DTD for the branch trace configuration format is given below:
 
 @smallexample
-<!ELEMENT btrace-conf	(bts?)>
-<!ATTLIST btrace-conf	version	CDATA	#FIXED "1.0">
+<!ELEMENT btrace-conf	(bts?, pt?)>
+<!ATTLIST btrace-conf	version	CDATA	#FIXED "2.0">
 
 <!ELEMENT bts	EMPTY>
 <!ATTLIST bts	size	CDATA	#IMPLIED>
+
+<!ELEMENT pt	EMPTY>
+<!ATTLIST pt	size	CDATA	#IMPLIED>
 @end smallexample
 
 @include agentexpr.texi
diff --git a/gdb/features/btrace-conf.dtd b/gdb/features/btrace-conf.dtd
index 907816c..c116bcb 100644
--- a/gdb/features/btrace-conf.dtd
+++ b/gdb/features/btrace-conf.dtd
@@ -4,8 +4,11 @@
      are permitted in any medium without royalty provided the copyright
      notice and this notice are preserved.  -->
 
-<!ELEMENT btrace-conf	(bts?)>
-<!ATTLIST btrace-conf	version	CDATA	#FIXED "1.0">
+<!ELEMENT btrace-conf	(bts?, pt?)>
+<!ATTLIST btrace-conf	version	CDATA	#FIXED "2.0">
 
 <!ELEMENT bts	EMPTY>
 <!ATTLIST bts	size	CDATA	#IMPLIED>
+
+<!ELEMENT pt	EMPTY>
+<!ATTLIST pt	size	CDATA	#IMPLIED>
diff --git a/gdb/features/btrace.dtd b/gdb/features/btrace.dtd
index a3193b0..945ebe7 100644
--- a/gdb/features/btrace.dtd
+++ b/gdb/features/btrace.dtd
@@ -4,9 +4,21 @@
      are permitted in any medium without royalty provided the copyright
      notice and this notice are preserved.  -->
 
-<!ELEMENT btrace  (block)* >
-<!ATTLIST btrace  version CDATA   #FIXED "1.0">
+<!ELEMENT btrace  (block* | pt)>
+<!ATTLIST btrace  version CDATA   #FIXED "2.0">
 
 <!ELEMENT block        EMPTY>
 <!ATTLIST block        begin  CDATA   #REQUIRED
                        end    CDATA   #REQUIRED>
+
+<!ELEMENT pt (pt-config?, raw?)>
+
+<!ELEMENT pt-config (cpu?)>
+
+<!ELEMENT cpu EMPTY>
+<!ATTLIST cpu vendor   CDATA #REQUIRED
+              family   CDATA #REQUIRED
+              model    CDATA #REQUIRED
+              stepping CDATA #REQUIRED>
+
+<!ELEMENT raw (#PCDATA)>
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 217b3f9..91590ad 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -20,6 +20,7 @@
 #include "linux-low.h"
 #include "nat/linux-osdata.h"
 #include "agent.h"
+#include "rsp-low.h"
 
 #include "nat/linux-nat.h"
 #include "nat/linux-waitpid.h"
@@ -5963,6 +5964,55 @@ linux_low_disable_btrace (struct btrace_target_info *tinfo)
   return (err == BTRACE_ERR_NONE ? 0 : -1);
 }
 
+/* Encode an Intel Processor Trace configuration.  */
+
+static void
+linux_low_encode_pt_config (struct buffer *buffer,
+			    const struct btrace_data_pt_config *config)
+{
+  buffer_grow_str (buffer, "<pt-config>\n");
+
+  switch (config->cpu.vendor)
+    {
+    case CV_INTEL:
+      buffer_xml_printf (buffer, "<cpu vendor=\"GenuineIntel\" family=\"%u\" "
+			 "model=\"%u\" stepping=\"%u\"/>\n",
+			 config->cpu.family, config->cpu.model,
+			 config->cpu.stepping);
+      break;
+
+    default:
+      break;
+    }
+
+  buffer_grow_str (buffer, "</pt-config>\n");
+}
+
+/* Encode a raw buffer.  */
+
+static void
+linux_low_encode_raw (struct buffer *buffer, const gdb_byte *data,
+		      unsigned int size)
+{
+  if (size == 0)
+    return;
+
+  /* We use hex encoding - see common/rsp-low.h.  */
+  buffer_grow_str (buffer, "<raw>\n");
+
+  while (size-- > 0)
+    {
+      char elem[2];
+
+      elem[0] = tohex ((*data >> 4) & 0xf);
+      elem[1] = tohex (*data++ & 0xf);
+
+      buffer_grow (buffer, elem, 2);
+    }
+
+  buffer_grow_str (buffer, "</raw>\n");
+}
+
 /* See to_read_btrace target method.  */
 
 static int
@@ -5984,15 +6034,14 @@ linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
       else
 	buffer_grow_str0 (buffer, "E.Generic Error.");
 
-      btrace_data_fini (&btrace);
-      return -1;
+      goto err;
     }
 
   switch (btrace.format)
     {
     case BTRACE_FORMAT_NONE:
       buffer_grow_str0 (buffer, "E.No Trace.");
-      break;
+      goto err;
 
     case BTRACE_FORMAT_BTS:
       buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
@@ -6007,15 +6056,31 @@ linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
       buffer_grow_str0 (buffer, "</btrace>\n");
       break;
 
-    default:
-      buffer_grow_str0 (buffer, "E.Unknown Trace Format.");
+    case BTRACE_FORMAT_PT:
+      buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
+      buffer_grow_str (buffer, "<btrace version=\"2.0\">\n");
+      buffer_grow_str (buffer, "<pt>\n");
+
+      linux_low_encode_pt_config (buffer, &btrace.variant.pt.config);
 
-      btrace_data_fini (&btrace);
-      return -1;
+      linux_low_encode_raw (buffer, btrace.variant.pt.data,
+			    btrace.variant.pt.size);
+
+      buffer_grow_str (buffer, "</pt>\n");
+      buffer_grow_str0 (buffer, "</btrace>\n");
+      break;
+
+    default:
+      buffer_grow_str0 (buffer, "E.Unsupported Trace Format.");
+      goto err;
     }
 
   btrace_data_fini (&btrace);
   return 0;
+
+err:
+  btrace_data_fini (&btrace);
+  return -1;
 }
 
 /* See to_btrace_conf target method.  */
@@ -6042,6 +6107,12 @@ linux_low_btrace_conf (const struct btrace_target_info *tinfo,
 	  buffer_xml_printf (buffer, " size=\"0x%x\"", conf->bts.size);
 	  buffer_xml_printf (buffer, " />\n");
 	  break;
+
+	case BTRACE_FORMAT_PT:
+	  buffer_xml_printf (buffer, "<pt");
+	  buffer_xml_printf (buffer, " size=\"0x%x\"", conf->pt.size);
+	  buffer_xml_printf (buffer, "/>\n");
+	  break;
 	}
     }
 
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 4ed9837..c290978 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -402,6 +402,23 @@ handle_btrace_enable_bts (struct thread_info *thread)
   return NULL;
 }
 
+/* Handle btrace enabling in Intel Processor Trace format.  */
+
+static const char *
+handle_btrace_enable_pt (struct thread_info *thread)
+{
+  if (thread->btrace != NULL)
+    return "E.Btrace already enabled.";
+
+  current_btrace_conf.format = BTRACE_FORMAT_PT;
+  thread->btrace = target_enable_btrace (thread->entry.id,
+					 &current_btrace_conf);
+  if (thread->btrace == NULL)
+    return "E.Could not enable btrace.";
+
+  return NULL;
+}
+
 /* Handle btrace disabling.  */
 
 static const char *
@@ -450,10 +467,12 @@ handle_btrace_general_set (char *own_buf)
 
   if (strcmp (op, "bts") == 0)
     err = handle_btrace_enable_bts (thread);
+  else if (strcmp (op, "pt") == 0)
+    err = handle_btrace_enable_pt (thread);
   else if (strcmp (op, "off") == 0)
     err = handle_btrace_disable (thread);
   else
-    err = "E.Bad Qbtrace operation. Use bts or off.";
+    err = "E.Bad Qbtrace operation. Use bts, pt, or off.";
 
   if (err != 0)
     strcpy (own_buf, err);
@@ -505,6 +524,21 @@ handle_btrace_conf_general_set (char *own_buf)
 
       current_btrace_conf.bts.size = (unsigned int) size;
     }
+  else if (strncmp (op, "pt:size=", strlen ("pt:size=")) == 0)
+    {
+      unsigned long size;
+      char *endp = NULL;
+
+      errno = 0;
+      size = strtoul (op + strlen ("pt:size="), &endp, 16);
+      if (endp == NULL || *endp != 0 || errno != 0 || size > UINT_MAX)
+	{
+	  strcpy (own_buf, "E.Bad size value.");
+	  return -1;
+	}
+
+      current_btrace_conf.pt.size = (unsigned int) size;
+    }
   else
     {
       strcpy (own_buf, "E.Bad Qbtrace configuration option.");
@@ -1815,12 +1849,25 @@ crc32 (CORE_ADDR base, int len, unsigned int crc)
 static void
 supported_btrace_packets (char *buf)
 {
+  int btrace_supported = 0;
+
   if (target_supports_btrace (BTRACE_FORMAT_BTS))
     {
       strcat (buf, ";Qbtrace:bts+");
       strcat (buf, ";Qbtrace-conf:bts:size+");
+
+      btrace_supported = 1;
     }
-  else
+
+  if (target_supports_btrace (BTRACE_FORMAT_PT))
+    {
+      strcat (buf, ";Qbtrace:pt+");
+      strcat (buf, ";Qbtrace-conf:pt:size+");
+
+      btrace_supported = 1;
+    }
+
+  if (!btrace_supported)
     return;
 
   strcat (buf, ";Qbtrace:off+");
diff --git a/gdb/nat/linux-btrace.c b/gdb/nat/linux-btrace.c
index 6948522..bbda3ec 100644
--- a/gdb/nat/linux-btrace.c
+++ b/gdb/nat/linux-btrace.c
@@ -66,6 +66,119 @@ perf_event_new_data (const struct perf_event_buffer *pev)
   return *pev->data_head != pev->last_head;
 }
 
+/* Copy the last SIZE bytes from PEV ending at DATA_HEAD and return a pointer
+   to the memory holding the copy.
+   The caller is responsible for freeing the memory.  */
+
+static gdb_byte *
+perf_event_read (const struct perf_event_buffer *pev, unsigned long data_head,
+		 unsigned long size)
+{
+  const gdb_byte *begin, *end, *start, *stop;
+  gdb_byte *buffer;
+  unsigned long data_tail, buffer_size;
+
+  if (size == 0)
+    return NULL;
+
+  gdb_assert (size <= data_head);
+  data_tail = data_head - size;
+
+  buffer_size = pev->size;
+  begin = pev->mem;
+  start = begin + data_tail % buffer_size;
+  stop = begin + data_head % buffer_size;
+
+  buffer = xmalloc (size);
+
+  if (start < stop)
+    memcpy (buffer, start, stop - start);
+  else
+    {
+      end = begin + buffer_size;
+
+      memcpy (buffer, start, end - start);
+      memcpy (buffer + (end - start), begin, stop - begin);
+    }
+
+  return buffer;
+}
+
+/* Copy the perf event buffer data from PEV.
+   Store a pointer to the copy into DATA and its size in SIZE.  */
+
+static void
+perf_event_read_all (struct perf_event_buffer *pev, gdb_byte **data,
+		     unsigned long *psize)
+{
+  unsigned long data_head, size;
+
+  data_head = *pev->data_head;
+
+  size = pev->size;
+  if (data_head < size)
+    size = data_head;
+
+  *data = perf_event_read (pev, data_head, size);
+  *psize = size;
+
+  pev->last_head = data_head;
+}
+
+/* Copy the new perf event data from PEV.
+   Store a pointer to the copy into DATA and its size in SIZE.
+   Returns BTRACE_ERR_NONE on success.
+   Returns BTRACE_ERR_OVERFLOW if the perf event buffer overflowed.  */
+
+static enum btrace_error
+perf_event_read_new (struct perf_event_buffer *pev, gdb_byte **data,
+		     unsigned long *psize)
+{
+  unsigned long data_head, data_tail, size, buffer_size;
+
+  data_head = *pev->data_head;
+  data_tail = pev->last_head;
+
+  /* Check for data head overflows.  We might be able to recover from those
+     but they are very unlikely and it's not worth the effort, I think.  */
+  if (data_head < data_tail)
+    return BTRACE_ERR_OVERFLOW;
+
+  /* Check for buffer overflows.  */
+  buffer_size = pev->size;
+  size = data_head - data_tail;
+  if (buffer_size < size)
+    return BTRACE_ERR_OVERFLOW;
+
+  *data = perf_event_read (pev, data_head, size);
+  *psize = size;
+
+  pev->last_head = data_head;
+  return BTRACE_ERR_NONE;
+}
+
+/* Determine the event type.
+   Returns zero on success and fills in TYPE; returns -1 otherwise.  */
+
+static int
+perf_event_pt_event_type (int *type)
+{
+  FILE *file;
+  int found;
+
+  file = fopen ("/sys/bus/event_source/devices/intel_pt/type", "r");
+  if (file == NULL)
+    return -1;
+
+  found = fscanf (file, "%d", type);
+
+  fclose (file);
+
+  if (found == 1)
+    return 0;
+  return -1;
+}
+
 /* Check whether an address is in the kernel.  */
 
 static inline int
@@ -298,6 +411,93 @@ kernel_supports_bts (void)
     }
 }
 
+/* Check whether the kernel supports Intel Processor Trace.  */
+
+static int
+kernel_supports_pt (void)
+{
+  struct perf_event_attr attr;
+  pid_t child, pid;
+  int status, file, type;
+
+  errno = 0;
+  child = fork ();
+  switch (child)
+    {
+    case -1:
+      warning (_("test pt: cannot fork: %s."), strerror (errno));
+      return 0;
+
+    case 0:
+      status = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
+      if (status != 0)
+	{
+	  warning (_("test pt: cannot PTRACE_TRACEME: %s."),
+		   strerror (errno));
+	  _exit (1);
+	}
+
+      status = raise (SIGTRAP);
+      if (status != 0)
+	{
+	  warning (_("test pt: cannot raise SIGTRAP: %s."),
+		   strerror (errno));
+	  _exit (1);
+	}
+
+      _exit (1);
+
+    default:
+      pid = waitpid (child, &status, 0);
+      if (pid != child)
+	{
+	  warning (_("test pt: bad pid %ld, error: %s."),
+		   (long) pid, strerror (errno));
+	  return 0;
+	}
+
+      if (!WIFSTOPPED (status))
+	{
+	  warning (_("test pt: expected stop. status: %d."),
+		   status);
+	  return 0;
+	}
+
+      status = perf_event_pt_event_type (&type);
+      if (status != 0)
+	file = -1;
+      else
+	{
+	  memset (&attr, 0, sizeof (attr));
+
+	  attr.size = sizeof (attr);
+	  attr.type = type;
+	  attr.exclude_kernel = 1;
+	  attr.exclude_hv = 1;
+	  attr.exclude_idle = 1;
+
+	  file = syscall (SYS_perf_event_open, &attr, child, -1, -1, 0);
+	  if (file >= 0)
+	    close (file);
+	}
+
+      kill (child, SIGKILL);
+      ptrace (PTRACE_KILL, child, NULL, NULL);
+
+      pid = waitpid (child, &status, 0);
+      if (pid != child)
+	{
+	  warning (_("test pt: bad pid %ld, error: %s."),
+		   (long) pid, strerror (errno));
+	  if (!WIFSIGNALED (status))
+	    warning (_("test pt: expected killed. status: %d."),
+		     status);
+	}
+
+      return (file >= 0);
+    }
+}
+
 /* Check whether an Intel cpu supports BTS.  */
 
 static int
@@ -368,6 +568,24 @@ linux_supports_bts (void)
   return cached > 0;
 }
 
+/* Check whether the linux target supports Intel Processor Trace.  */
+
+static int
+linux_supports_pt (void)
+{
+  static int cached;
+
+  if (cached == 0)
+    {
+      if (!kernel_supports_pt ())
+	cached = -1;
+      else
+	cached = 1;
+    }
+
+  return cached > 0;
+}
+
 /* See linux-btrace.h.  */
 
 int
@@ -380,6 +598,9 @@ linux_supports_btrace (struct target_ops *ops, enum btrace_format format)
 
     case BTRACE_FORMAT_BTS:
       return linux_supports_bts ();
+
+    case BTRACE_FORMAT_PT:
+      return linux_supports_pt ();
     }
 
   internal_error (__FILE__, __LINE__, _("Unknown branch trace format"));
@@ -424,13 +645,13 @@ linux_enable_bts (ptid_t ptid, const struct btrace_config_bts *conf)
   if (size == 0)
     {
       errno = EINVAL;
-      goto err;
-    }
+      goto err_out;
+  }
 
   errno = 0;
   bts->file = syscall (SYS_perf_event_open, &bts->attr, pid, -1, -1, 0);
   if (bts->file < 0)
-    goto err;
+    goto err_out;
 
   /* The buffer size can be requested in powers of two pages.
      We track the number of pages we request in PAGES and its base-2 log
@@ -468,11 +689,116 @@ linux_enable_bts (ptid_t ptid, const struct btrace_config_bts *conf)
   /* We were not able to allocate any buffer.  */
   close (bts->file);
 
+ err_out:
+  xfree (tinfo);
+  return NULL;
+}
+
+#if defined(PERF_ATTR_SIZE_VER4)
+
+/* Enable branch tracing in Intel Processor Trace format.  */
+
+static struct btrace_target_info *
+linux_enable_pt (ptid_t ptid, const struct btrace_config_pt *conf)
+{
+  struct perf_event_mmap_page *header;
+  struct btrace_target_info *tinfo;
+  struct btrace_tinfo_pt *pt;
+  unsigned int pages, size;
+  int pid, pg, errcode, type;
+
+  if (conf->size == 0)
+    return NULL;
+
+  errcode = perf_event_pt_event_type (&type);
+  if (errcode != 0)
+    return NULL;
+
+  pid = ptid_get_lwp (ptid);
+  if (pid == 0)
+    pid = ptid_get_pid (ptid);
+
+  tinfo = xzalloc (sizeof (*tinfo));
+  tinfo->ptid = ptid;
+  tinfo->ptr_bits = 0;
+
+  tinfo->conf.format = BTRACE_FORMAT_PT;
+  pt = &tinfo->variant.pt;
+
+  pt->attr.size = sizeof (pt->attr);
+  pt->attr.type = type;
+
+  pt->attr.exclude_kernel = 1;
+  pt->attr.exclude_hv = 1;
+  pt->attr.exclude_idle = 1;
+
+  errno = 0;
+  pt->file = syscall (SYS_perf_event_open, &pt->attr, pid, -1, -1, 0);
+  if (pt->file < 0)
+    goto err;
+
+  /* Allocate the configuration page. */
+  header = mmap (NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
+		 pt->file, 0);
+  if (header == MAP_FAILED)
+    goto err_file;
+
+  header->aux_offset = header->data_offset + header->data_size;
+  size = (conf->size + PAGE_SIZE - 1) / PAGE_SIZE;
+
+  /* The buffer size can be requested in powers of two pages.
+     We track the number of pages we request in PAGES and its base-2 log
+     in PG.  */
+
+  /* Adjust size to the next power of two.  */
+  for (pg = 0, pages = 1; pages != size; ++pg, pages = (1u << pg))
+    if ((pages & size) != 0)
+      size += pages;
+
+  /* We try to allocate the requested size.
+     If that fails, try to get as much as we can.  */
+  for (; size > 0; size >>= 1)
+    {
+      header->aux_size = size * PAGE_SIZE;
+
+      pt->pt.mem = mmap (NULL, header->aux_size, PROT_READ, MAP_SHARED,
+			 pt->file, header->aux_offset);
+      if (pt->pt.mem != MAP_FAILED)
+	break;
+    }
+
+  if (size == 0)
+    goto err_conf;
+
+  pt->header = header;
+  pt->pt.size = header->aux_size;
+  pt->pt.data_head = &header->aux_head;
+
+  tinfo->conf.pt.size = pt->pt.size;
+  return tinfo;
+
+ err_conf:
+  munmap((void *) header, PAGE_SIZE);
+
+ err_file:
+  close (pt->file);
+
  err:
   xfree (tinfo);
   return NULL;
 }
 
+#else /* !defined(PERF_ATTR_SIZE_VER4) */
+
+static struct btrace_target_info *
+linux_enable_pt (ptid_t ptid, const struct btrace_config_pt *conf)
+{
+  errno = EOPNOTSUPP;
+  return NULL;
+}
+
+#endif /* !defined(PERF_ATTR_SIZE_VER4) */
+
 /* See linux-btrace.h.  */
 
 struct btrace_target_info *
@@ -489,6 +815,10 @@ linux_enable_btrace (ptid_t ptid, const struct btrace_config *conf)
     case BTRACE_FORMAT_BTS:
       tinfo = linux_enable_bts (ptid, &conf->bts);
       break;
+
+    case BTRACE_FORMAT_PT:
+      tinfo = linux_enable_pt (ptid, &conf->pt);
+      break;
     }
 
   return tinfo;
@@ -505,6 +835,18 @@ linux_disable_bts (struct btrace_tinfo_bts *tinfo)
   return BTRACE_ERR_NONE;
 }
 
+/* Disable Intel Processor Trace tracing.  */
+
+static enum btrace_error
+linux_disable_pt (struct btrace_tinfo_pt *tinfo)
+{
+  munmap((void *) tinfo->pt.mem, tinfo->pt.size);
+  munmap((void *) tinfo->header, PAGE_SIZE);
+  close (tinfo->file);
+
+  return BTRACE_ERR_NONE;
+}
+
 /* See linux-btrace.h.  */
 
 enum btrace_error
@@ -521,6 +863,10 @@ linux_disable_btrace (struct btrace_target_info *tinfo)
     case BTRACE_FORMAT_BTS:
       errcode = linux_disable_bts (&tinfo->variant.bts);
       break;
+
+    case BTRACE_FORMAT_PT:
+      errcode = linux_disable_pt (&tinfo->variant.pt);
+      break;
     }
 
   if (errcode == BTRACE_ERR_NONE)
@@ -618,6 +964,51 @@ linux_read_bts (struct btrace_data_bts *btrace,
   return BTRACE_ERR_NONE;
 }
 
+/* Fill in the Intel Processor Trace configuration information.  */
+
+static void
+linux_fill_btrace_pt_config (struct btrace_data_pt_config *conf)
+{
+  conf->cpu = btrace_this_cpu ();
+}
+
+/* Read branch trace data in Intel Processor Trace format for the thread given
+   by TINFO into BTRACE using the TYPE reading method.  */
+
+static enum btrace_error
+linux_read_pt (struct btrace_data_pt *btrace,
+	       struct btrace_target_info *tinfo,
+	       enum btrace_read_type type)
+{
+  struct perf_event_buffer *pt;
+  enum btrace_error errcode;
+
+  pt = &tinfo->variant.pt.pt;
+
+  linux_fill_btrace_pt_config (&btrace->config);
+
+  switch (type)
+    {
+    case BTRACE_READ_DELTA:
+      errcode = perf_event_read_new (pt, &btrace->data, &btrace->size);
+      if (errcode != BTRACE_ERR_NONE)
+	return errcode;
+
+      return BTRACE_ERR_NONE;
+
+    case BTRACE_READ_NEW:
+      if (!perf_event_new_data (pt))
+	return BTRACE_ERR_NONE;
+
+      /* Fall through.  */
+    case BTRACE_READ_ALL:
+      perf_event_read_all (pt, &btrace->data, &btrace->size);
+      return BTRACE_ERR_NONE;
+    }
+
+  internal_error (__FILE__, __LINE__, _("Unkown btrace read type."));
+}
+
 /* See linux-btrace.h.  */
 
 enum btrace_error
@@ -636,6 +1027,13 @@ linux_read_btrace (struct btrace_data *btrace,
       btrace->variant.bts.blocks = NULL;
 
       return linux_read_bts (&btrace->variant.bts, tinfo, type);
+
+    case BTRACE_FORMAT_PT:
+      /* We read btrace in Intel Processor Trace format.  */
+      btrace->format = BTRACE_FORMAT_PT;
+      btrace->variant.pt.data = NULL;
+
+      return linux_read_pt (&btrace->variant.pt, tinfo, type);
     }
 
   internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
diff --git a/gdb/nat/linux-btrace.h b/gdb/nat/linux-btrace.h
index 1ad83f0..6ad1cdc 100644
--- a/gdb/nat/linux-btrace.h
+++ b/gdb/nat/linux-btrace.h
@@ -64,6 +64,22 @@ struct btrace_tinfo_bts
   /* The BTS perf event buffer.  */
   struct perf_event_buffer bts;
 };
+
+/* Branch trace target information for Intel Processor Trace.  */
+struct btrace_tinfo_pt
+{
+  /* The Linux perf_event configuration for collecting the branch trace.  */
+  struct perf_event_attr attr;
+
+  /* The perf event file.  */
+  int file;
+
+  /* The perf event configuration page. */
+  volatile struct perf_event_mmap_page *header;
+
+  /* The trace perf event buffer.  */
+  struct perf_event_buffer pt;
+};
 #endif /* HAVE_LINUX_PERF_EVENT_H */
 
 /* Branch trace target information per thread.  */
@@ -81,6 +97,9 @@ struct btrace_target_info
   {
     /* CONF.FORMAT == BTRACE_FORMAT_BTS.  */
     struct btrace_tinfo_bts bts;
+
+    /* CONF.FORMAT == BTRACE_FORMAT_PT.  */
+    struct btrace_tinfo_pt pt;
   } variant;
 #endif /* HAVE_LINUX_PERF_EVENT_H */
 
diff --git a/gdb/nat/x86-btrace.c b/gdb/nat/x86-btrace.c
index 845e895..0488a00 100644
--- a/gdb/nat/x86-btrace.c
+++ b/gdb/nat/x86-btrace.c
@@ -69,6 +69,9 @@ btrace_format_string (enum btrace_format format)
 
     case BTRACE_FORMAT_BTS:
       return _("Intel(R) Branch Trace Store");
+
+    case BTRACE_FORMAT_PT:
+      return _("Intel(R) Processor Trace");
     }
 
   internal_error (__FILE__, __LINE__, _("Unknown branch trace format"));
@@ -96,6 +99,10 @@ btrace_data_fini (struct btrace_data *data)
     case BTRACE_FORMAT_BTS:
       VEC_free (btrace_block_s, data->variant.bts.blocks);
       return;
+
+    case BTRACE_FORMAT_PT:
+      xfree (data->variant.pt.data);
+      return;
     }
 
   internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
@@ -113,6 +120,9 @@ btrace_data_empty (struct btrace_data *data)
 
     case BTRACE_FORMAT_BTS:
       return VEC_empty (btrace_block_s, data->variant.bts.blocks);
+
+    case BTRACE_FORMAT_PT:
+      return (data->variant.pt.size == 0);
     }
 
   internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
diff --git a/gdb/nat/x86-btrace.h b/gdb/nat/x86-btrace.h
index 05d41fd..c001068b 100644
--- a/gdb/nat/x86-btrace.h
+++ b/gdb/nat/x86-btrace.h
@@ -59,7 +59,10 @@ enum btrace_format
 
   /* Branch trace is in Branch Trace Store format.
      Actually, the format is a sequence of blocks derived from BTS.  */
-  BTRACE_FORMAT_BTS
+  BTRACE_FORMAT_BTS,
+
+  /* Branch trace is in Intel(R) Processor Trace format.  */
+  BTRACE_FORMAT_PT
 };
 
 /* An enumeration of cpu vendors.  */
@@ -98,6 +101,14 @@ struct btrace_config_bts
   unsigned int size;
 };
 
+/* An Intel Processor Trace configuration.  */
+
+struct btrace_config_pt
+{
+  /* The size of the branch trace buffer in bytes.  */
+  unsigned int size;
+};
+
 /* A branch tracing configuration.
 
    This describes the requested configuration as well as the actually
@@ -112,6 +123,9 @@ struct btrace_config
 
   /* The BTS format configuration.  */
   struct btrace_config_bts bts;
+
+  /* The Intel Processor Trace format configuration.  */
+  struct btrace_config_pt pt;
 };
 
 /* Branch trace in BTS format.  */
@@ -122,6 +136,26 @@ struct btrace_data_bts
   VEC (btrace_block_s) *blocks;
 };
 
+/* Configuration information to go with the trace data.  */
+struct btrace_data_pt_config
+{
+  /* The processor on which the trace has been collected.  */
+  struct btrace_cpu cpu;
+};
+
+/* Branch trace in Intel Processor Trace format.  */
+struct btrace_data_pt
+{
+  /* Some configuration information to go with the data.  */
+  struct btrace_data_pt_config config;
+
+  /* The trace data.  */
+  gdb_byte *data;
+
+  /* The size of DATA in bytes.  */
+  unsigned long size;
+};
+
 /* The branch trace data.  */
 struct btrace_data
 {
@@ -131,6 +165,9 @@ struct btrace_data
   {
     /* Format == BTRACE_FORMAT_BTS.  */
     struct btrace_data_bts bts;
+
+    /* Format == BTRACE_FORMAT_PT.  */
+    struct btrace_data_pt pt;
   } variant;
 };
 
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 3075b8e..38c47dd 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -84,6 +84,10 @@ static struct cmd_list_element *show_record_btrace_cmdlist;
 static struct cmd_list_element *set_record_btrace_bts_cmdlist;
 static struct cmd_list_element *show_record_btrace_bts_cmdlist;
 
+/* Command lists for "set/show record btrace pt".  */
+static struct cmd_list_element *set_record_btrace_pt_cmdlist;
+static struct cmd_list_element *show_record_btrace_pt_cmdlist;
+
 /* Print a record-btrace debug message.  Use do ... while (0) to avoid
    ambiguities when used in if statements.  */
 
@@ -321,6 +325,22 @@ record_btrace_print_bts_conf (const struct btrace_config_bts *conf)
     }
 }
 
+/* Print an Intel Processor Trace configuration.  */
+
+static void
+record_btrace_print_pt_conf (const struct btrace_config_pt *conf)
+{
+  const char *suffix;
+  unsigned int size;
+
+  size = conf->size;
+  if (size > 0)
+    {
+      suffix = record_btrace_adjust_size (&size);
+      printf_unfiltered (_("Buffer size: %u%s.\n"), size, suffix);
+    }
+}
+
 /* Print a branch tracing configuration.  */
 
 static void
@@ -337,6 +357,10 @@ record_btrace_print_conf (const struct btrace_config *conf)
     case BTRACE_FORMAT_BTS:
       record_btrace_print_bts_conf (&conf->bts);
       return;
+
+    case BTRACE_FORMAT_PT:
+      record_btrace_print_pt_conf (&conf->pt);
+      return;
     }
 
   internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
@@ -428,6 +452,33 @@ btrace_ui_out_decode_error (struct ui_out *uiout, int errcode,
 	  break;
 	}
       break;
+
+#if defined (HAVE_LIBIPT)
+    case BTRACE_FORMAT_PT:
+      switch (errcode)
+	{
+	case BDE_PT_USER_QUIT:
+	  is_error = 0;
+	  errstr = _("trace decode cancelled");
+	  break;
+
+	case BDE_PT_DISABLED:
+	  is_error = 0;
+	  errstr = _("disabled");
+	  break;
+
+	case BDE_PT_OVERFLOW:
+	  is_error = 0;
+	  errstr = _("overflow");
+	  break;
+
+	default:
+	  if (errcode < 0)
+	    errstr = pt_errstr (pt_errcode (errcode));
+	  break;
+	}
+      break;
+#endif /* defined (HAVE_LIBIPT)  */
     }
 
   ui_out_text (uiout, _("["));
@@ -2176,6 +2227,28 @@ cmd_record_btrace_bts_start (char *args, int from_tty)
     }
 }
 
+/* Start recording Intel Processor Trace.  */
+
+static void
+cmd_record_btrace_pt_start (char *args, int from_tty)
+{
+  volatile struct gdb_exception exception;
+
+  if (args != NULL && *args != 0)
+    error (_("Invalid argument."));
+
+  record_btrace_conf.format = BTRACE_FORMAT_PT;
+
+  TRY_CATCH (exception, RETURN_MASK_ALL)
+    execute_command ("target record-btrace", from_tty);
+
+  if (exception.error != 0)
+    {
+      record_btrace_conf.format = BTRACE_FORMAT_NONE;
+      throw_exception (exception);
+    }
+}
+
 /* Alias for "target record".  */
 
 static void
@@ -2186,6 +2259,14 @@ cmd_record_btrace_start (char *args, int from_tty)
   if (args != NULL && *args != 0)
     error (_("Invalid argument."));
 
+  record_btrace_conf.format = BTRACE_FORMAT_PT;
+
+  TRY_CATCH (exception, RETURN_MASK_ALL)
+    execute_command ("target record-btrace", from_tty);
+
+  if (exception.error == 0)
+    return;
+
   record_btrace_conf.format = BTRACE_FORMAT_BTS;
 
   TRY_CATCH (exception, RETURN_MASK_ALL)
@@ -2243,6 +2324,25 @@ cmd_show_record_btrace_bts (char *args, int from_tty)
   cmd_show_list (show_record_btrace_bts_cmdlist, from_tty, "");
 }
 
+/* The "set record btrace pt" command.  */
+
+static void
+cmd_set_record_btrace_pt (char *args, int from_tty)
+{
+  printf_unfiltered (_("\"set record btrace pt\" must be followed "
+		       "by an apporpriate subcommand.\n"));
+  help_list (set_record_btrace_pt_cmdlist, "set record btrace pt ",
+	     all_commands, gdb_stdout);
+}
+
+/* The "show record btrace pt" command.  */
+
+static void
+cmd_show_record_btrace_pt (char *args, int from_tty)
+{
+  cmd_show_list (show_record_btrace_pt_cmdlist, from_tty, "");
+}
+
 void _initialize_record_btrace (void);
 
 /* Initialize btrace commands.  */
@@ -2263,6 +2363,13 @@ This format may not be available on all processors."),
 	   &record_btrace_cmdlist);
   add_alias_cmd ("bts", "btrace bts", class_obscure, 1, &record_cmdlist);
 
+  add_cmd ("pt", class_obscure, cmd_record_btrace_pt_start,
+	   _("\
+Start branch trace recording in Intel(R) Processor Trace format.\n\n\
+This format may not be available on all processors."),
+	   &record_btrace_cmdlist);
+  add_alias_cmd ("pt", "btrace pt", class_obscure, 1, &record_cmdlist);
+
   add_prefix_cmd ("btrace", class_support, cmd_set_record_btrace,
 		  _("Set record options"), &set_record_btrace_cmdlist,
 		  "set record btrace ", 0, &set_record_cmdlist);
@@ -2309,6 +2416,27 @@ The trace buffer size may not be changed while recording."), NULL, NULL,
 			    &set_record_btrace_bts_cmdlist,
 			    &show_record_btrace_bts_cmdlist);
 
+  add_prefix_cmd ("pt", class_support, cmd_set_record_btrace_pt,
+		  _("Set record btrace pt options"),
+		  &set_record_btrace_pt_cmdlist,
+		  "set record btrace pt ", 0, &set_record_btrace_cmdlist);
+
+  add_prefix_cmd ("pt", class_support, cmd_show_record_btrace_pt,
+		  _("Show record btrace pt options"),
+		  &show_record_btrace_pt_cmdlist,
+		  "show record btrace pt ", 0, &show_record_btrace_cmdlist);
+
+  add_setshow_uinteger_cmd ("buffer-size", no_class,
+			    &record_btrace_conf.pt.size,
+			    _("Set the record/replay pt buffer size."),
+			    _("Show the record/replay pt buffer size."), _("\
+Bigger buffers allow longer recording but also take more time to process \
+the recorded execution.\n\
+The actual buffer size may differ from the requested size.  Use \"info record\" \
+to see the actual buffer size."), NULL, NULL,
+			    &set_record_btrace_pt_cmdlist,
+			    &show_record_btrace_pt_cmdlist);
+
   init_record_btrace_ops ();
   add_target (&record_btrace_ops);
 
@@ -2316,4 +2444,5 @@ The trace buffer size may not be changed while recording."), NULL, NULL,
 			       xcalloc, xfree);
 
   record_btrace_conf.bts.size = 64 * 1024;
+  record_btrace_conf.pt.size = 16 * 1024;
 }
diff --git a/gdb/remote.c b/gdb/remote.c
index e97a9f5..3b560df 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1310,6 +1310,7 @@ enum {
   PACKET_QTBuffer_size,
   PACKET_Qbtrace_off,
   PACKET_Qbtrace_bts,
+  PACKET_Qbtrace_pt,
   PACKET_qXfer_btrace,
 
   /* Support for the QNonStop packet.  */
@@ -1338,6 +1339,9 @@ enum {
   /* Support for the Qbtrace-conf:bts:size packet.  */
   PACKET_Qbtrace_conf_bts_size,
 
+  /* Support for the Qbtrace-conf:pt:size packet.  */
+  PACKET_Qbtrace_conf_pt_size,
+
   PACKET_MAX
 };
 
@@ -4020,12 +4024,15 @@ static const struct protocol_feature remote_protocol_features[] = {
   { "tracenz", PACKET_DISABLE, remote_supported_packet, PACKET_tracenz_feature },
   { "Qbtrace:off", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_off },
   { "Qbtrace:bts", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_bts },
+  { "Qbtrace:pt", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_pt },
   { "qXfer:btrace:read", PACKET_DISABLE, remote_supported_packet,
     PACKET_qXfer_btrace },
   { "qXfer:btrace-conf:read", PACKET_DISABLE, remote_supported_packet,
     PACKET_qXfer_btrace_conf },
   { "Qbtrace-conf:bts:size", PACKET_DISABLE, remote_supported_packet,
-    PACKET_Qbtrace_conf_bts_size }
+    PACKET_Qbtrace_conf_bts_size },
+  { "Qbtrace-conf:pt:size", PACKET_DISABLE, remote_supported_packet,
+    PACKET_Qbtrace_conf_pt_size }
 };
 
 static char *remote_support_xml;
@@ -11373,6 +11380,15 @@ remote_supports_btrace (struct target_ops *self, enum btrace_format format)
 
       case BTRACE_FORMAT_BTS:
 	return (packet_support (PACKET_Qbtrace_bts) == PACKET_ENABLE);
+
+      case BTRACE_FORMAT_PT:
+	/* The trace is decoded on the host.  Even if our target supports it,
+	   we still need to have libipt to decode the trace.  */
+#if defined (HAVE_LIBIPT)
+	return (packet_support (PACKET_Qbtrace_pt) == PACKET_ENABLE);
+#else /* !defined (HAVE_LIBIPT)  */
+	return 0;
+#endif /* !defined (HAVE_LIBIPT)  */
     }
 
   internal_error (__FILE__, __LINE__, _("Unknown branch trace format"));
@@ -11412,6 +11428,28 @@ btrace_sync_conf (const struct btrace_config *conf)
 
       rs->btrace_config.bts.size = conf->bts.size;
     }
+
+  packet = &remote_protocol_packets[PACKET_Qbtrace_conf_pt_size];
+  if (packet_config_support (packet) == PACKET_ENABLE
+      && conf->pt.size != rs->btrace_config.pt.size)
+    {
+      pos = buf;
+      pos += xsnprintf (pos, endbuf - pos, "%s=0x%x", packet->name,
+                        conf->pt.size);
+
+      putpkt (buf);
+      getpkt (&buf, &rs->buf_size, 0);
+
+      if (packet_ok (buf, packet) == PACKET_ERROR)
+	{
+	  if (buf[0] == 'E' && buf[1] == '.')
+	    error (_("Failed to configure the trace buffer size: %s"), buf + 2);
+	  else
+	    error (_("Failed to configure the trace buffer size."));
+	}
+
+      rs->btrace_config.pt.size = conf->pt.size;
+    }
 }
 
 /* Read the current thread's btrace configuration from the target and
@@ -11423,7 +11461,7 @@ btrace_read_config (struct btrace_config *conf)
   char *xml;
 
   xml = target_read_stralloc (&current_target,
-                              TARGET_OBJECT_BTRACE_CONF, "");
+			      TARGET_OBJECT_BTRACE_CONF, "");
   if (xml != NULL)
     {
       struct cleanup *cleanup;
@@ -11441,13 +11479,24 @@ remote_enable_btrace (struct target_ops *self, ptid_t ptid,
 		      const struct btrace_config *conf)
 {
   struct btrace_target_info *tinfo = NULL;
-  struct packet_config *packet = &remote_protocol_packets[PACKET_Qbtrace_bts];
+  struct packet_config *packet = NULL;
   struct remote_state *rs = get_remote_state ();
   char *buf = rs->buf;
   char *endbuf = rs->buf + get_remote_packet_size ();
   volatile struct gdb_exception err;
 
-  if (packet_config_support (packet) != PACKET_ENABLE)
+  switch (conf->format)
+    {
+      case BTRACE_FORMAT_BTS:
+	packet = &remote_protocol_packets[PACKET_Qbtrace_bts];
+	break;
+
+      case BTRACE_FORMAT_PT:
+	packet = &remote_protocol_packets[PACKET_Qbtrace_pt];
+	break;
+    }
+
+  if (packet == NULL || packet_config_support (packet) != PACKET_ENABLE)
     error (_("Target does not support branch tracing."));
 
   btrace_sync_conf (conf);
@@ -11564,7 +11613,7 @@ remote_read_btrace (struct target_ops *self,
     }
 
   xml = target_read_stralloc (&current_target,
-                              TARGET_OBJECT_BTRACE, annex);
+			      TARGET_OBJECT_BTRACE, annex);
   if (xml == NULL)
     return BTRACE_ERR_UNKNOWN;
 
@@ -12304,7 +12353,10 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
        "Qbtrace:off", "disable-btrace", 0);
 
   add_packet_config_cmd (&remote_protocol_packets[PACKET_Qbtrace_bts],
-       "Qbtrace:bts", "enable-btrace", 0);
+       "Qbtrace:bts", "enable-btrace-bts", 0);
+
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_Qbtrace_pt],
+       "Qbtrace:pt", "enable-btrace-pt", 0);
 
   add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_btrace],
        "qXfer:btrace", "read-btrace", 0);
@@ -12315,6 +12367,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (&remote_protocol_packets[PACKET_Qbtrace_conf_bts_size],
        "Qbtrace-conf:bts:size", "btrace-conf-bts-size", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_Qbtrace_conf_pt_size],
+       "Qbtrace-conf:pt:size", "btrace-conf-pt-size", 0);
+
   /* Assert that we've registered commands for all packet configs.  */
   {
     int i;
-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH v2 04/13] record btrace: add configuration struct
  2014-11-20 10:47 [PATCH v2 00/13] record btrace: prepare for a new trace format Markus Metzger
                   ` (2 preceding siblings ...)
  2014-11-20 10:47 ` [PATCH v2 07/13] btrace: extend struct btrace_insn Markus Metzger
@ 2014-11-20 10:47 ` Markus Metzger
  2014-11-20 10:47 ` [PATCH v2 10/13] btrace: use the new cpu identifier Markus Metzger
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Markus Metzger @ 2014-11-20 10:47 UTC (permalink / raw)
  To: palves; +Cc: gdb-patches, Eli Zaretskii

Add a struct to describe the branch trace configuration and use it for
enabling branch tracing.

The user will be able to set configuration fields for each tracing format
to be used for new threads.

The actual configuration that is active for a given thread will be shown
in the "info record" command.

At the moment, the configuration struct only contains a format field
that is set to the only available format.

The format is the only configuration option that can not be set via set
commands.  It is given as argument to the "record btrace" command when
starting recording.

CC: Eli Zaretskii  <eliz@gnu.org>

2014-11-20  Markus Metzger  <markus.t.metzger@intel.com>

	* Makefile.in (XMLFILES): Add btrace-conf.dtd.
	* x86-linux-nat.c (x86_linux_enable_btrace): Update parameters.
	(x86_linux_btrace_conf): New.
	(x86_linux_create_target): Initialize to_btrace_conf.
	* nat/linux-btrace.c (linux_enable_btrace): Update parameters.
	Check format.  Split into this and ...
	(linux_enable_bts): ... this.
	(linux_btrace_conf): New.
	(perf_event_skip_record): Renamed into ...
	(perf_event_skip_bts_record): ... this.  Updated users.
	(linux_disable_btrace): Split into this and ...
	(linux_disable_bts): ... this.
	(linux_read_btrace): Check format.
	* nat/linux-btrace.h (linux_enable_btrace): Update parameters.
	(linux_btrace_conf): New.
	(btrace_target_info)<ptid>: Moved.
	(btrace_target_info)<conf>: New.
	(btrace_target_info): Split into this and ...
	(btrace_tinfo_bts): ... this.  Updated users.
	* btrace.c (btrace_enable): Update parameters.
	(btrace_conf, parse_xml_btrace_conf_bts, parse_xml_btrace_conf)
	(btrace_conf_children, btrace_conf_attributes)
	(btrace_conf_elements): New.
	* btrace.h (btrace_enable): Update parameters.
	(btrace_conf, parse_xml_btrace_conf): New.
	* common/btrace-common.h (btrace_config): New.
	* feature/btrace-conf.dtd: New.
	* record-btrace.c (record_btrace_conf): New.
	(record_btrace_cmdlist): New.
	(record_btrace_enable_warn, record_btrace_open): Pass
	&record_btrace_conf.
	(record_btrace_info): Print recording format.
	(cmd_record_btrace_bts_start): New.
	(cmd_record_btrace_start): Call cmd_record_btrace_bts_start.
	(_initialize_record_btrace): Add "record btrace bts" subcommand.
	Add "record bts" alias command.
	* remote.c (remote_state)<btrace_config>: New.
	(remote_btrace_reset, PACKET_qXfer_btrace_conf): New.
	(remote_protocol_features): Add qXfer:btrace-conf:read.
	(remote_open_1): Call remote_btrace_reset.
	(remote_xfer_partial): Handle TARGET_OBJECT_BTRACE_CONF.
	(btrace_target_info)<conf>: New.
	(btrace_sync_conf, btrace_read_config): New.
	(remote_enable_btrace): Update parameters.  Call btrace_sync_conf and
	btrace_read_conf.
	(remote_btrace_conf): New.
	(init_remote_ops): Initialize to_btrace_conf.
	(_initialize_remote): Add qXfer:btrace-conf packet.
	* target.c (target_enable_btrace): Update parameters.
	(target_btrace_conf): New.
	* target.h (target_enable_btrace): Update parameters.
	(target_btrace_conf): New.
	(target_object)<TARGET_OBJECT_BTRACE_CONF>: New.
	(target_ops)<to_enable_btrace>: Update parameters and comment.
	(target_ops)<to_btrace_conf>: New.
	* target-delegates: Regenerate.
	* target-debug.h (target_debug_print_const_struct_btrace_config_p)
	(target_debug_print_const_struct_btrace_target_info_p): New.
	NEWS: Announce new command and new packet.

doc/
	* gdb.texinfo (Process Record and Replay): Describe the "record
	btrace bts" command.
	(General Query Packets): Describe qXfer:btrace-conf:read packet.
	(Branch Trace Configuration Format): New.

gdbserver/
	* linux-low.c (linux_low_enable_btrace): Update parameters.
	(linux_low_btrace_conf): New.
	(linux_target_ops)<to_btrace_conf>: Initialize.
	* server.c (current_btrace_conf): New.
	(handle_btrace_enable): Rename to ...
	(handle_btrace_enable_bts): ... this.  Pass &current_btrace_conf
	to target_enable_btrace.  Update comment.  Update users.
	(handle_qxfer_btrace_conf): New.
    (qxfer_packets): Add btrace-conf entry.
	(handle_query): Report qXfer:btrace-conf:read as supported packet.
	* target.h (target_ops)<enable_btrace>: Update parameters and comment.
	(target_ops)<read_btrace_conf>: New.
	(target_enable_btrace): Update parameters.
	(target_read_btrace_conf): New.

testsuite/
	* gdb.btrace/delta.exp: Update "info record" output.
	* gdb.btrace/enable.exp: Update "info record" output.
	* gdb.btrace/finish.exp: Update "info record" output.
	* gdb.btrace/instruction_history.exp: Update "info record" output.
	* gdb.btrace/next.exp: Update "info record" output.
	* gdb.btrace/nexti.exp: Update "info record" output.
	* gdb.btrace/step.exp: Update "info record" output.
	* gdb.btrace/stepi.exp: Update "info record" output.
	* gdb.btrace/nohist.exp: Update "info record" output.
---
 gdb/Makefile.in                                  |   2 +-
 gdb/NEWS                                         |   9 ++
 gdb/btrace.c                                     |  71 +++++++++++-
 gdb/btrace.h                                     |  11 +-
 gdb/common/btrace-common.h                       |  11 ++
 gdb/doc/gdb.texinfo                              |  70 +++++++++++-
 gdb/features/btrace-conf.dtd                     |  10 ++
 gdb/gdbserver/linux-low.c                        |  35 +++++-
 gdb/gdbserver/server.c                           |  78 ++++++++++++-
 gdb/gdbserver/target.h                           |  19 ++-
 gdb/nat/linux-btrace.c                           | 140 +++++++++++++++++------
 gdb/nat/linux-btrace.h                           |  35 ++++--
 gdb/record-btrace.c                              |  69 +++++++++--
 gdb/remote.c                                     |  85 +++++++++++++-
 gdb/target-debug.h                               |   4 +
 gdb/target-delegates.c                           |  45 +++++++-
 gdb/target.c                                     |  12 +-
 gdb/target.h                                     |  24 +++-
 gdb/testsuite/gdb.btrace/delta.exp               |   4 +
 gdb/testsuite/gdb.btrace/enable.exp              |   4 +-
 gdb/testsuite/gdb.btrace/finish.exp              |   1 +
 gdb/testsuite/gdb.btrace/instruction_history.exp |   2 +-
 gdb/testsuite/gdb.btrace/next.exp                |   2 +
 gdb/testsuite/gdb.btrace/nexti.exp               |   2 +
 gdb/testsuite/gdb.btrace/nohist.exp              |   1 +
 gdb/testsuite/gdb.btrace/step.exp                |   2 +
 gdb/testsuite/gdb.btrace/stepi.exp               |   2 +
 gdb/x86-linux-nat.c                              |  16 ++-
 28 files changed, 676 insertions(+), 90 deletions(-)
 create mode 100644 gdb/features/btrace-conf.dtd

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 344ff94..e7181c4 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -581,7 +581,7 @@ XMLFILES = $(srcdir)/features/gdb-target.dtd $(srcdir)/features/xinclude.dtd \
 	$(srcdir)/features/library-list-aix.dtd \
 	$(srcdir)/features/library-list-svr4.dtd $(srcdir)/features/osdata.dtd \
 	$(srcdir)/features/threads.dtd $(srcdir)/features/traceframe-info.dtd \
-	$(srcdir)/features/btrace.dtd
+	$(srcdir)/features/btrace.dtd $(srcdir)/features/btrace-conf.dtd
 
 # This is ser-unix.o for any system which supports a v7/BSD/SYSV/POSIX
 # interface to the serial port.  Hopefully if get ported to OS/2, VMS,
diff --git a/gdb/NEWS b/gdb/NEWS
index d6a8b61..689eaa1 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -27,6 +27,10 @@
 queue-signal signal-name-or-number
   Queue a signal to be delivered to the thread when it is resumed.
 
+record btrace bts
+record bts
+  Start branch trace recording using Intel(R) Branch Trace Store format.
+
 * On resume, GDB now always passes the signal the program had stopped
   for to the thread the signal was sent to, even if the user changed
   threads before resuming.  Previously GDB would often (but not
@@ -60,6 +64,11 @@ SGI Irix-6.x				mips-*-irix6*
 VAX running (4.2 - 4.3 Reno) BSD 	vax-*-bsd*
 VAX running Ultrix 			vax-*-ultrix*
 
+* New remote packets
+
+qXfer:btrace-conf:read
+  Return the branch trace configuration for the current thread.
+
 *** Changes in GDB 7.8
 
 * New command line options
diff --git a/gdb/btrace.c b/gdb/btrace.c
index d8dae56..6b50be7 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -726,17 +726,17 @@ btrace_add_pc (struct thread_info *tp)
 /* See btrace.h.  */
 
 void
-btrace_enable (struct thread_info *tp)
+btrace_enable (struct thread_info *tp, const struct btrace_config *conf)
 {
   if (tp->btrace.target != NULL)
     return;
 
-  if (!target_supports_btrace (BTRACE_FORMAT_BTS))
+  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));
 
-  tp->btrace.target = target_enable_btrace (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.  */
@@ -746,6 +746,17 @@ btrace_enable (struct thread_info *tp)
 
 /* See btrace.h.  */
 
+const struct btrace_config *
+btrace_conf (const struct btrace_thread_info *btinfo)
+{
+  if (btinfo->target == NULL)
+    return NULL;
+
+  return target_btrace_conf (btinfo->target);
+}
+
+/* See btrace.h.  */
+
 void
 btrace_disable (struct thread_info *tp)
 {
@@ -1106,6 +1117,60 @@ parse_xml_btrace (struct btrace_data *btrace, const char *buffer)
 #endif  /* !defined (HAVE_LIBEXPAT) */
 }
 
+#if defined (HAVE_LIBEXPAT)
+
+/* Parse a btrace-conf "bts" xml record.  */
+
+static void
+parse_xml_btrace_conf_bts (struct gdb_xml_parser *parser,
+			  const struct gdb_xml_element *element,
+			  void *user_data, VEC (gdb_xml_value_s) *attributes)
+{
+  struct btrace_config *conf;
+
+  conf = user_data;
+  conf->format = BTRACE_FORMAT_BTS;
+}
+
+static const struct gdb_xml_element btrace_conf_children[] = {
+  { "bts", NULL, NULL, GDB_XML_EF_OPTIONAL, parse_xml_btrace_conf_bts, NULL },
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_attribute btrace_conf_attributes[] = {
+  { "version", GDB_XML_AF_NONE, NULL, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element btrace_conf_elements[] = {
+  { "btrace-conf", btrace_conf_attributes, btrace_conf_children,
+    GDB_XML_EF_NONE, NULL, NULL },
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+#endif /* defined (HAVE_LIBEXPAT) */
+
+/* See btrace.h.  */
+
+void
+parse_xml_btrace_conf (struct btrace_config *conf, const char *xml)
+{
+  int errcode;
+
+#if defined (HAVE_LIBEXPAT)
+
+  errcode = gdb_xml_parse_quick (_("btrace-conf"), "btrace-conf.dtd",
+				 btrace_conf_elements, xml, conf);
+  if (errcode != 0)
+    error (_("Error parsing branch trace configuration."));
+
+#else  /* !defined (HAVE_LIBEXPAT) */
+
+  error (_("XML parsing is not supported."));
+
+#endif  /* !defined (HAVE_LIBEXPAT) */
+}
+
 /* See btrace.h.  */
 
 const struct btrace_insn *
diff --git a/gdb/btrace.h b/gdb/btrace.h
index 3954581..8656cda 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -215,7 +215,13 @@ struct btrace_thread_info
 };
 
 /* Enable branch tracing for a thread.  */
-extern void btrace_enable (struct thread_info *tp);
+extern void btrace_enable (struct thread_info *tp,
+			   const struct btrace_config *conf);
+
+/* Get the branch trace configuration for a thread.
+   Return NULL if branch tracing is not enabled for that thread.  */
+extern const struct btrace_config *
+  btrace_conf (const struct btrace_thread_info *);
 
 /* Disable branch tracing for a thread.
    This will also delete the current branch trace data.  */
@@ -238,6 +244,9 @@ extern void btrace_free_objfile (struct objfile *);
 /* Parse a branch trace xml document XML into DATA.  */
 extern void parse_xml_btrace (struct btrace_data *data, const char *xml);
 
+/* Parse a branch trace configuration xml document XML into CONF.  */
+extern void parse_xml_btrace_conf (struct btrace_config *conf, const char *xml);
+
 /* Dereference a branch trace instruction iterator.  Return a pointer to the
    instruction the iterator points to.  */
 extern const struct btrace_insn *
diff --git a/gdb/common/btrace-common.h b/gdb/common/btrace-common.h
index 27e7a95..1e0e595 100644
--- a/gdb/common/btrace-common.h
+++ b/gdb/common/btrace-common.h
@@ -62,6 +62,17 @@ enum btrace_format
   BTRACE_FORMAT_BTS
 };
 
+/* A branch tracing configuration.
+
+   This describes the requested configuration as well as the actually
+   obtained configuration.  */
+
+struct btrace_config
+{
+  /* The branch tracing format.  */
+  enum btrace_format format;
+};
+
 /* Branch trace in BTS format.  */
 struct btrace_data_bts
 {
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 15c2908..121ae5b 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6337,9 +6337,13 @@ For architecture environments that support process record and replay,
 @kindex record
 @kindex record full
 @kindex record btrace
+@kindex record btrace bts
+@kindex record bts
 @kindex rec
 @kindex rec full
 @kindex rec btrace
+@kindex rec btrace bts
+@kindex rec bts
 @item record @var{method}
 This command starts the process record and replay target.  The
 recording method can be specified as parameter.  Without a parameter
@@ -6352,13 +6356,24 @@ Full record/replay recording using @value{GDBN}'s software record and
 replay implementation.  This method allows replaying and reverse
 execution.
 
-@item btrace
+@item btrace @var{format}
 Hardware-supported instruction recording.  This method does not record
 data.  Further, the data is collected in a ring buffer so old data will
 be overwritten when the buffer is full.  It allows limited replay and
 reverse execution.
 
-This recording method may not be available on all processors.
+The recording format can be specified as parameter.  Without a parameter
+the command chooses the recording format.  The following recording
+formats are available:
+
+@table @code
+@item bts
+Use Intel's Branch Trace Store (BTS) recording format.  In this format,
+the processor stores a from/to record for each executed branch in the
+btrace ring buffer.
+@end table
+
+Not all recording formats may be available on all processors.
 @end table
 
 The process record and replay target can only debug a process that is
@@ -6536,9 +6551,9 @@ Maximum number of instructions that may be contained in the execution log.
 @end itemize
 
 @item btrace
-For the @code{btrace} recording method, it shows the number of
-instructions that have been recorded and the number of blocks of
-sequential control-flow that is formed by the recorded instructions.
+For the @code{btrace} recording method, it shows the recording format,
+the number of instructions that have been recorded and the number of blocks
+of sequential control-flow that is formed by the recorded instructions.
 @end table
 
 @kindex record delete
@@ -32699,7 +32714,8 @@ MS-Windows shared libraries (@pxref{Shared Libraries})
 @item
 Traceframe info (@pxref{Traceframe Info Format})
 @item
-Branch trace (@pxref{Branch Trace Format})
+Branch trace (@pxref{Branch Trace Format},
+@pxref{Branch Trace Configuration Format})
 @end itemize
 
 @item zlib
@@ -33677,6 +33693,7 @@ Show the current setting of the target wait timeout.
 * Thread List Format::
 * Traceframe Info Format::
 * Branch Trace Format::
+* Branch Trace Configuration Format::
 @end menu
 
 @node Overview
@@ -35349,6 +35366,11 @@ These are the currently defined stub features and their properties:
 @tab @samp{-}
 @tab Yes
 
+@item @samp{qXfer:btrace-conf:read}
+@tab No
+@tab @samp{-}
+@tab Yes
+
 @item @samp{qXfer:features:read}
 @tab No
 @tab @samp{-}
@@ -35533,6 +35555,10 @@ The remote stub understands the @samp{qXfer:auxv:read} packet
 The remote stub understands the @samp{qXfer:btrace:read}
 packet (@pxref{qXfer btrace read}).
 
+@item qXfer:btrace-conf:read
+The remote stub understands the @samp{qXfer:btrace-conf:read}
+packet (@pxref{qXfer btrace-conf read}).
+
 @item qXfer:features:read
 The remote stub understands the @samp{qXfer:features:read} packet
 (@pxref{qXfer target description read}).
@@ -35827,6 +35853,15 @@ If the trace buffer overflowed, returns an error indicating the overflow.
 This packet is not probed by default; the remote stub must request it
 by supplying an appropriate @samp{qSupported} response (@pxref{qSupported}).
 
+@item qXfer:btrace-conf:read::@var{offset},@var{length}
+@anchor{qXfer btrace-conf read}
+
+Return a description of the current branch trace configuration.
+@xref{Branch Trace Configuration Format}.
+
+This packet is not probed by default; the remote stub must request it
+by supplying an appropriate @samp{qSupported} response (@pxref{qSupported}).
+
 @item qXfer:features:read:@var{annex}:@var{offset},@var{length}
 @anchor{qXfer target description read}
 Access the @dfn{target description}.  @xref{Target Descriptions}.  The
@@ -38631,6 +38666,29 @@ The formal DTD for the branch trace format is given below:
                        end    CDATA   #REQUIRED>
 @end smallexample
 
+@node Branch Trace Configuration Format
+@section Branch Trace Configuration Format
+@cindex branch trace configuration format
+
+For each inferior thread, @value{GDBN} can obtain the branch trace
+configuration using the @samp{qXfer:btrace-conf:read}
+(@pxref{qXfer btrace-conf read}) packet.
+
+The configuration describes the branch trace format and configuration
+settings for that format.
+
+@value{GDBN} must be linked with the Expat library to support XML
+branch trace configuration discovery.  @xref{Expat}.
+
+The formal DTD for the branch trace configuration format is given below:
+
+@smallexample
+<!ELEMENT btrace-conf	(bts?)>
+<!ATTLIST btrace-conf	version	CDATA	#FIXED "1.0">
+
+<!ELEMENT bts	EMPTY>
+@end smallexample
+
 @include agentexpr.texi
 
 @node Target Descriptions
diff --git a/gdb/features/btrace-conf.dtd b/gdb/features/btrace-conf.dtd
new file mode 100644
index 0000000..e9cb2ef
--- /dev/null
+++ b/gdb/features/btrace-conf.dtd
@@ -0,0 +1,10 @@
+<!-- Copyright (C) 2013-2014 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!ELEMENT btrace-conf	(bts?)>
+<!ATTLIST btrace-conf	version	CDATA	#FIXED "1.0">
+
+<!ELEMENT bts	EMPTY>
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index dcaa829..8a706a8 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -5935,11 +5935,11 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
 /* See to_enable_btrace target method.  */
 
 static struct btrace_target_info *
-linux_low_enable_btrace (ptid_t ptid)
+linux_low_enable_btrace (ptid_t ptid, const struct btrace_config *conf)
 {
   struct btrace_target_info *tinfo;
 
-  tinfo = linux_enable_btrace (ptid);
+  tinfo = linux_enable_btrace (ptid, conf);
 
   if (tinfo != NULL)
     {
@@ -6017,6 +6017,35 @@ linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
   btrace_data_fini (&btrace);
   return 0;
 }
+
+/* See to_btrace_conf target method.  */
+
+static int
+linux_low_btrace_conf (const struct btrace_target_info *tinfo,
+		       struct buffer *buffer)
+{
+  const struct btrace_config *conf;
+
+  buffer_grow_str (buffer, "<!DOCTYPE btrace-conf SYSTEM \"btrace-conf.dtd\">\n");
+  buffer_grow_str (buffer, "<btrace-conf version=\"1.0\">\n");
+
+  conf = linux_btrace_conf (tinfo);
+  if (conf != NULL)
+    {
+      switch (conf->format)
+	{
+	case BTRACE_FORMAT_NONE:
+	  break;
+
+	case BTRACE_FORMAT_BTS:
+	  buffer_xml_printf (buffer, "<bts/>\n");
+	  break;
+	}
+    }
+
+  buffer_grow_str0 (buffer, "</btrace-conf>\n");
+  return 0;
+}
 #endif /* HAVE_LINUX_BTRACE */
 
 static struct target_ops linux_target_ops = {
@@ -6091,11 +6120,13 @@ static struct target_ops linux_target_ops = {
   linux_low_enable_btrace,
   linux_low_disable_btrace,
   linux_low_read_btrace,
+  linux_low_btrace_conf,
 #else
   NULL,
   NULL,
   NULL,
   NULL,
+  NULL,
 #endif
   linux_supports_range_stepping,
 };
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index dae364d..b8359e6 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -122,6 +122,10 @@ struct vstop_notif
   struct target_waitstatus status;
 };
 
+/* The current btrace configuration.  This is gdbserver's mirror of GDB's
+   btrace configuration.  */
+static struct btrace_config current_btrace_conf;
+
 DEFINE_QUEUE_P (notif_event_p);
 
 /* Put a stop reply to the stop reply queue.  */
@@ -381,15 +385,17 @@ write_qxfer_response (char *buf, const void *data, int len, int is_more)
 			       PBUFSIZ - 2) + 1;
 }
 
-/* Handle btrace enabling.  */
+/* Handle btrace enabling in BTS format.  */
 
 static const char *
-handle_btrace_enable (struct thread_info *thread)
+handle_btrace_enable_bts (struct thread_info *thread)
 {
   if (thread->btrace != NULL)
     return "E.Btrace already enabled.";
 
-  thread->btrace = target_enable_btrace (thread->entry.id);
+  current_btrace_conf.format = BTRACE_FORMAT_BTS;
+  thread->btrace = target_enable_btrace (thread->entry.id,
+					 &current_btrace_conf);
   if (thread->btrace == NULL)
     return "E.Could not enable btrace.";
 
@@ -443,7 +449,7 @@ handle_btrace_general_set (char *own_buf)
   err = NULL;
 
   if (strcmp (op, "bts") == 0)
-    err = handle_btrace_enable (thread);
+    err = handle_btrace_enable_bts (thread);
   else if (strcmp (op, "off") == 0)
     err = handle_btrace_disable (thread);
   else
@@ -1510,10 +1516,73 @@ handle_qxfer_btrace (const char *annex,
   return len;
 }
 
+/* Handle qXfer:btrace-conf:read.  */
+
+static int
+handle_qxfer_btrace_conf (const char *annex,
+			  gdb_byte *readbuf, const gdb_byte *writebuf,
+			  ULONGEST offset, LONGEST len)
+{
+  static struct buffer cache;
+  struct thread_info *thread;
+  int result;
+
+  if (the_target->read_btrace_conf == NULL || writebuf != NULL)
+    return -2;
+
+  if (annex[0] != '\0' || !target_running ())
+    return -1;
+
+  if (ptid_equal (general_thread, null_ptid)
+      || ptid_equal (general_thread, minus_one_ptid))
+    {
+      strcpy (own_buf, "E.Must select a single thread.");
+      return -3;
+    }
+
+  thread = find_thread_ptid (general_thread);
+  if (thread == NULL)
+    {
+      strcpy (own_buf, "E.No such thread.");
+      return -3;
+    }
+
+  if (thread->btrace == NULL)
+    {
+      strcpy (own_buf, "E.Btrace not enabled.");
+      return -3;
+    }
+
+  if (offset == 0)
+    {
+      buffer_free (&cache);
+
+      result = target_read_btrace_conf (thread->btrace, &cache);
+      if (result != 0)
+	{
+	  memcpy (own_buf, cache.buffer, cache.used_size);
+	  return -3;
+	}
+    }
+  else if (offset > cache.used_size)
+    {
+      buffer_free (&cache);
+      return -3;
+    }
+
+  if (len > cache.used_size - offset)
+    len = cache.used_size - offset;
+
+  memcpy (readbuf, cache.buffer + offset, len);
+
+  return len;
+}
+
 static const struct qxfer qxfer_packets[] =
   {
     { "auxv", handle_qxfer_auxv },
     { "btrace", handle_qxfer_btrace },
+    { "btrace-conf", handle_qxfer_btrace_conf },
     { "fdpic", handle_qxfer_fdpic},
     { "features", handle_qxfer_features },
     { "libraries", handle_qxfer_libraries },
@@ -1698,6 +1767,7 @@ supported_btrace_packets (char *buf)
 
   strcat (buf, ";Qbtrace:off+");
   strcat (buf, ";qXfer:btrace:read+");
+  strcat (buf, ";qXfer:btrace-conf:read+");
 }
 
 /* Handle all of the extended 'q' packets.  */
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index dd5b07a..b877df8 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -360,9 +360,10 @@ struct target_ops
   /* Check whether the target supports branch tracing.  */
   int (*supports_btrace) (struct target_ops *, enum btrace_format);
 
-  /* Enable branch tracing for @ptid and allocate a branch trace target
-     information struct for reading and for disabling branch trace.  */
-  struct btrace_target_info *(*enable_btrace) (ptid_t ptid);
+  /* Enable branch tracing for PTID based on CONF and allocate a branch trace
+     target information struct for reading and for disabling branch trace.  */
+  struct btrace_target_info *(*enable_btrace)
+    (ptid_t ptid, const struct btrace_config *conf);
 
   /* Disable branch tracing.
      Returns zero on success, non-zero otherwise.  */
@@ -374,6 +375,11 @@ struct target_ops
      otherwise.  */
   int (*read_btrace) (struct btrace_target_info *, struct buffer *, int type);
 
+  /* Read the branch trace configuration into BUFFER.
+     Return 0 on success; print an error message into BUFFER and return -1
+     otherwise.  */
+  int (*read_btrace_conf) (const struct btrace_target_info *, struct buffer *);
+
   /* Return true if target supports range stepping.  */
   int (*supports_range_stepping) (void);
 };
@@ -503,8 +509,8 @@ int kill_inferior (int);
   (the_target->supports_btrace				\
    ? (*the_target->supports_btrace) (the_target, format) : 0)
 
-#define target_enable_btrace(ptid) \
-  (*the_target->enable_btrace) (ptid)
+#define target_enable_btrace(ptid, conf) \
+  (*the_target->enable_btrace) (ptid, conf)
 
 #define target_disable_btrace(tinfo) \
   (*the_target->disable_btrace) (tinfo)
@@ -512,6 +518,9 @@ int kill_inferior (int);
 #define target_read_btrace(tinfo, buffer, type)	\
   (*the_target->read_btrace) (tinfo, buffer, type)
 
+#define target_read_btrace_conf(tinfo, buffer)	\
+  (*the_target->read_btrace_conf) (tinfo, buffer)
+
 #define target_supports_range_stepping() \
   (the_target->supports_range_stepping ? \
    (*the_target->supports_range_stepping) () : 0)
diff --git a/gdb/nat/linux-btrace.c b/gdb/nat/linux-btrace.c
index 1976512..c369483 100644
--- a/gdb/nat/linux-btrace.c
+++ b/gdb/nat/linux-btrace.c
@@ -90,8 +90,8 @@ perf_event_is_kernel_addr (const struct btrace_target_info *tinfo,
 /* Check whether a perf event record should be skipped.  */
 
 static inline int
-perf_event_skip_record (const struct btrace_target_info *tinfo,
-			const struct perf_event_bts *bts)
+perf_event_skip_bts_record (const struct btrace_target_info *tinfo,
+			    const struct perf_event_bts *bts)
 {
   /* The hardware may report branches from kernel into user space.  Branches
      from user into kernel space will be suppressed.  We filter the former to
@@ -194,7 +194,7 @@ perf_event_read_bts (struct btrace_target_info* tinfo, const uint8_t *begin,
 	  break;
 	}
 
-      if (perf_event_skip_record (tinfo, &psample->bts))
+      if (perf_event_skip_bts_record (tinfo, &psample->bts))
 	continue;
 
       /* We found a valid sample, so we can complete the current block.  */
@@ -395,39 +395,42 @@ linux_supports_btrace (struct target_ops *ops, enum btrace_format format)
   internal_error (__FILE__, __LINE__, _("Unknown branch trace format"));
 }
 
-/* See linux-btrace.h.  */
+/* Enable branch tracing in BTS format.  */
 
-struct btrace_target_info *
-linux_enable_btrace (ptid_t ptid)
+static struct btrace_target_info *
+linux_enable_bts (ptid_t ptid, const struct btrace_config *conf)
 {
   struct perf_event_mmap_page *header;
   struct btrace_target_info *tinfo;
+  struct btrace_tinfo_bts *bts;
   int pid, pg;
 
   tinfo = xzalloc (sizeof (*tinfo));
   tinfo->ptid = ptid;
+  tinfo->ptr_bits = 0;
 
-  tinfo->attr.size = sizeof (tinfo->attr);
-  tinfo->attr.type = PERF_TYPE_HARDWARE;
-  tinfo->attr.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS;
-  tinfo->attr.sample_period = 1;
+  tinfo->conf.format = BTRACE_FORMAT_BTS;
+  bts = &tinfo->variant.bts;
 
-  /* We sample from and to address.  */
-  tinfo->attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_ADDR;
+  bts->attr.size = sizeof (bts->attr);
+  bts->attr.type = PERF_TYPE_HARDWARE;
+  bts->attr.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS;
+  bts->attr.sample_period = 1;
 
-  tinfo->attr.exclude_kernel = 1;
-  tinfo->attr.exclude_hv = 1;
-  tinfo->attr.exclude_idle = 1;
+  /* We sample from and to address.  */
+  bts->attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_ADDR;
 
-  tinfo->ptr_bits = 0;
+  bts->attr.exclude_kernel = 1;
+  bts->attr.exclude_hv = 1;
+  bts->attr.exclude_idle = 1;
 
   pid = ptid_get_lwp (ptid);
   if (pid == 0)
     pid = ptid_get_pid (ptid);
 
   errno = 0;
-  tinfo->file = syscall (SYS_perf_event_open, &tinfo->attr, pid, -1, -1, 0);
-  if (tinfo->file < 0)
+  bts->file = syscall (SYS_perf_event_open, &bts->attr, pid, -1, -1, 0);
+  if (bts->file < 0)
     goto err;
 
   /* We try to allocate as much buffer as we can get.
@@ -437,7 +440,7 @@ linux_enable_btrace (ptid_t ptid)
     {
       /* The number of pages we request needs to be a power of two.  */
       header = mmap (NULL, ((1 << pg) + 1) * PAGE_SIZE, PROT_READ, MAP_SHARED,
-		     tinfo->file, 0);
+		     bts->file, 0);
       if (header != MAP_FAILED)
 	break;
     }
@@ -445,17 +448,17 @@ linux_enable_btrace (ptid_t ptid)
   if (header == MAP_FAILED)
     goto err_file;
 
-  tinfo->header = header;
-  tinfo->bts.mem = ((const uint8_t *) header) + PAGE_SIZE;
-  tinfo->bts.size = (1 << pg) * PAGE_SIZE;
-  tinfo->bts.data_head = &header->data_head;
-  tinfo->bts.last_head = 0;
+  bts->header = header;
+  bts->bts.mem = ((const uint8_t *) header) + PAGE_SIZE;
+  bts->bts.size = (1 << pg) * PAGE_SIZE;
+  bts->bts.data_head = &header->data_head;
+  bts->bts.last_head = 0;
 
   return tinfo;
 
  err_file:
   /* We were not able to allocate any buffer.  */
-  close (tinfo->file);
+  close (bts->file);
 
  err:
   xfree (tinfo);
@@ -464,16 +467,60 @@ linux_enable_btrace (ptid_t ptid)
 
 /* See linux-btrace.h.  */
 
-enum btrace_error
-linux_disable_btrace (struct btrace_target_info *tinfo)
+struct btrace_target_info *
+linux_enable_btrace (ptid_t ptid, const struct btrace_config *conf)
+{
+  struct btrace_target_info *tinfo;
+
+  tinfo = NULL;
+  switch (conf->format)
+    {
+    case BTRACE_FORMAT_NONE:
+      break;
+
+    case BTRACE_FORMAT_BTS:
+      tinfo = linux_enable_bts (ptid, conf);
+      break;
+    }
+
+  return tinfo;
+}
+
+/* Disable BTS tracing.  */
+
+static enum btrace_error
+linux_disable_bts (struct btrace_tinfo_bts *tinfo)
 {
   munmap((void *) tinfo->header, tinfo->bts.size + PAGE_SIZE);
   close (tinfo->file);
-  xfree (tinfo);
 
   return BTRACE_ERR_NONE;
 }
 
+/* See linux-btrace.h.  */
+
+enum btrace_error
+linux_disable_btrace (struct btrace_target_info *tinfo)
+{
+  enum btrace_error errcode;
+
+  errcode = BTRACE_ERR_NOT_SUPPORTED;
+  switch (tinfo->conf.format)
+    {
+    case BTRACE_FORMAT_NONE:
+      break;
+
+    case BTRACE_FORMAT_BTS:
+      errcode = linux_disable_bts (&tinfo->variant.bts);
+      break;
+    }
+
+  if (errcode == BTRACE_ERR_NONE)
+    xfree (tinfo);
+
+  return errcode;
+}
+
 /* Read branch trace data in BTS format for the thread given by TINFO into
    BTRACE using the TYPE reading method.  */
 
@@ -487,7 +534,7 @@ linux_read_bts (struct btrace_data_bts *btrace,
   unsigned long long data_head, data_tail, buffer_size, size;
   size_t retries = 5;
 
-  pevent = &tinfo->bts;
+  pevent = &tinfo->variant.bts.bts;
 
   /* For delta reads, we return at least the partial last block containing
      the current PC.  */
@@ -570,11 +617,28 @@ linux_read_btrace (struct btrace_data *btrace,
 		   struct btrace_target_info *tinfo,
 		   enum btrace_read_type type)
 {
-  /* We read btrace in BTS format.  */
-  btrace->format = BTRACE_FORMAT_BTS;
-  btrace->variant.bts.blocks = NULL;
+  switch (tinfo->conf.format)
+    {
+    case BTRACE_FORMAT_NONE:
+      return BTRACE_ERR_NOT_SUPPORTED;
+
+    case BTRACE_FORMAT_BTS:
+      /* We read btrace in BTS format.  */
+      btrace->format = BTRACE_FORMAT_BTS;
+      btrace->variant.bts.blocks = NULL;
+
+      return linux_read_bts (&btrace->variant.bts, tinfo, type);
+    }
+
+  internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
+}
+
+/* See linux-btrace.h.  */
 
-  return linux_read_bts (&btrace->variant.bts, tinfo, type);
+const struct btrace_config *
+linux_btrace_conf (const struct btrace_target_info *tinfo)
+{
+  return &tinfo->conf;
 }
 
 #else /* !HAVE_LINUX_PERF_EVENT_H */
@@ -590,7 +654,7 @@ linux_supports_btrace (struct target_ops *ops, enum btrace_format format)
 /* See linux-btrace.h.  */
 
 struct btrace_target_info *
-linux_enable_btrace (ptid_t ptid)
+linux_enable_btrace (ptid_t ptid, const struct btrace_config *conf)
 {
   return NULL;
 }
@@ -613,4 +677,12 @@ linux_read_btrace (struct btrace_data *btrace,
   return BTRACE_ERR_NOT_SUPPORTED;
 }
 
+/* See linux-btrace.h.  */
+
+const struct btrace_config *
+linux_btrace_conf (const struct btrace_target_info *tinfo)
+{
+  return NULL;
+}
+
 #endif /* !HAVE_LINUX_PERF_EVENT_H */
diff --git a/gdb/nat/linux-btrace.h b/gdb/nat/linux-btrace.h
index 01ef934..1e9af88 100644
--- a/gdb/nat/linux-btrace.h
+++ b/gdb/nat/linux-btrace.h
@@ -48,18 +48,13 @@ struct perf_event_buffer
   /* The data_head value from the last read.  */
   unsigned long long last_head;
 };
-#endif /* HAVE_LINUX_PERF_EVENT_H */
 
-/* Branch trace target information per thread.  */
-struct btrace_target_info
+/* Branch trace target information for BTS tracing.  */
+struct btrace_tinfo_bts
 {
-#if HAVE_LINUX_PERF_EVENT_H
   /* The Linux perf_event configuration for collecting the branch trace.  */
   struct perf_event_attr attr;
 
-  /* The ptid of this thread.  */
-  ptid_t ptid;
-
   /* The perf event file.  */
   int file;
 
@@ -68,6 +63,25 @@ struct btrace_target_info
 
   /* The BTS perf event buffer.  */
   struct perf_event_buffer bts;
+};
+#endif /* HAVE_LINUX_PERF_EVENT_H */
+
+/* Branch trace target information per thread.  */
+struct btrace_target_info
+{
+  /* The ptid of this thread.  */
+  ptid_t ptid;
+
+  /* The obtained branch trace configuration.  */
+  struct btrace_config conf;
+
+#if HAVE_LINUX_PERF_EVENT_H
+  /* The branch tracing format specific information.  */
+  union
+  {
+    /* CONF.FORMAT == BTRACE_FORMAT_BTS.  */
+    struct btrace_tinfo_bts bts;
+  } variant;
 #endif /* HAVE_LINUX_PERF_EVENT_H */
 
   /* The size of a pointer in bits for this thread.
@@ -80,7 +94,8 @@ struct btrace_target_info
 extern int linux_supports_btrace (struct target_ops *, enum btrace_format);
 
 /* See to_enable_btrace in target.h.  */
-extern struct btrace_target_info *linux_enable_btrace (ptid_t ptid);
+extern struct btrace_target_info *
+  linux_enable_btrace (ptid_t ptid, const struct btrace_config *conf);
 
 /* See to_disable_btrace in target.h.  */
 extern enum btrace_error linux_disable_btrace (struct btrace_target_info *ti);
@@ -90,4 +105,8 @@ extern enum btrace_error linux_read_btrace (struct btrace_data *btrace,
 					    struct btrace_target_info *btinfo,
 					    enum btrace_read_type type);
 
+/* See to_btrace_conf in target.h.  */
+extern const struct btrace_config *
+  linux_btrace_conf (const struct btrace_target_info *);
+
 #endif /* LINUX_BTRACE_H */
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 6bc1d15..d9ad9d4 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -70,6 +70,12 @@ static struct async_event_handler *record_btrace_async_inferior_event_handler;
 /* A flag indicating that we are currently generating a core file.  */
 static int record_btrace_generating_corefile;
 
+/* The current branch trace configuration.  */
+static struct btrace_config record_btrace_conf;
+
+/* Command list for "record btrace".  */
+static struct cmd_list_element *record_btrace_cmdlist;
+
 /* Print a record-btrace debug message.  Use do ... while (0) to avoid
    ambiguities when used in if statements.  */
 
@@ -132,7 +138,7 @@ record_btrace_enable_warn (struct thread_info *tp)
   volatile struct gdb_exception error;
 
   TRY_CATCH (error, RETURN_MASK_ERROR)
-    btrace_enable (tp);
+    btrace_enable (tp, &record_btrace_conf);
 
   if (error.message != NULL)
     warning ("%s", error.message);
@@ -208,7 +214,7 @@ record_btrace_open (const char *args, int from_tty)
   ALL_NON_EXITED_THREADS (tp)
     if (args == NULL || *args == 0 || number_is_in_list (args, tp->num))
       {
-	btrace_enable (tp);
+	btrace_enable (tp, &record_btrace_conf);
 
 	make_cleanup (record_btrace_disable_callback, tp);
       }
@@ -269,6 +275,7 @@ static void
 record_btrace_info (struct target_ops *self)
 {
   struct btrace_thread_info *btinfo;
+  const struct btrace_config *conf;
   struct thread_info *tp;
   unsigned int insns, calls;
 
@@ -278,13 +285,18 @@ record_btrace_info (struct target_ops *self)
   if (tp == NULL)
     error (_("No thread."));
 
+  btinfo = &tp->btrace;
+
+  conf = btrace_conf (btinfo);
+  if (conf != NULL)
+    printf_unfiltered (_("Recording format: %s.\n"),
+		       btrace_format_string (conf->format));
+
   btrace_fetch (tp);
 
   insns = 0;
   calls = 0;
 
-  btinfo = &tp->btrace;
-
   if (!btrace_is_empty (tp))
     {
       struct btrace_call_iterator call;
@@ -1974,15 +1986,48 @@ init_record_btrace_ops (void)
   ops->to_magic = OPS_MAGIC;
 }
 
+/* Start recording in BTS format.  */
+
+static void
+cmd_record_btrace_bts_start (char *args, int from_tty)
+{
+  volatile struct gdb_exception exception;
+
+  if (args != NULL && *args != 0)
+    error (_("Invalid argument."));
+
+  record_btrace_conf.format = BTRACE_FORMAT_BTS;
+
+  TRY_CATCH (exception, RETURN_MASK_ALL)
+    execute_command ("target record-btrace", from_tty);
+
+  if (exception.error != 0)
+    {
+      record_btrace_conf.format = BTRACE_FORMAT_NONE;
+      throw_exception (exception);
+    }
+}
+
 /* Alias for "target record".  */
 
 static void
 cmd_record_btrace_start (char *args, int from_tty)
 {
+  volatile struct gdb_exception exception;
+
   if (args != NULL && *args != 0)
     error (_("Invalid argument."));
 
-  execute_command ("target record-btrace", from_tty);
+  record_btrace_conf.format = BTRACE_FORMAT_BTS;
+
+  TRY_CATCH (exception, RETURN_MASK_ALL)
+    execute_command ("target record-btrace", from_tty);
+
+  if (exception.error == 0)
+    return;
+
+  record_btrace_conf.format = BTRACE_FORMAT_NONE;
+  throw_exception (exception);
 }
 
 /* The "set record btrace" command.  */
@@ -2018,11 +2063,19 @@ void _initialize_record_btrace (void);
 void
 _initialize_record_btrace (void)
 {
-  add_cmd ("btrace", class_obscure, cmd_record_btrace_start,
-	   _("Start branch trace recording."),
-	   &record_cmdlist);
+  add_prefix_cmd ("btrace", class_obscure, cmd_record_btrace_start,
+		  _("Start branch trace recording."), &record_btrace_cmdlist,
+		  "record btrace ", 0, &record_cmdlist);
   add_alias_cmd ("b", "btrace", class_obscure, 1, &record_cmdlist);
 
+  add_cmd ("bts", class_obscure, cmd_record_btrace_bts_start,
+	   _("\
+Start branch trace recording in Intel(R) Branch Trace Store (BTS) format.\n\n\
+The processor stores a from/to record for each branch into a cyclic buffer.\n\
+This format may not be available on all processors."),
+	   &record_btrace_cmdlist);
+  add_alias_cmd ("bts", "btrace bts", class_obscure, 1, &record_cmdlist);
+
   add_prefix_cmd ("btrace", class_support, cmd_set_record_btrace,
 		  _("Set record options"), &set_record_btrace_cmdlist,
 		  "set record btrace ", 0, &set_record_cmdlist);
diff --git a/gdb/remote.c b/gdb/remote.c
index e31698f..16cb60f 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -221,6 +221,8 @@ static int remote_supports_cond_breakpoints (struct target_ops *self);
 
 static int remote_can_run_breakpoint_commands (struct target_ops *self);
 
+static void remote_btrace_reset (void);
+
 /* For "remote".  */
 
 static struct cmd_list_element *remote_cmdlist;
@@ -371,6 +373,9 @@ struct remote_state
 
   /* The state of remote notification.  */
   struct remote_notif_state *notif_state;
+
+  /* The branch trace configuration.  */
+  struct btrace_config btrace_config;
 };
 
 /* Private data that we'll store in (struct thread_info)->private.  */
@@ -1327,6 +1332,9 @@ enum {
   /* Support for qXfer:libraries-svr4:read with a non-empty annex.  */
   PACKET_augmented_libraries_svr4_read_feature,
 
+  /* Support for the qXfer:btrace-conf:read packet.  */
+  PACKET_qXfer_btrace_conf,
+
   PACKET_MAX
 };
 
@@ -4010,7 +4018,9 @@ static const struct protocol_feature remote_protocol_features[] = {
   { "Qbtrace:off", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_off },
   { "Qbtrace:bts", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_bts },
   { "qXfer:btrace:read", PACKET_DISABLE, remote_supported_packet,
-    PACKET_qXfer_btrace }
+    PACKET_qXfer_btrace },
+  { "qXfer:btrace-conf:read", PACKET_DISABLE, remote_supported_packet,
+    PACKET_qXfer_btrace_conf }
 };
 
 static char *remote_support_xml;
@@ -4358,6 +4368,8 @@ remote_open_1 (const char *name, int from_tty,
       }
   }
 
+  remote_btrace_reset ();
+
   if (target_async_permitted)
     wait_forever_enabled_p = 1;
 }
@@ -8945,6 +8957,11 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object,
 				xfered_len,
         &remote_protocol_packets[PACKET_qXfer_btrace]);
 
+    case TARGET_OBJECT_BTRACE_CONF:
+      return remote_read_qxfer (ops, "btrace-conf", annex, readbuf, offset,
+				len, xfered_len,
+	&remote_protocol_packets[PACKET_qXfer_btrace_conf]);
+
     default:
       return TARGET_XFER_E_IO;
     }
@@ -11319,8 +11336,21 @@ struct btrace_target_info
 {
   /* The ptid of the traced thread.  */
   ptid_t ptid;
+
+  /* The obtained branch trace configuration.  */
+  struct btrace_config conf;
 };
 
+/* Reset our idea of our target's btrace configuration.  */
+
+static void
+remote_btrace_reset (void)
+{
+  struct remote_state *rs = get_remote_state ();
+
+  memset (&rs->btrace_config, 0, sizeof (rs->btrace_config));
+}
+
 /* Check whether the target supports branch tracing.  */
 
 static int
@@ -11343,20 +11373,52 @@ remote_supports_btrace (struct target_ops *self, enum btrace_format format)
   internal_error (__FILE__, __LINE__, _("Unknown branch trace format"));
 }
 
+/* Synchronize the configuration with the target.  */
+
+static void
+btrace_sync_conf (const struct btrace_config *conf)
+{
+  /* Nothing to do for now.  */
+}
+
+/* Read the current thread's btrace configuration from the target and
+   store it into CONF.  */
+
+static void
+btrace_read_config (struct btrace_config *conf)
+{
+  char *xml;
+
+  xml = target_read_stralloc (&current_target,
+                              TARGET_OBJECT_BTRACE_CONF, "");
+  if (xml != NULL)
+    {
+      struct cleanup *cleanup;
+
+      cleanup = make_cleanup (xfree, xml);
+      parse_xml_btrace_conf (conf, xml);
+      do_cleanups (cleanup);
+    }
+}
+
 /* Enable branch tracing.  */
 
 static struct btrace_target_info *
-remote_enable_btrace (struct target_ops *self, ptid_t ptid)
+remote_enable_btrace (struct target_ops *self, ptid_t ptid,
+		      const struct btrace_config *conf)
 {
   struct btrace_target_info *tinfo = NULL;
   struct packet_config *packet = &remote_protocol_packets[PACKET_Qbtrace_bts];
   struct remote_state *rs = get_remote_state ();
   char *buf = rs->buf;
   char *endbuf = rs->buf + get_remote_packet_size ();
+  volatile struct gdb_exception err;
 
   if (packet_config_support (packet) != PACKET_ENABLE)
     error (_("Target does not support branch tracing."));
 
+  btrace_sync_conf (conf);
+
   set_general_thread (ptid);
 
   buf += xsnprintf (buf, endbuf - buf, "%s", packet->name);
@@ -11376,6 +11438,14 @@ remote_enable_btrace (struct target_ops *self, ptid_t ptid)
   tinfo = xzalloc (sizeof (*tinfo));
   tinfo->ptid = ptid;
 
+  /* If we fail to read the configuration, we lose some information, but the
+     tracing itself is not impacted.  */
+  TRY_CATCH (err, RETURN_MASK_ERROR)
+    btrace_read_config (&tinfo->conf);
+
+  if (err.message != NULL)
+    warning ("%s", err.message);
+
   return tinfo;
 }
 
@@ -11472,6 +11542,13 @@ remote_read_btrace (struct target_ops *self,
   return BTRACE_ERR_NONE;
 }
 
+static const struct btrace_config *
+remote_btrace_conf (struct target_ops *self,
+		    const struct btrace_target_info *tinfo)
+{
+  return &tinfo->conf;
+}
+
 static int
 remote_augmented_libraries_svr4_read (struct target_ops *self)
 {
@@ -11608,6 +11685,7 @@ Specify the serial device it is connected to\n\
   remote_ops.to_disable_btrace = remote_disable_btrace;
   remote_ops.to_teardown_btrace = remote_teardown_btrace;
   remote_ops.to_read_btrace = remote_read_btrace;
+  remote_ops.to_btrace_conf = remote_btrace_conf;
   remote_ops.to_augmented_libraries_svr4_read =
     remote_augmented_libraries_svr4_read;
 }
@@ -12198,6 +12276,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_btrace],
        "qXfer:btrace", "read-btrace", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_btrace_conf],
+       "qXfer:btrace-conf", "read-btrace-conf", 0);
+
   /* Assert that we've registered commands for all packet configs.  */
   {
     int i;
diff --git a/gdb/target-debug.h b/gdb/target-debug.h
index d243ad6..d73b795 100644
--- a/gdb/target-debug.h
+++ b/gdb/target-debug.h
@@ -148,6 +148,10 @@
   target_debug_do_print (host_address_to_string (X))
 #define target_debug_print_enum_btrace_format(X)	\
   target_debug_do_print (plongest (X))
+#define target_debug_print_const_struct_btrace_config_p(X)	\
+  target_debug_do_print (host_address_to_string (X))
+#define target_debug_print_const_struct_btrace_target_info_p(X)	\
+  target_debug_do_print (host_address_to_string (X))
 
 static void
 target_debug_print_struct_target_waitstatus_p (struct target_waitstatus *status)
diff --git a/gdb/target-delegates.c b/gdb/target-delegates.c
index 9cdb31e..e026179 100644
--- a/gdb/target-delegates.c
+++ b/gdb/target-delegates.c
@@ -3114,28 +3114,30 @@ debug_supports_btrace (struct target_ops *self, enum btrace_format arg1)
 }
 
 static struct btrace_target_info *
-delegate_enable_btrace (struct target_ops *self, ptid_t arg1)
+delegate_enable_btrace (struct target_ops *self, ptid_t arg1, const struct btrace_config *arg2)
 {
   self = self->beneath;
-  return self->to_enable_btrace (self, arg1);
+  return self->to_enable_btrace (self, arg1, arg2);
 }
 
 static struct btrace_target_info *
-tdefault_enable_btrace (struct target_ops *self, ptid_t arg1)
+tdefault_enable_btrace (struct target_ops *self, ptid_t arg1, const struct btrace_config *arg2)
 {
   tcomplain ();
 }
 
 static struct btrace_target_info *
-debug_enable_btrace (struct target_ops *self, ptid_t arg1)
+debug_enable_btrace (struct target_ops *self, ptid_t arg1, const struct btrace_config *arg2)
 {
   struct btrace_target_info * result;
   fprintf_unfiltered (gdb_stdlog, "-> %s->to_enable_btrace (...)\n", debug_target.to_shortname);
-  result = debug_target.to_enable_btrace (&debug_target, arg1);
+  result = debug_target.to_enable_btrace (&debug_target, arg1, arg2);
   fprintf_unfiltered (gdb_stdlog, "<- %s->to_enable_btrace (", debug_target.to_shortname);
   target_debug_print_struct_target_ops_p (&debug_target);
   fputs_unfiltered (", ", gdb_stdlog);
   target_debug_print_ptid_t (arg1);
+  fputs_unfiltered (", ", gdb_stdlog);
+  target_debug_print_const_struct_btrace_config_p (arg2);
   fputs_unfiltered (") = ", gdb_stdlog);
   target_debug_print_struct_btrace_target_info_p (result);
   fputs_unfiltered ("\n", gdb_stdlog);
@@ -3225,6 +3227,35 @@ debug_read_btrace (struct target_ops *self, struct btrace_data *arg1, struct btr
   return result;
 }
 
+static const struct btrace_config *
+delegate_btrace_conf (struct target_ops *self, const struct btrace_target_info *arg1)
+{
+  self = self->beneath;
+  return self->to_btrace_conf (self, arg1);
+}
+
+static const struct btrace_config *
+tdefault_btrace_conf (struct target_ops *self, const struct btrace_target_info *arg1)
+{
+  return NULL;
+}
+
+static const struct btrace_config *
+debug_btrace_conf (struct target_ops *self, const struct btrace_target_info *arg1)
+{
+  const struct btrace_config * result;
+  fprintf_unfiltered (gdb_stdlog, "-> %s->to_btrace_conf (...)\n", debug_target.to_shortname);
+  result = debug_target.to_btrace_conf (&debug_target, arg1);
+  fprintf_unfiltered (gdb_stdlog, "<- %s->to_btrace_conf (", debug_target.to_shortname);
+  target_debug_print_struct_target_ops_p (&debug_target);
+  fputs_unfiltered (", ", gdb_stdlog);
+  target_debug_print_const_struct_btrace_target_info_p (arg1);
+  fputs_unfiltered (") = ", gdb_stdlog);
+  target_debug_print_const_struct_btrace_config_p (result);
+  fputs_unfiltered ("\n", gdb_stdlog);
+  return result;
+}
+
 static void
 delegate_stop_recording (struct target_ops *self)
 {
@@ -3972,6 +4003,8 @@ install_delegators (struct target_ops *ops)
     ops->to_teardown_btrace = delegate_teardown_btrace;
   if (ops->to_read_btrace == NULL)
     ops->to_read_btrace = delegate_read_btrace;
+  if (ops->to_btrace_conf == NULL)
+    ops->to_btrace_conf = delegate_btrace_conf;
   if (ops->to_stop_recording == NULL)
     ops->to_stop_recording = delegate_stop_recording;
   if (ops->to_info_record == NULL)
@@ -4135,6 +4168,7 @@ install_dummy_methods (struct target_ops *ops)
   ops->to_disable_btrace = tdefault_disable_btrace;
   ops->to_teardown_btrace = tdefault_teardown_btrace;
   ops->to_read_btrace = tdefault_read_btrace;
+  ops->to_btrace_conf = tdefault_btrace_conf;
   ops->to_stop_recording = tdefault_stop_recording;
   ops->to_info_record = tdefault_info_record;
   ops->to_save_record = tdefault_save_record;
@@ -4278,6 +4312,7 @@ init_debug_target (struct target_ops *ops)
   ops->to_disable_btrace = debug_disable_btrace;
   ops->to_teardown_btrace = debug_teardown_btrace;
   ops->to_read_btrace = debug_read_btrace;
+  ops->to_btrace_conf = debug_btrace_conf;
   ops->to_stop_recording = debug_stop_recording;
   ops->to_info_record = debug_info_record;
   ops->to_save_record = debug_save_record;
diff --git a/gdb/target.c b/gdb/target.c
index c847276..10fcab4 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -3364,9 +3364,9 @@ target_supports_btrace (enum btrace_format format)
 /* See target.h.  */
 
 struct btrace_target_info *
-target_enable_btrace (ptid_t ptid)
+target_enable_btrace (ptid_t ptid, const struct btrace_config *conf)
 {
-  return current_target.to_enable_btrace (&current_target, ptid);
+  return current_target.to_enable_btrace (&current_target, ptid, conf);
 }
 
 /* See target.h.  */
@@ -3397,6 +3397,14 @@ target_read_btrace (struct btrace_data *btrace,
 
 /* See target.h.  */
 
+const struct btrace_config *
+target_btrace_conf (const struct btrace_target_info *btinfo)
+{
+  return current_target.to_btrace_conf (&current_target, btinfo);
+}
+
+/* See target.h.  */
+
 void
 target_stop_recording (void)
 {
diff --git a/gdb/target.h b/gdb/target.h
index 1654df2..bd22151 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -200,7 +200,9 @@ enum target_object
   /* OpenVMS Unwind Information Block.  */
   TARGET_OBJECT_OPENVMS_UIB,
   /* Branch trace data, in XML format.  */
-  TARGET_OBJECT_BTRACE
+  TARGET_OBJECT_BTRACE,
+  /* Branch trace configuration, in XML format.  */
+  TARGET_OBJECT_BTRACE_CONF
   /* Possible future objects: TARGET_OBJECT_FILE, ...  */
 };
 
@@ -1002,10 +1004,12 @@ struct target_ops
     int (*to_supports_btrace) (struct target_ops *, enum btrace_format)
       TARGET_DEFAULT_RETURN (0);
 
-    /* Enable branch tracing for PTID and allocate a branch trace target
-       information struct for reading and for disabling branch trace.  */
+    /* Enable branch tracing for PTID using CONF configuration.
+       Return a branch trace target information struct for reading and for
+       disabling branch trace.  */
     struct btrace_target_info *(*to_enable_btrace) (struct target_ops *,
-						    ptid_t ptid)
+						    ptid_t ptid,
+						    const struct btrace_config *conf)
       TARGET_DEFAULT_NORETURN (tcomplain ());
 
     /* Disable branch tracing and deallocate TINFO.  */
@@ -1029,6 +1033,11 @@ struct target_ops
 					 enum btrace_read_type type)
       TARGET_DEFAULT_NORETURN (tcomplain ());
 
+    /* Get the branch trace configuration.  */
+    const struct btrace_config *(*to_btrace_conf) (struct target_ops *self,
+						   const struct btrace_target_info *)
+      TARGET_DEFAULT_RETURN (NULL);
+
     /* Stop trace recording.  */
     void (*to_stop_recording) (struct target_ops *)
       TARGET_DEFAULT_IGNORE ();
@@ -2214,7 +2223,8 @@ extern void update_target_permissions (void);
 extern int target_supports_btrace (enum btrace_format);
 
 /* See to_enable_btrace in struct target_ops.  */
-extern struct btrace_target_info *target_enable_btrace (ptid_t ptid);
+extern struct btrace_target_info *
+  target_enable_btrace (ptid_t ptid, const struct btrace_config *);
 
 /* See to_disable_btrace in struct target_ops.  */
 extern void target_disable_btrace (struct btrace_target_info *btinfo);
@@ -2227,6 +2237,10 @@ extern enum btrace_error target_read_btrace (struct btrace_data *,
 					     struct btrace_target_info *,
 					     enum btrace_read_type);
 
+/* See to_btrace_conf in struct target_ops.  */
+extern const struct btrace_config *
+  target_btrace_conf (const struct btrace_target_info *);
+
 /* See to_stop_recording in struct target_ops.  */
 extern void target_stop_recording (void);
 
diff --git a/gdb/testsuite/gdb.btrace/delta.exp b/gdb/testsuite/gdb.btrace/delta.exp
index 5c3505c..71ccf07 100644
--- a/gdb/testsuite/gdb.btrace/delta.exp
+++ b/gdb/testsuite/gdb.btrace/delta.exp
@@ -39,6 +39,7 @@ gdb_test_no_output "record btrace"
 with_test_prefix "no trace" {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
+    "Recording format: .*" \
     "Recorded 0 instructions in 0 functions for .*" \
     ] "\r\n"]
   gdb_test "record instruction-history" "No trace\."
@@ -51,6 +52,7 @@ gdb_test "stepi"
 proc check_trace {} {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
+    "Recording format: .*" \
     "Recorded 1 instructions in 1 functions for .*" \
     ] "\r\n"]
   gdb_test "record instruction-history /f 1" \
@@ -71,6 +73,7 @@ with_test_prefix "twice" {
 gdb_test "reverse-stepi"
 gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
+  "Recording format: .*" \
   "Recorded 1 instructions in 1 functions for .*" \
   "Replay in progress\.  At instruction 1\." \
   ] "\r\n"] "reverse-stepi"
@@ -79,5 +82,6 @@ gdb_test "info record" [join [list \
 gdb_test "stepi"
 gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
+  "Recording format: .*" \
   "Recorded 1 instructions in 1 functions for .*" \
   ] "\r\n"] "and back"
diff --git a/gdb/testsuite/gdb.btrace/enable.exp b/gdb/testsuite/gdb.btrace/enable.exp
index 78c7e53..2926c0f 100644
--- a/gdb/testsuite/gdb.btrace/enable.exp
+++ b/gdb/testsuite/gdb.btrace/enable.exp
@@ -56,7 +56,9 @@ gdb_test "record btrace" "The process is already being recorded\\.  Use \"record
 gdb_test "record full" "The process is already being recorded\\.  Use \"record stop\" to stop recording first\\." "record full cannot be enabled"
 
 # no trace recorded yet
-gdb_test "info record" "Active record target: record-btrace\r\nRecorded 0 instructions in 0 functions for thread 1.*\\." "info record without trace"
+gdb_test "info record" "Active record target: record-btrace\r
+.*\r
+Recorded 0 instructions in 0 functions for thread 1.*\\." "info record without trace"
 
 # stop btrace record
 gdb_test "record stop" "Process record is stopped and all execution logs are deleted\\." "record stop"
diff --git a/gdb/testsuite/gdb.btrace/finish.exp b/gdb/testsuite/gdb.btrace/finish.exp
index a27082e..593055b 100644
--- a/gdb/testsuite/gdb.btrace/finish.exp
+++ b/gdb/testsuite/gdb.btrace/finish.exp
@@ -37,6 +37,7 @@ gdb_test "next"
 proc check_replay_at { insn } {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
+    "Recording format: .*" \
     "Recorded 40 instructions in 16 functions for .*" \
     "Replay in progress\.  At instruction $insn\." \
     ] "\r\n"]
diff --git a/gdb/testsuite/gdb.btrace/instruction_history.exp b/gdb/testsuite/gdb.btrace/instruction_history.exp
index 29102c4..5a77357 100644
--- a/gdb/testsuite/gdb.btrace/instruction_history.exp
+++ b/gdb/testsuite/gdb.btrace/instruction_history.exp
@@ -50,7 +50,7 @@ gdb_continue_to_breakpoint "cont to $bp_location" ".*$srcfile2:$bp_location.*"
 set traced {}
 set testname "determine number of recorded instructions"
 gdb_test_multiple "info record" $testname {
-    -re "Active record target: record-btrace\r\nRecorded \(\[0-9\]*\) instructions in \(\[0-9\]*\) functions for thread 1 .*\\.\r\n$gdb_prompt $" {
+    -re "Active record target: record-btrace\r\n.*\r\nRecorded \(\[0-9\]*\) instructions in \(\[0-9\]*\) functions for thread 1 .*\\.\r\n$gdb_prompt $" {
         set traced $expect_out(1,string)
         pass $testname
     }
diff --git a/gdb/testsuite/gdb.btrace/next.exp b/gdb/testsuite/gdb.btrace/next.exp
index b39eb79..1bc8125 100644
--- a/gdb/testsuite/gdb.btrace/next.exp
+++ b/gdb/testsuite/gdb.btrace/next.exp
@@ -37,6 +37,7 @@ gdb_test "next"
 proc check_replay_at { insn } {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
+    "Recording format: .*" \
     "Recorded 40 instructions in 16 functions for .*" \
     "Replay in progress\.  At instruction $insn\." \
     ] "\r\n"]
@@ -55,6 +56,7 @@ with_test_prefix "reverse-next - 2" { check_replay_at 1 }
 gdb_test "next" ".*main\.3.*"
 gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
+  "Recording format: .*" \
   "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
   ] "\r\n"] "next back"
 
diff --git a/gdb/testsuite/gdb.btrace/nexti.exp b/gdb/testsuite/gdb.btrace/nexti.exp
index fd94df6..a263607 100644
--- a/gdb/testsuite/gdb.btrace/nexti.exp
+++ b/gdb/testsuite/gdb.btrace/nexti.exp
@@ -37,6 +37,7 @@ gdb_test "next"
 proc check_replay_at { insn } {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
+    "Recording format: .*" \
     "Recorded 40 instructions in 16 functions for .*" \
     "Replay in progress\.  At instruction $insn\." \
     ] "\r\n"]
@@ -55,6 +56,7 @@ with_test_prefix "reverse-nexti - 1" { check_replay_at 1 }
 gdb_test "nexti" ".*main\.3.*" "next, 1.5"
 gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
+  "Recording format: .*" \
   "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
   ] "\r\n"] "nexti back"
 
diff --git a/gdb/testsuite/gdb.btrace/nohist.exp b/gdb/testsuite/gdb.btrace/nohist.exp
index c0a1a87..4c1d875 100644
--- a/gdb/testsuite/gdb.btrace/nohist.exp
+++ b/gdb/testsuite/gdb.btrace/nohist.exp
@@ -33,6 +33,7 @@ if ![runto_main] {
 proc check_not_replaying {} {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
+    "Recording format: .*" \
 	"Recorded 0 instructions in 0 functions for \[^\\\r\\\n\]*" \
     ] "\r\n"]
 }
diff --git a/gdb/testsuite/gdb.btrace/step.exp b/gdb/testsuite/gdb.btrace/step.exp
index 7c34d25..483166b 100644
--- a/gdb/testsuite/gdb.btrace/step.exp
+++ b/gdb/testsuite/gdb.btrace/step.exp
@@ -37,6 +37,7 @@ gdb_test "next"
 proc check_replay_at { insn } {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
+    "Recording format: .*" \
     "Recorded 40 instructions in 16 functions for .*" \
     "Replay in progress\.  At instruction $insn\." \
     ] "\r\n"]
@@ -85,5 +86,6 @@ with_test_prefix "step to 39" { check_replay_at 39 }
 gdb_test "step" ".*main\.3.*"
 gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
+  "Recording format: .*" \
   "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
   ] "\r\n"] "step to live"
diff --git a/gdb/testsuite/gdb.btrace/stepi.exp b/gdb/testsuite/gdb.btrace/stepi.exp
index fb12e28..cce41e6 100644
--- a/gdb/testsuite/gdb.btrace/stepi.exp
+++ b/gdb/testsuite/gdb.btrace/stepi.exp
@@ -35,6 +35,7 @@ if ![runto_main] {
 proc check_replay_at { insn } {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
+    "Recording format: .*" \
     "Recorded 40 instructions in 16 functions for .*" \
     "Replay in progress\.  At instruction $insn\." \
     ] "\r\n"]
@@ -59,6 +60,7 @@ with_test_prefix "stepi to 40" { check_replay_at 40 }
 gdb_test "stepi" ".*main\.3.*"
 gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
+  "Recording format: .*" \
   "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
   ] "\r\n"] "stepi to live"
 
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
index d9ba33e..1548f67 100644
--- a/gdb/x86-linux-nat.c
+++ b/gdb/x86-linux-nat.c
@@ -427,13 +427,14 @@ x86_linux_read_description (struct target_ops *ops)
 /* Enable branch tracing.  */
 
 static struct btrace_target_info *
-x86_linux_enable_btrace (struct target_ops *self, ptid_t ptid)
+x86_linux_enable_btrace (struct target_ops *self, ptid_t ptid,
+			 const struct btrace_config *conf)
 {
   struct btrace_target_info *tinfo;
   struct gdbarch *gdbarch;
 
   errno = 0;
-  tinfo = linux_enable_btrace (ptid);
+  tinfo = linux_enable_btrace (ptid, conf);
 
   if (tinfo == NULL)
     error (_("Could not enable branch tracing for %s: %s."),
@@ -476,6 +477,16 @@ x86_linux_read_btrace (struct target_ops *self,
 {
   return linux_read_btrace (data, btinfo, type);
 }
+
+/* See to_btrace_conf in target.h.  */
+
+static const struct btrace_config *
+x86_linux_btrace_conf (struct target_ops *self,
+		       const struct btrace_target_info *btinfo)
+{
+  return linux_btrace_conf (btinfo);
+}
+
 \f
 
 /* Helper for ps_get_thread_area.  Sets BASE_ADDR to a pointer to
@@ -550,6 +561,7 @@ x86_linux_create_target (void)
   t->to_disable_btrace = x86_linux_disable_btrace;
   t->to_teardown_btrace = x86_linux_teardown_btrace;
   t->to_read_btrace = x86_linux_read_btrace;
+  t->to_btrace_conf = x86_linux_btrace_conf;
 
   return t;
 }
-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH v2 09/13] btrace: identify cpu
  2014-11-20 10:47 [PATCH v2 00/13] record btrace: prepare for a new trace format Markus Metzger
@ 2014-11-20 10:47 ` Markus Metzger
  2014-11-20 10:47 ` [PATCH v2 02/13] btrace: add format argument to supports_btrace Markus Metzger
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Markus Metzger @ 2014-11-20 10:47 UTC (permalink / raw)
  To: palves; +Cc: gdb-patches

Add a struct for identifying a processor and a function to identify the
processor we're running on.

We will need this feature for the new btrace format.

2014-11-20  Markus Metzger  <markus.t.metzger@intel.com>

	* nat/x86-btrace.h (btrace_cpu_vendor, btrace_cpu)
	(btrace_this_cpu): New.
	* nat/x86-btrace.c: Include x86-cpuid.h.
	(btrace_this_cpu): New.
---
 gdb/nat/x86-btrace.c | 37 +++++++++++++++++++++++++++++++++++++
 gdb/nat/x86-btrace.h | 31 +++++++++++++++++++++++++++++++
 2 files changed, 68 insertions(+)

diff --git a/gdb/nat/x86-btrace.c b/gdb/nat/x86-btrace.c
index 5ca5731..845e895 100644
--- a/gdb/nat/x86-btrace.c
+++ b/gdb/nat/x86-btrace.c
@@ -18,10 +18,47 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "x86-btrace.h"
+#include "x86-cpuid.h"
 
 
 /* See x86-btrace.h.  */
 
+struct btrace_cpu
+btrace_this_cpu (void)
+{
+  struct btrace_cpu cpu;
+  unsigned int eax, ebx, ecx, edx;
+  int ok;
+
+  memset (&cpu, 0, sizeof (cpu));
+
+  ok = x86_cpuid (0, &eax, &ebx, &ecx, &edx);
+  if (ok != 0)
+    {
+      if (ebx == signature_INTEL_ebx && ecx == signature_INTEL_ecx
+	  && edx == signature_INTEL_edx)
+	{
+	  unsigned int cpuid, ignore;
+
+	  ok = x86_cpuid (1, &cpuid, &ignore, &ignore, &ignore);
+	  if (ok != 0)
+	    {
+	      cpu.vendor = CV_INTEL;
+
+	      cpu.family = (cpuid >> 8) & 0xf;
+	      cpu.model = (cpuid >> 4) & 0xf;
+
+	      if (cpu.family == 0x6)
+		cpu.model += (cpuid >> 12) & 0xf0;
+	    }
+	}
+    }
+
+  return cpu;
+}
+
+/* See x86-btrace.h.  */
+
 const char *
 btrace_format_string (enum btrace_format format)
 {
diff --git a/gdb/nat/x86-btrace.h b/gdb/nat/x86-btrace.h
index 48d6284..05d41fd 100644
--- a/gdb/nat/x86-btrace.h
+++ b/gdb/nat/x86-btrace.h
@@ -62,6 +62,34 @@ enum btrace_format
   BTRACE_FORMAT_BTS
 };
 
+/* An enumeration of cpu vendors.  */
+
+enum btrace_cpu_vendor
+{
+  /* We do not know this vendor.  */
+  CV_UNKNOWN,
+
+  /* Intel.  */
+  CV_INTEL
+};
+
+/* A cpu identifier.  */
+
+struct btrace_cpu
+{
+  /* The processor vendor.  */
+  enum btrace_cpu_vendor vendor;
+
+  /* The cpu family.  */
+  unsigned short family;
+
+  /* The cpu model.  */
+  unsigned char model;
+
+  /* The cpu stepping.  */
+  unsigned char stepping;
+};
+
 /* A BTS configuration.  */
 
 struct btrace_config_bts
@@ -141,6 +169,9 @@ enum btrace_error
   BTRACE_ERR_OVERFLOW
 };
 
+/* Identify the cpu we're running on.  */
+extern struct btrace_cpu btrace_this_cpu (void);
+
 /* Return a string representation of FORMAT.  */
 extern const char *btrace_format_string (enum btrace_format format);
 
-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH v2 01/13] btrace: add struct btrace_data
  2014-11-20 10:47 [PATCH v2 00/13] record btrace: prepare for a new trace format Markus Metzger
                   ` (10 preceding siblings ...)
  2014-11-20 10:47 ` [PATCH v2 06/13] btrace: update btrace_compute_ftrace parameters Markus Metzger
@ 2014-11-20 10:47 ` Markus Metzger
  2015-01-27 11:29   ` Pedro Alves
  2014-11-20 10:47 ` [PATCH v2 13/13] [wip] btrace: support Intel(R) Processor Trace Markus Metzger
  12 siblings, 1 reply; 25+ messages in thread
From: Markus Metzger @ 2014-11-20 10:47 UTC (permalink / raw)
  To: palves; +Cc: gdb-patches

Add a structure to hold the branch trace data and an enum to describe
the format of that data.  So far, only BTS is supported.  Also added
a NONE format to indicate that no branch trace data is available.

This will make it easier to support different branch trace formats in
the future.

2014-11-20  Markus Metzger  <markus.t.metzger@intel.com>

	* Makefile.in (SFILES): Add common/btrace-common.c.
	(COMMON_OBS): Add common/btrace-common.o.
	(btrace-common.o): Add build rules.
	* btrace.c (parse_xml_btrace): Update parameters.
	(parse_xml_btrace_block): Set format field.
	(btrace_add_pc, btrace_fetch): Use struct btrace_data.
	(do_btrace_data_cleanup, make_cleanup_btrace_data): New.
	(btrace_compute_ftrace): Split into this and...
	(btrace_compute_ftrace_bts): ...this.
	(btrace_stitch_trace): Split into this and...
	(btrace_stitch_bts): ...this.
	* btrace.h (parse_xml_btrace): Update parameters.
	(make_cleanup_btrace_data): New.
	* common/btrace-common.c: New.
	* common/btrace-common.h: Include common-defs.h.
	(btrace_block_s): Update comment.
	(btrace_format): New.
	(btrace_format_string): New.
	(btrace_data_bts): New.
	(btrace_data): New.
	(btrace_data_init, btrace_data_fini, btrace_data_empty): New.
	* remote.c (remote_read_btrace): Update parameters.
	* target.c (target_read_btrace): Update parameters.
	* target.h (target_read_btrace): Update parameters.
	(target_ops)<to_read_btrace>: Update parameters.
	* x86-linux-nat.c (x86_linux_read_btrace): Update parameters.
	* target-delegates.c: Regenerate.
	* target-debug (target_debug_print_struct_btrace_data_p): New.

nat/
	* linux-btrace.c (linux_read_btrace): Split into this and...
	(linux_read_bts): ...this.
	* linux-btrace.h (linux_read_btrace): Update parameters.

gdbserver/
	* Makefile.in (SFILES): Add common/btrace-common.c.
	(OBS): Add common/btrace-common.o.
	(btrace-common.o): Add build rules.
	* linux-low: Include btrace-common.h.
	(linux_low_read_btrace): Use struct btrace_data.  Call
	btrace_data_init and btrace_data_fini.
---
 gdb/Makefile.in            |   9 ++-
 gdb/btrace.c               | 154 ++++++++++++++++++++++++++++++++-------------
 gdb/btrace.h               |   6 +-
 gdb/common/btrace-common.c |  82 ++++++++++++++++++++++++
 gdb/common/btrace-common.h |  50 +++++++++++++--
 gdb/gdbserver/Makefile.in  |   8 ++-
 gdb/gdbserver/linux-low.c  |  37 ++++++++---
 gdb/nat/linux-btrace.c     |  36 ++++++++---
 gdb/nat/linux-btrace.h     |   2 +-
 gdb/remote.c               |   4 +-
 gdb/target-debug.h         |   2 +
 gdb/target-delegates.c     |   8 +--
 gdb/target.c               |   2 +-
 gdb/target.h               |   8 +--
 gdb/x86-linux-nat.c        |   2 +-
 15 files changed, 325 insertions(+), 85 deletions(-)
 create mode 100644 gdb/common/btrace-common.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 1da8af6..344ff94 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -852,7 +852,8 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
 	common/ptid.c common/buffer.c gdb-dlfcn.c common/agent.c \
 	common/format.c common/filestuff.c btrace.c record-btrace.c ctf.c \
 	target/waitstatus.c common/print-utils.c common/rsp-low.c \
-	common/errors.c common/common-debug.c common/common-exceptions.c
+	common/errors.c common/common-debug.c common/common-exceptions.c \
+	common/btrace-common.c
 
 LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
 
@@ -1039,7 +1040,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \
 	format.o registry.o btrace.o record-btrace.o waitstatus.o \
 	print-utils.o rsp-low.o errors.o common-debug.o debug.o \
-	common-exceptions.o
+	common-exceptions.o btrace-common.o
 
 TSOBS = inflow.o
 
@@ -2172,6 +2173,10 @@ common-exceptions.o: ${srcdir}/common/common-exceptions.c
 	$(COMPILE) $(srcdir)/common/common-exceptions.c
 	$(POSTCOMPILE)
 
+btrace-common.o: ${srcdir}/common/btrace-common.c
+	$(COMPILE) $(srcdir)/common/btrace-common.c
+	$(POSTCOMPILE)
+
 #
 # gdb/target/ dependencies
 #
diff --git a/gdb/btrace.c b/gdb/btrace.c
index b8b5139..470a676 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -585,25 +585,22 @@ ftrace_update_insns (struct btrace_function *bfun, CORE_ADDR pc)
     ftrace_debug (bfun, "update insn");
 }
 
-/* Compute the function branch trace from a block branch trace BTRACE for
-   a thread given by BTINFO.  */
+/* Compute the function branch trace from BTS trace.  */
 
 static void
-btrace_compute_ftrace (struct btrace_thread_info *btinfo,
-		       VEC (btrace_block_s) *btrace)
+btrace_compute_ftrace_bts (struct btrace_thread_info *btinfo,
+			   const struct btrace_data_bts *btrace)
 {
   struct btrace_function *begin, *end;
   struct gdbarch *gdbarch;
   unsigned int blk;
   int level;
 
-  DEBUG ("compute ftrace");
-
   gdbarch = target_gdbarch ();
   begin = btinfo->begin;
   end = btinfo->end;
   level = begin != NULL ? -btinfo->level : INT_MAX;
-  blk = VEC_length (btrace_block_s, btrace);
+  blk = VEC_length (btrace_block_s, btrace->blocks);
 
   while (blk != 0)
     {
@@ -612,7 +609,7 @@ btrace_compute_ftrace (struct btrace_thread_info *btinfo,
 
       blk -= 1;
 
-      block = VEC_index (btrace_block_s, btrace, blk);
+      block = VEC_index (btrace_block_s, btrace->blocks, blk);
       pc = block->begin;
 
       for (;;)
@@ -675,12 +672,34 @@ btrace_compute_ftrace (struct btrace_thread_info *btinfo,
   btinfo->level = -level;
 }
 
+/* Compute the function branch trace from a block branch trace BTRACE for
+   a thread given by BTINFO.  */
+
+static void
+btrace_compute_ftrace (struct btrace_thread_info *btinfo,
+		       struct btrace_data *btrace)
+{
+  DEBUG ("compute ftrace");
+
+  switch (btrace->format)
+    {
+    case BTRACE_FORMAT_NONE:
+      return;
+
+    case BTRACE_FORMAT_BTS:
+      btrace_compute_ftrace_bts (btinfo, &btrace->variant.bts);
+      return;
+    }
+
+  internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
+}
+
 /* Add an entry for the current PC.  */
 
 static void
 btrace_add_pc (struct thread_info *tp)
 {
-  VEC (btrace_block_s) *btrace;
+  struct btrace_data btrace;
   struct btrace_block *block;
   struct regcache *regcache;
   struct cleanup *cleanup;
@@ -689,14 +708,17 @@ btrace_add_pc (struct thread_info *tp)
   regcache = get_thread_regcache (tp->ptid);
   pc = regcache_read_pc (regcache);
 
-  btrace = NULL;
-  cleanup = make_cleanup (VEC_cleanup (btrace_block_s), &btrace);
+  btrace_data_init (&btrace);
+  btrace.format = BTRACE_FORMAT_BTS;
+  btrace.variant.bts.blocks = NULL;
 
-  block = VEC_safe_push (btrace_block_s, btrace, NULL);
+  cleanup = make_cleanup_btrace_data (&btrace);
+
+  block = VEC_safe_push (btrace_block_s, btrace.variant.bts.blocks, NULL);
   block->begin = pc;
   block->end = pc;
 
-  btrace_compute_ftrace (&tp->btrace, btrace);
+  btrace_compute_ftrace (&tp->btrace, &btrace);
 
   do_cleanups (cleanup);
 }
@@ -760,31 +782,24 @@ btrace_teardown (struct thread_info *tp)
   btrace_clear (tp);
 }
 
-/* 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 modify BTRACE as well as the existing trace in BTINFO.
-   Return 0 on success, -1 otherwise.  */
+/* Stitch branch trace in BTS format.  */
 
 static int
-btrace_stitch_trace (VEC (btrace_block_s) **btrace,
-		     const struct btrace_thread_info *btinfo)
+btrace_stitch_bts (struct btrace_data_bts *btrace,
+		   const struct btrace_thread_info *btinfo)
 {
   struct btrace_function *last_bfun;
   struct btrace_insn *last_insn;
   btrace_block_s *first_new_block;
 
-  /* If we don't have trace, there's nothing to do.  */
-  if (VEC_empty (btrace_block_s, *btrace))
-    return 0;
-
   last_bfun = btinfo->end;
   gdb_assert (last_bfun != NULL);
 
   /* 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.  */
-  first_new_block = VEC_last (btrace_block_s, *btrace);
+  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);
 
   /* If the current PC at the end of the block is the same as in our current
@@ -796,9 +811,9 @@ btrace_stitch_trace (VEC (btrace_block_s) **btrace,
      In the second case, the delta trace vector should contain exactly one
      entry for the partial block containing the current PC.  Remove it.  */
   if (first_new_block->end == last_insn->pc
-      && VEC_length (btrace_block_s, *btrace) == 1)
+      && VEC_length (btrace_block_s, btrace->blocks) == 1)
     {
-      VEC_pop (btrace_block_s, *btrace);
+      VEC_pop (btrace_block_s, btrace->blocks);
       return 0;
     }
 
@@ -834,6 +849,32 @@ btrace_stitch_trace (VEC (btrace_block_s) **btrace,
   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.
+   Return 0 on success, -1 otherwise.  */
+
+static int
+btrace_stitch_trace (struct btrace_data *btrace,
+		     const struct btrace_thread_info *btinfo)
+{
+  /* If we don't have trace, there's nothing to do.  */
+  if (btrace_data_empty (btrace))
+    return 0;
+
+  switch (btrace->format)
+    {
+    case BTRACE_FORMAT_NONE:
+      return 0;
+
+    case BTRACE_FORMAT_BTS:
+      return btrace_stitch_bts (&btrace->variant.bts, btinfo);
+    }
+
+  internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
+}
+
 /* Clear the branch trace histories in BTINFO.  */
 
 static void
@@ -855,13 +896,12 @@ btrace_fetch (struct thread_info *tp)
 {
   struct btrace_thread_info *btinfo;
   struct btrace_target_info *tinfo;
-  VEC (btrace_block_s) *btrace;
+  struct btrace_data btrace;
   struct cleanup *cleanup;
   int errcode;
 
   DEBUG ("fetch thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
 
-  btrace = NULL;
   btinfo = &tp->btrace;
   tinfo = btinfo->target;
   if (tinfo == NULL)
@@ -873,7 +913,8 @@ btrace_fetch (struct thread_info *tp)
   if (btinfo->replay != NULL)
     return;
 
-  cleanup = make_cleanup (VEC_cleanup (btrace_block_s), &btrace);
+  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)
@@ -890,7 +931,7 @@ btrace_fetch (struct thread_info *tp)
 	  errcode = target_read_btrace (&btrace, tinfo, BTRACE_READ_NEW);
 
 	  /* If we got any new trace, discard what we have.  */
-	  if (errcode == 0 && !VEC_empty (btrace_block_s, btrace))
+	  if (errcode == 0 && !btrace_data_empty (&btrace))
 	    btrace_clear (tp);
 	}
 
@@ -909,10 +950,10 @@ btrace_fetch (struct thread_info *tp)
     error (_("Failed to read branch trace."));
 
   /* Compute the trace, provided we have any.  */
-  if (!VEC_empty (btrace_block_s, btrace))
+  if (!btrace_data_empty (&btrace))
     {
       btrace_clear_history (btinfo);
-      btrace_compute_ftrace (btinfo, btrace);
+      btrace_compute_ftrace (btinfo, &btrace);
     }
 
   do_cleanups (cleanup);
@@ -984,16 +1025,30 @@ parse_xml_btrace_block (struct gdb_xml_parser *parser,
 			const struct gdb_xml_element *element,
 			void *user_data, VEC (gdb_xml_value_s) *attributes)
 {
-  VEC (btrace_block_s) **btrace;
+  struct btrace_data *btrace;
   struct btrace_block *block;
   ULONGEST *begin, *end;
 
   btrace = user_data;
-  block = VEC_safe_push (btrace_block_s, *btrace, NULL);
+
+  switch (btrace->format)
+    {
+    case BTRACE_FORMAT_BTS:
+      break;
+
+    case BTRACE_FORMAT_NONE:
+      btrace->format = BTRACE_FORMAT_BTS;
+      btrace->variant.bts.blocks = NULL;
+      break;
+
+    default:
+      gdb_xml_error (parser, _("Btrace format error."));
+    }
 
   begin = xml_find_attribute (attributes, "begin")->value;
   end = xml_find_attribute (attributes, "end")->value;
 
+  block = VEC_safe_push (btrace_block_s, btrace->variant.bts.blocks, NULL);
   block->begin = *begin;
   block->end = *end;
 }
@@ -1025,18 +1080,19 @@ static const struct gdb_xml_element btrace_elements[] = {
 
 /* See btrace.h.  */
 
-VEC (btrace_block_s) *
-parse_xml_btrace (const char *buffer)
+void
+parse_xml_btrace (struct btrace_data *btrace, const char *buffer)
 {
-  VEC (btrace_block_s) *btrace = NULL;
   struct cleanup *cleanup;
   int errcode;
 
 #if defined (HAVE_LIBEXPAT)
 
-  cleanup = make_cleanup (VEC_cleanup (btrace_block_s), &btrace);
+  btrace->format = BTRACE_FORMAT_NONE;
+
+  cleanup = make_cleanup_btrace_data (btrace);
   errcode = gdb_xml_parse_quick (_("btrace"), "btrace.dtd", btrace_elements,
-				 buffer, &btrace);
+				 buffer, btrace);
   if (errcode != 0)
     error (_("Error parsing branch trace."));
 
@@ -1048,8 +1104,6 @@ parse_xml_btrace (const char *buffer)
   error (_("Cannot process branch trace.  XML parsing is not supported."));
 
 #endif  /* !defined (HAVE_LIBEXPAT) */
-
-  return btrace;
 }
 
 /* See btrace.h.  */
@@ -1526,3 +1580,19 @@ btrace_is_empty (struct thread_info *tp)
 
   return btrace_insn_cmp (&begin, &end) == 0;
 }
+
+/* Forward the cleanup request.  */
+
+static void
+do_btrace_data_cleanup (void *arg)
+{
+  btrace_data_fini (arg);
+}
+
+/* See btrace.h.  */
+
+struct cleanup *
+make_cleanup_btrace_data (struct btrace_data *data)
+{
+  return make_cleanup (do_btrace_data_cleanup, data);
+}
diff --git a/gdb/btrace.h b/gdb/btrace.h
index f83a80f..3954581 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -235,8 +235,8 @@ extern void btrace_clear (struct thread_info *);
 /* Clear the branch trace for all threads when an object file goes away.  */
 extern void btrace_free_objfile (struct objfile *);
 
-/* Parse a branch trace xml document into a block vector.  */
-extern VEC (btrace_block_s) *parse_xml_btrace (const char*);
+/* Parse a branch trace xml document XML into DATA.  */
+extern void parse_xml_btrace (struct btrace_data *data, const char *xml);
 
 /* Dereference a branch trace instruction iterator.  Return a pointer to the
    instruction the iterator points to.  */
@@ -339,5 +339,7 @@ extern int btrace_is_replaying (struct thread_info *tp);
 /* Return non-zero if the branch trace for TP is empty; zero otherwise.  */
 extern int btrace_is_empty (struct thread_info *tp);
 
+/* Create a cleanup for DATA.  */
+extern struct cleanup *make_cleanup_btrace_data (struct btrace_data *data);
 
 #endif /* BTRACE_H */
diff --git a/gdb/common/btrace-common.c b/gdb/common/btrace-common.c
new file mode 100644
index 0000000..869d0cb
--- /dev/null
+++ b/gdb/common/btrace-common.c
@@ -0,0 +1,82 @@
+/* Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <markus.t.metzger@intel.com>
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "btrace-common.h"
+
+
+/* See btrace-common.h.  */
+
+const char *
+btrace_format_string (enum btrace_format format)
+{
+  switch (format)
+    {
+    case BTRACE_FORMAT_NONE:
+      return _("No or unknown format");
+
+    case BTRACE_FORMAT_BTS:
+      return _("Intel(R) Branch Trace Store");
+    }
+
+  internal_error (__FILE__, __LINE__, _("Unknown branch trace format"));
+}
+
+/* See btrace-common.h.  */
+
+void
+btrace_data_init (struct btrace_data *data)
+{
+  data->format = BTRACE_FORMAT_NONE;
+}
+
+/* See btrace-common.h.  */
+
+void
+btrace_data_fini (struct btrace_data *data)
+{
+  switch (data->format)
+    {
+    case BTRACE_FORMAT_NONE:
+      /* Nothing to do.  */
+      return;
+
+    case BTRACE_FORMAT_BTS:
+      VEC_free (btrace_block_s, data->variant.bts.blocks);
+      return;
+    }
+
+  internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
+}
+
+/* See btrace-common.h.  */
+
+int
+btrace_data_empty (struct btrace_data *data)
+{
+  switch (data->format)
+    {
+    case BTRACE_FORMAT_NONE:
+      return 1;
+
+    case BTRACE_FORMAT_BTS:
+      return VEC_empty (btrace_block_s, data->variant.bts.blocks);
+    }
+
+  internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
+}
diff --git a/gdb/common/btrace-common.h b/gdb/common/btrace-common.h
index 339e684..27e7a95 100644
--- a/gdb/common/btrace-common.h
+++ b/gdb/common/btrace-common.h
@@ -26,6 +26,7 @@
    inferior.  For presentation purposes, the branch trace is represented as a
    list of sequential control-flow blocks, one such list per thread.  */
 
+#include "common-defs.h"
 #include "vec.h"
 
 /* A branch trace block.
@@ -45,13 +46,42 @@ struct btrace_block
   CORE_ADDR end;
 };
 
-/* Branch trace is represented as a vector of branch trace blocks starting with
-   the most recent block.  */
-typedef struct btrace_block btrace_block_s;
-
 /* Define functions operating on a vector of branch trace blocks.  */
+typedef struct btrace_block btrace_block_s;
 DEF_VEC_O (btrace_block_s);
 
+/* Enumeration of btrace formats.  */
+
+enum btrace_format
+{
+  /* No branch trace format.  */
+  BTRACE_FORMAT_NONE,
+
+  /* Branch trace is in Branch Trace Store format.
+     Actually, the format is a sequence of blocks derived from BTS.  */
+  BTRACE_FORMAT_BTS
+};
+
+/* Branch trace in BTS format.  */
+struct btrace_data_bts
+{
+  /* Branch trace is represented as a vector of branch trace blocks starting
+     with the most recent block.  */
+  VEC (btrace_block_s) *blocks;
+};
+
+/* The branch trace data.  */
+struct btrace_data
+{
+  enum btrace_format format;
+
+  union
+  {
+    /* Format == BTRACE_FORMAT_BTS.  */
+    struct btrace_data_bts bts;
+  } variant;
+};
+
 /* Target specific branch trace information.  */
 struct btrace_target_info;
 
@@ -87,4 +117,16 @@ enum btrace_error
   BTRACE_ERR_OVERFLOW
 };
 
+/* Return a string representation of FORMAT.  */
+extern const char *btrace_format_string (enum btrace_format format);
+
+/* Initialize DATA.  */
+extern void btrace_data_init (struct btrace_data *data);
+
+/* Cleanup DATA.  */
+extern void btrace_data_fini (struct btrace_data *data);
+
+/* Return non-zero if DATA is empty; zero otherwise.  */
+extern int btrace_data_empty (struct btrace_data *data);
+
 #endif /* BTRACE_COMMON_H */
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index 8b0318a..98897d9 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -172,7 +172,8 @@ SFILES=	$(srcdir)/gdbreplay.c $(srcdir)/inferiors.c $(srcdir)/dll.c \
 	$(srcdir)/nat/mips-linux-watch.c $(srcdir)/common/print-utils.c \
 	$(srcdir)/common/rsp-low.c $(srcdir)/common/errors.c \
 	$(srcdir)/common/common-debug.c $(srcdir)/common/cleanups.c \
-	$(srcdir)/common/common-exceptions.c $(srcdir)/symbol.c
+	$(srcdir)/common/common-exceptions.c $(srcdir)/symbol.c \
+	$(srcdir)/common/btrace-common.c
 
 DEPFILES = @GDBSERVER_DEPFILES@
 
@@ -186,7 +187,7 @@ OBS = agent.o ax.o inferiors.o regcache.o remote-utils.o server.o signals.o \
       mem-break.o hostio.o event-loop.o tracepoint.o xml-utils.o \
       common-utils.o ptid.o buffer.o format.o filestuff.o dll.o notif.o \
       tdesc.o print-utils.o rsp-low.o errors.o common-debug.o cleanups.o \
-      common-exceptions.o symbol.o \
+      common-exceptions.o symbol.o btrace-common.o \
       $(XML_BUILTIN) $(DEPFILES) $(LIBOBJS)
 GDBREPLAY_OBS = gdbreplay.o version.o
 GDBSERVER_LIBS = @GDBSERVER_LIBS@
@@ -581,6 +582,9 @@ linux-waitpid.o: ../nat/linux-waitpid.c
 mips-linux-watch.o: ../nat/mips-linux-watch.c
 	$(COMPILE) $<
 	$(POSTCOMPILE)
+btrace-common.o: ../common/btrace-common.c
+	$(COMPILE) $<
+	$(POSTCOMPILE)
 
 aarch64.c : $(srcdir)/../regformats/aarch64.dat $(regdat_sh)
 	$(SHELL) $(regdat_sh) $(srcdir)/../regformats/aarch64.dat aarch64.c
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 01f11b7..dcaa829 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -102,6 +102,7 @@
 
 #ifdef HAVE_LINUX_BTRACE
 # include "nat/linux-btrace.h"
+# include "btrace-common.h"
 #endif
 
 #ifndef HAVE_ELF32_AUXV_T
@@ -5968,12 +5969,13 @@ static int
 linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
 		       int type)
 {
-  VEC (btrace_block_s) *btrace;
+  struct btrace_data btrace;
   struct btrace_block *block;
   enum btrace_error err;
   int i;
 
-  btrace = NULL;
+  btrace_data_init (&btrace);
+
   err = linux_read_btrace (&btrace, tinfo, type);
   if (err != BTRACE_ERR_NONE)
     {
@@ -5982,20 +5984,37 @@ linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
       else
 	buffer_grow_str0 (buffer, "E.Generic Error.");
 
+      btrace_data_fini (&btrace);
       return -1;
     }
 
-  buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
-  buffer_grow_str (buffer, "<btrace version=\"1.0\">\n");
+  switch (btrace.format)
+    {
+    case BTRACE_FORMAT_NONE:
+      buffer_grow_str0 (buffer, "E.No Trace.");
+      break;
+
+    case BTRACE_FORMAT_BTS:
+      buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
+      buffer_grow_str (buffer, "<btrace version=\"1.0\">\n");
 
-  for (i = 0; VEC_iterate (btrace_block_s, btrace, i, block); i++)
-    buffer_xml_printf (buffer, "<block begin=\"0x%s\" end=\"0x%s\"/>\n",
-		       paddress (block->begin), paddress (block->end));
+      for (i = 0;
+	   VEC_iterate (btrace_block_s, btrace.variant.bts.blocks, i, block);
+	   i++)
+	buffer_xml_printf (buffer, "<block begin=\"0x%s\" end=\"0x%s\"/>\n",
+			   paddress (block->begin), paddress (block->end));
 
-  buffer_grow_str0 (buffer, "</btrace>\n");
+      buffer_grow_str0 (buffer, "</btrace>\n");
+      break;
+
+    default:
+      buffer_grow_str0 (buffer, "E.Unknown Trace Format.");
 
-  VEC_free (btrace_block_s, btrace);
+      btrace_data_fini (&btrace);
+      return -1;
+    }
 
+  btrace_data_fini (&btrace);
   return 0;
 }
 #endif /* HAVE_LINUX_BTRACE */
diff --git a/gdb/nat/linux-btrace.c b/gdb/nat/linux-btrace.c
index 37c85c0..916f6b3 100644
--- a/gdb/nat/linux-btrace.c
+++ b/gdb/nat/linux-btrace.c
@@ -495,12 +495,13 @@ linux_btrace_has_changed (struct btrace_target_info *tinfo)
   return header->data_head != tinfo->data_head;
 }
 
-/* See linux-btrace.h.  */
+/* Read branch trace data in BTS format for the thread given by TINFO into
+   BTRACE using the TYPE reading method.  */
 
-enum btrace_error
-linux_read_btrace (VEC (btrace_block_s) **btrace,
-		   struct btrace_target_info *tinfo,
-		   enum btrace_read_type type)
+static enum btrace_error
+linux_read_bts (struct btrace_data_bts *btrace,
+		struct btrace_target_info *tinfo,
+		enum btrace_read_type type)
 {
   volatile struct perf_event_mmap_page *header;
   const uint8_t *begin, *end, *start;
@@ -522,7 +523,7 @@ linux_read_btrace (VEC (btrace_block_s) **btrace,
       data_head = header->data_head;
 
       /* Delete any leftover trace from the previous iteration.  */
-      VEC_free (btrace_block_s, *btrace);
+      VEC_free (btrace_block_s, btrace->blocks);
 
       if (type == BTRACE_READ_DELTA)
 	{
@@ -559,7 +560,7 @@ linux_read_btrace (VEC (btrace_block_s) **btrace,
       else
 	end = perf_event_buffer_end (tinfo);
 
-      *btrace = perf_event_read_bts (tinfo, begin, end, start, size);
+      btrace->blocks = perf_event_read_bts (tinfo, begin, end, start, size);
 
       /* The stopping thread notifies its ptracer before it is scheduled out.
 	 On multi-core systems, the debugger might therefore run while the
@@ -575,12 +576,27 @@ linux_read_btrace (VEC (btrace_block_s) **btrace,
   /* Prune the incomplete last block (i.e. the first one of inferior execution)
      if we're not doing a delta read.  There is no way of filling in its zeroed
      BEGIN element.  */
-  if (!VEC_empty (btrace_block_s, *btrace) && type != BTRACE_READ_DELTA)
-    VEC_pop (btrace_block_s, *btrace);
+  if (!VEC_empty (btrace_block_s, btrace->blocks)
+      && type != BTRACE_READ_DELTA)
+    VEC_pop (btrace_block_s, btrace->blocks);
 
   return BTRACE_ERR_NONE;
 }
 
+/* See linux-btrace.h.  */
+
+enum btrace_error
+linux_read_btrace (struct btrace_data *btrace,
+		   struct btrace_target_info *tinfo,
+		   enum btrace_read_type type)
+{
+  /* We read btrace in BTS format.  */
+  btrace->format = BTRACE_FORMAT_BTS;
+  btrace->variant.bts.blocks = NULL;
+
+  return linux_read_bts (&btrace->variant.bts, tinfo, type);
+}
+
 #else /* !HAVE_LINUX_PERF_EVENT_H */
 
 /* See linux-btrace.h.  */
@@ -610,7 +626,7 @@ linux_disable_btrace (struct btrace_target_info *tinfo)
 /* See linux-btrace.h.  */
 
 enum btrace_error
-linux_read_btrace (VEC (btrace_block_s) **btrace,
+linux_read_btrace (struct btrace_data *btrace,
 		   struct btrace_target_info *tinfo,
 		   enum btrace_read_type type)
 {
diff --git a/gdb/nat/linux-btrace.h b/gdb/nat/linux-btrace.h
index e4b2604..2a08df7 100644
--- a/gdb/nat/linux-btrace.h
+++ b/gdb/nat/linux-btrace.h
@@ -70,7 +70,7 @@ extern struct btrace_target_info *linux_enable_btrace (ptid_t ptid);
 extern enum btrace_error linux_disable_btrace (struct btrace_target_info *ti);
 
 /* See to_read_btrace in target.h.  */
-extern enum btrace_error linux_read_btrace (VEC (btrace_block_s) **btrace,
+extern enum btrace_error linux_read_btrace (struct btrace_data *btrace,
 					    struct btrace_target_info *btinfo,
 					    enum btrace_read_type type);
 
diff --git a/gdb/remote.c b/gdb/remote.c
index 4b9b099..249deaa 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -11419,7 +11419,7 @@ remote_teardown_btrace (struct target_ops *self,
 
 static enum btrace_error
 remote_read_btrace (struct target_ops *self,
-		    VEC (btrace_block_s) **btrace,
+		    struct btrace_data *btrace,
 		    struct btrace_target_info *tinfo,
 		    enum btrace_read_type type)
 {
@@ -11459,7 +11459,7 @@ remote_read_btrace (struct target_ops *self,
     return BTRACE_ERR_UNKNOWN;
 
   cleanup = make_cleanup (xfree, xml);
-  *btrace = parse_xml_btrace (xml);
+  parse_xml_btrace (btrace, xml);
   do_cleanups (cleanup);
 
   return BTRACE_ERR_NONE;
diff --git a/gdb/target-debug.h b/gdb/target-debug.h
index 3dd705c..60da35c 100644
--- a/gdb/target-debug.h
+++ b/gdb/target-debug.h
@@ -144,6 +144,8 @@
   target_debug_do_print (host_address_to_string (X))
 #define target_debug_print_const_struct_frame_unwind_p(X)	\
   target_debug_do_print (host_address_to_string (X))
+#define target_debug_print_struct_btrace_data_p(X)	\
+  target_debug_do_print (host_address_to_string (X))
 
 static void
 target_debug_print_struct_target_waitstatus_p (struct target_waitstatus *status)
diff --git a/gdb/target-delegates.c b/gdb/target-delegates.c
index 9beb5ff..00956ba 100644
--- a/gdb/target-delegates.c
+++ b/gdb/target-delegates.c
@@ -3191,20 +3191,20 @@ debug_teardown_btrace (struct target_ops *self, struct btrace_target_info *arg1)
 }
 
 static enum btrace_error
-delegate_read_btrace (struct target_ops *self, VEC (btrace_block_s) **arg1, struct btrace_target_info *arg2, enum btrace_read_type arg3)
+delegate_read_btrace (struct target_ops *self, struct btrace_data *arg1, struct btrace_target_info *arg2, enum btrace_read_type arg3)
 {
   self = self->beneath;
   return self->to_read_btrace (self, arg1, arg2, arg3);
 }
 
 static enum btrace_error
-tdefault_read_btrace (struct target_ops *self, VEC (btrace_block_s) **arg1, struct btrace_target_info *arg2, enum btrace_read_type arg3)
+tdefault_read_btrace (struct target_ops *self, struct btrace_data *arg1, struct btrace_target_info *arg2, enum btrace_read_type arg3)
 {
   tcomplain ();
 }
 
 static enum btrace_error
-debug_read_btrace (struct target_ops *self, VEC (btrace_block_s) **arg1, struct btrace_target_info *arg2, enum btrace_read_type arg3)
+debug_read_btrace (struct target_ops *self, struct btrace_data *arg1, struct btrace_target_info *arg2, enum btrace_read_type arg3)
 {
   enum btrace_error result;
   fprintf_unfiltered (gdb_stdlog, "-> %s->to_read_btrace (...)\n", debug_target.to_shortname);
@@ -3212,7 +3212,7 @@ debug_read_btrace (struct target_ops *self, VEC (btrace_block_s) **arg1, struct
   fprintf_unfiltered (gdb_stdlog, "<- %s->to_read_btrace (", debug_target.to_shortname);
   target_debug_print_struct_target_ops_p (&debug_target);
   fputs_unfiltered (", ", gdb_stdlog);
-  target_debug_print_VEC__btrace_block_s__pp (arg1);
+  target_debug_print_struct_btrace_data_p (arg1);
   fputs_unfiltered (", ", gdb_stdlog);
   target_debug_print_struct_btrace_target_info_p (arg2);
   fputs_unfiltered (", ", gdb_stdlog);
diff --git a/gdb/target.c b/gdb/target.c
index ab5f2b9..891183f 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -3380,7 +3380,7 @@ target_teardown_btrace (struct btrace_target_info *btinfo)
 /* See target.h.  */
 
 enum btrace_error
-target_read_btrace (VEC (btrace_block_s) **btrace,
+target_read_btrace (struct btrace_data *btrace,
 		    struct btrace_target_info *btinfo,
 		    enum btrace_read_type type)
 {
diff --git a/gdb/target.h b/gdb/target.h
index d363b61..dee587f 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -1022,11 +1022,9 @@ struct target_ops
       TARGET_DEFAULT_NORETURN (tcomplain ());
 
     /* Read branch trace data for the thread indicated by BTINFO into DATA.
-       DATA is cleared before new trace is added.
-       The branch trace will start with the most recent block and continue
-       towards older blocks.  */
+       DATA is cleared before new trace is added.  */
     enum btrace_error (*to_read_btrace) (struct target_ops *self,
-					 VEC (btrace_block_s) **data,
+					 struct btrace_data *data,
 					 struct btrace_target_info *btinfo,
 					 enum btrace_read_type type)
       TARGET_DEFAULT_NORETURN (tcomplain ());
@@ -2226,7 +2224,7 @@ extern void target_disable_btrace (struct btrace_target_info *btinfo);
 extern void target_teardown_btrace (struct btrace_target_info *btinfo);
 
 /* See to_read_btrace in struct target_ops.  */
-extern enum btrace_error target_read_btrace (VEC (btrace_block_s) **,
+extern enum btrace_error target_read_btrace (struct btrace_data *,
 					     struct btrace_target_info *,
 					     enum btrace_read_type);
 
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
index b2141eb..d9ba33e 100644
--- a/gdb/x86-linux-nat.c
+++ b/gdb/x86-linux-nat.c
@@ -470,7 +470,7 @@ x86_linux_teardown_btrace (struct target_ops *self,
 
 static enum btrace_error
 x86_linux_read_btrace (struct target_ops *self,
-		       VEC (btrace_block_s) **data,
+		       struct btrace_data *data,
 		       struct btrace_target_info *btinfo,
 		       enum btrace_read_type type)
 {
-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH v2 12/13] configure: check for libipt
  2014-11-20 10:47 [PATCH v2 00/13] record btrace: prepare for a new trace format Markus Metzger
                   ` (5 preceding siblings ...)
  2014-11-20 10:47 ` [PATCH v2 11/13] record-btrace: indicate gaps Markus Metzger
@ 2014-11-20 10:47 ` Markus Metzger
  2014-11-20 10:47 ` [PATCH v2 03/13] btrace, linux: add perf event buffer abstraction Markus Metzger
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Markus Metzger @ 2014-11-20 10:47 UTC (permalink / raw)
  To: palves; +Cc: gdb-patches

2014-11-20  Markus Metzger  <markus.t.metzger@intel.com>

	* configure.ac: Check for libipt
	* configure: Regenerate.
	* config.in: Regenerate.
	* Makefile.in (LIBIPT): New.
	(CLIBS): Add $LIBIPT.
---
 gdb/Makefile.in  |   5 +-
 gdb/config.in    |   3 +
 gdb/configure    | 498 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/configure.ac |  20 +++
 4 files changed, 525 insertions(+), 1 deletion(-)

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index fc9c695..4bc8b9c 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -163,6 +163,9 @@ LIBLZMA = @LIBLZMA@
 # available.
 LIBBABELTRACE = @LIBBABELTRACE@
 
+# Where is libipt?  This will be empty if libipt was not available.
+LIBIPT = @LIBIPT@
+
 WARN_CFLAGS = @WARN_CFLAGS@
 WERROR_CFLAGS = @WERROR_CFLAGS@
 GDB_WARN_CFLAGS = $(WARN_CFLAGS)
@@ -557,7 +560,7 @@ INTERNAL_LDFLAGS = $(CFLAGS) $(GLOBAL_CFLAGS) $(MH_LDFLAGS) $(LDFLAGS) $(CONFIG_
 CLIBS = $(SIM) $(READLINE) $(OPCODES) $(BFD) $(INTL) $(LIBIBERTY) $(LIBDECNUMBER) \
 	$(XM_CLIBS) $(NAT_CLIBS) $(GDBTKLIBS) \
 	@LIBS@ @GUILE_LIBS@ @PYTHON_LIBS@ \
-	$(LIBEXPAT) $(LIBLZMA) $(LIBBABELTRACE) \
+	$(LIBEXPAT) $(LIBLZMA) $(LIBBABELTRACE) $(LIBIPT) \
 	$(LIBIBERTY) $(WIN32LIBS) $(LIBGNU)
 CDEPS = $(XM_CDEPS) $(NAT_CDEPS) $(SIM) $(BFD) $(READLINE_DEPS) \
 	$(OPCODES) $(INTL_DEPS) $(LIBIBERTY) $(CONFIG_DEPS) $(LIBGNU)
diff --git a/gdb/config.in b/gdb/config.in
index f1593bd..d53d973 100644
--- a/gdb/config.in
+++ b/gdb/config.in
@@ -189,6 +189,9 @@
 /* Define to 1 if you have the `libiconvlist' function. */
 #undef HAVE_LIBICONVLIST
 
+/* Define if you have the ipt library. */
+#undef HAVE_LIBIPT
+
 /* Define if you have the lzma library. */
 #undef HAVE_LIBLZMA
 
diff --git a/gdb/configure b/gdb/configure
index 1d6d88b..a534cb7 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -658,6 +658,9 @@ TARGET_SYSTEM_ROOT
 CONFIG_LDFLAGS
 RDYNAMIC
 ALLOCA
+LTLIBIPT
+LIBIPT
+HAVE_LIBIPT
 HAVE_GUILE_FALSE
 HAVE_GUILE_TRUE
 GUILE_LIBS
@@ -824,6 +827,8 @@ with_libexpat_prefix
 with_python
 with_guile
 enable_libmcheck
+with_intel_pt
+with_libipt_prefix
 with_included_regex
 with_sysroot
 with_system_gdbinit
@@ -1542,6 +1547,9 @@ Optional Packages:
                           (auto/yes/no/<python-program>)
   --with-guile[=GUILE]    include guile support
                           (auto/yes/no/<guile-version>/<pkg-config-program>)
+  --with-intel-pt         include Intel Processor Trace support (auto/yes/no)
+  --with-libipt-prefix[=DIR]  search for libipt in DIR/include and DIR/lib
+  --without-libipt-prefix     don't search for libipt in includedir and libdir
   --without-included-regex
                           don't use included regex; this is the default on
                           systems with version 2 of the GNU C library (use
@@ -9349,6 +9357,496 @@ if test "$ENABLE_LIBMCHECK" = "yes" \
 $as_echo "$as_me: WARNING: --enable-libmcheck may lead to spurious crashes if threads are used in python" >&2;}
 fi
 
+
+# Check whether --with-intel_pt was given.
+if test "${with_intel_pt+set}" = set; then :
+  withval=$with_intel_pt;
+else
+  with_intel_pt=auto
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use intel pt" >&5
+$as_echo_n "checking whether to use intel pt... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_intel_pt" >&5
+$as_echo "$with_intel_pt" >&6; }
+
+if test "${with_intel_pt}" = no; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Intel(R) Processor Trace support disabled; some features may be unavailable." >&5
+$as_echo "$as_me: WARNING: Intel(R) Processor Trace support disabled; some features may be unavailable." >&2;}
+  HAVE_LIBIPT=no
+else
+
+
+
+
+
+
+
+
+    use_additional=yes
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+
+    eval additional_includedir=\"$includedir\"
+    eval additional_libdir=\"$libdir\"
+
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+
+# Check whether --with-libipt-prefix was given.
+if test "${with_libipt_prefix+set}" = set; then :
+  withval=$with_libipt_prefix;
+    if test "X$withval" = "Xno"; then
+      use_additional=no
+    else
+      if test "X$withval" = "X"; then
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+
+          eval additional_includedir=\"$includedir\"
+          eval additional_libdir=\"$libdir\"
+
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+      else
+        additional_includedir="$withval/include"
+        additional_libdir="$withval/lib"
+      fi
+    fi
+
+fi
+
+      LIBIPT=
+  LTLIBIPT=
+  INCIPT=
+  rpathdirs=
+  ltrpathdirs=
+  names_already_handled=
+  names_next_round='ipt '
+  while test -n "$names_next_round"; do
+    names_this_round="$names_next_round"
+    names_next_round=
+    for name in $names_this_round; do
+      already_handled=
+      for n in $names_already_handled; do
+        if test "$n" = "$name"; then
+          already_handled=yes
+          break
+        fi
+      done
+      if test -z "$already_handled"; then
+        names_already_handled="$names_already_handled $name"
+                        uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+        eval value=\"\$HAVE_LIB$uppername\"
+        if test -n "$value"; then
+          if test "$value" = yes; then
+            eval value=\"\$LIB$uppername\"
+            test -z "$value" || LIBIPT="${LIBIPT}${LIBIPT:+ }$value"
+            eval value=\"\$LTLIB$uppername\"
+            test -z "$value" || LTLIBIPT="${LTLIBIPT}${LTLIBIPT:+ }$value"
+          else
+                                    :
+          fi
+        else
+                              found_dir=
+          found_la=
+          found_so=
+          found_a=
+          if test $use_additional = yes; then
+            if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext"; then
+              found_dir="$additional_libdir"
+              found_so="$additional_libdir/lib$name.$shlibext"
+              if test -f "$additional_libdir/lib$name.la"; then
+                found_la="$additional_libdir/lib$name.la"
+              fi
+            else
+              if test -f "$additional_libdir/lib$name.$libext"; then
+                found_dir="$additional_libdir"
+                found_a="$additional_libdir/lib$name.$libext"
+                if test -f "$additional_libdir/lib$name.la"; then
+                  found_la="$additional_libdir/lib$name.la"
+                fi
+              fi
+            fi
+          fi
+          if test "X$found_dir" = "X"; then
+            for x in $LDFLAGS $LTLIBIPT; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+              case "$x" in
+                -L*)
+                  dir=`echo "X$x" | sed -e 's/^X-L//'`
+                  if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext"; then
+                    found_dir="$dir"
+                    found_so="$dir/lib$name.$shlibext"
+                    if test -f "$dir/lib$name.la"; then
+                      found_la="$dir/lib$name.la"
+                    fi
+                  else
+                    if test -f "$dir/lib$name.$libext"; then
+                      found_dir="$dir"
+                      found_a="$dir/lib$name.$libext"
+                      if test -f "$dir/lib$name.la"; then
+                        found_la="$dir/lib$name.la"
+                      fi
+                    fi
+                  fi
+                  ;;
+              esac
+              if test "X$found_dir" != "X"; then
+                break
+              fi
+            done
+          fi
+          if test "X$found_dir" != "X"; then
+                        LTLIBIPT="${LTLIBIPT}${LTLIBIPT:+ }-L$found_dir -l$name"
+            if test "X$found_so" != "X"; then
+                                                        if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then
+                                LIBIPT="${LIBIPT}${LIBIPT:+ }$found_so"
+              else
+                                                                                haveit=
+                for x in $ltrpathdirs; do
+                  if test "X$x" = "X$found_dir"; then
+                    haveit=yes
+                    break
+                  fi
+                done
+                if test -z "$haveit"; then
+                  ltrpathdirs="$ltrpathdirs $found_dir"
+                fi
+                                if test "$hardcode_direct" = yes; then
+                                                      LIBIPT="${LIBIPT}${LIBIPT:+ }$found_so"
+                else
+                  if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+                                                            LIBIPT="${LIBIPT}${LIBIPT:+ }$found_so"
+                                                            haveit=
+                    for x in $rpathdirs; do
+                      if test "X$x" = "X$found_dir"; then
+                        haveit=yes
+                        break
+                      fi
+                    done
+                    if test -z "$haveit"; then
+                      rpathdirs="$rpathdirs $found_dir"
+                    fi
+                  else
+                                                                                haveit=
+                    for x in $LDFLAGS $LIBIPT; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                      if test "X$x" = "X-L$found_dir"; then
+                        haveit=yes
+                        break
+                      fi
+                    done
+                    if test -z "$haveit"; then
+                      LIBIPT="${LIBIPT}${LIBIPT:+ }-L$found_dir"
+                    fi
+                    if test "$hardcode_minus_L" != no; then
+                                                                                        LIBIPT="${LIBIPT}${LIBIPT:+ }$found_so"
+                    else
+                                                                                                                                                                                LIBIPT="${LIBIPT}${LIBIPT:+ }-l$name"
+                    fi
+                  fi
+                fi
+              fi
+            else
+              if test "X$found_a" != "X"; then
+                                LIBIPT="${LIBIPT}${LIBIPT:+ }$found_a"
+              else
+                                                LIBIPT="${LIBIPT}${LIBIPT:+ }-L$found_dir -l$name"
+              fi
+            fi
+                        additional_includedir=
+            case "$found_dir" in
+              */lib | */lib/)
+                basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'`
+                additional_includedir="$basedir/include"
+                ;;
+            esac
+            if test "X$additional_includedir" != "X"; then
+                                                                                                                if test "X$additional_includedir" != "X/usr/include"; then
+                haveit=
+                if test "X$additional_includedir" = "X/usr/local/include"; then
+                  if test -n "$GCC"; then
+                    case $host_os in
+                      linux*) haveit=yes;;
+                    esac
+                  fi
+                fi
+                if test -z "$haveit"; then
+                  for x in $CPPFLAGS $INCIPT; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                    if test "X$x" = "X-I$additional_includedir"; then
+                      haveit=yes
+                      break
+                    fi
+                  done
+                  if test -z "$haveit"; then
+                    if test -d "$additional_includedir"; then
+                                            INCIPT="${INCIPT}${INCIPT:+ }-I$additional_includedir"
+                    fi
+                  fi
+                fi
+              fi
+            fi
+                        if test -n "$found_la"; then
+                                                        save_libdir="$libdir"
+              case "$found_la" in
+                */* | *\\*) . "$found_la" ;;
+                *) . "./$found_la" ;;
+              esac
+              libdir="$save_libdir"
+                            for dep in $dependency_libs; do
+                case "$dep" in
+                  -L*)
+                    additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+                                                                                                                                                                if test "X$additional_libdir" != "X/usr/lib"; then
+                      haveit=
+                      if test "X$additional_libdir" = "X/usr/local/lib"; then
+                        if test -n "$GCC"; then
+                          case $host_os in
+                            linux*) haveit=yes;;
+                          esac
+                        fi
+                      fi
+                      if test -z "$haveit"; then
+                        haveit=
+                        for x in $LDFLAGS $LIBIPT; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                          if test "X$x" = "X-L$additional_libdir"; then
+                            haveit=yes
+                            break
+                          fi
+                        done
+                        if test -z "$haveit"; then
+                          if test -d "$additional_libdir"; then
+                                                        LIBIPT="${LIBIPT}${LIBIPT:+ }-L$additional_libdir"
+                          fi
+                        fi
+                        haveit=
+                        for x in $LDFLAGS $LTLIBIPT; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                          if test "X$x" = "X-L$additional_libdir"; then
+                            haveit=yes
+                            break
+                          fi
+                        done
+                        if test -z "$haveit"; then
+                          if test -d "$additional_libdir"; then
+                                                        LTLIBIPT="${LTLIBIPT}${LTLIBIPT:+ }-L$additional_libdir"
+                          fi
+                        fi
+                      fi
+                    fi
+                    ;;
+                  -R*)
+                    dir=`echo "X$dep" | sed -e 's/^X-R//'`
+                    if test "$enable_rpath" != no; then
+                                                                  haveit=
+                      for x in $rpathdirs; do
+                        if test "X$x" = "X$dir"; then
+                          haveit=yes
+                          break
+                        fi
+                      done
+                      if test -z "$haveit"; then
+                        rpathdirs="$rpathdirs $dir"
+                      fi
+                                                                  haveit=
+                      for x in $ltrpathdirs; do
+                        if test "X$x" = "X$dir"; then
+                          haveit=yes
+                          break
+                        fi
+                      done
+                      if test -z "$haveit"; then
+                        ltrpathdirs="$ltrpathdirs $dir"
+                      fi
+                    fi
+                    ;;
+                  -l*)
+                                        names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+                    ;;
+                  *.la)
+                                                                                names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+                    ;;
+                  *)
+                                        LIBIPT="${LIBIPT}${LIBIPT:+ }$dep"
+                    LTLIBIPT="${LTLIBIPT}${LTLIBIPT:+ }$dep"
+                    ;;
+                esac
+              done
+            fi
+          else
+                                                            LIBIPT="${LIBIPT}${LIBIPT:+ }-l$name"
+            LTLIBIPT="${LTLIBIPT}${LTLIBIPT:+ }-l$name"
+          fi
+        fi
+      fi
+    done
+  done
+  if test "X$rpathdirs" != "X"; then
+    if test -n "$hardcode_libdir_separator"; then
+                        alldirs=
+      for found_dir in $rpathdirs; do
+        alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+      done
+            acl_save_libdir="$libdir"
+      libdir="$alldirs"
+      eval flag=\"$hardcode_libdir_flag_spec\"
+      libdir="$acl_save_libdir"
+      LIBIPT="${LIBIPT}${LIBIPT:+ }$flag"
+    else
+            for found_dir in $rpathdirs; do
+        acl_save_libdir="$libdir"
+        libdir="$found_dir"
+        eval flag=\"$hardcode_libdir_flag_spec\"
+        libdir="$acl_save_libdir"
+        LIBIPT="${LIBIPT}${LIBIPT:+ }$flag"
+      done
+    fi
+  fi
+  if test "X$ltrpathdirs" != "X"; then
+            for found_dir in $ltrpathdirs; do
+      LTLIBIPT="${LTLIBIPT}${LTLIBIPT:+ }-R$found_dir"
+    done
+  fi
+
+
+        ac_save_CPPFLAGS="$CPPFLAGS"
+
+  for element in $INCIPT; do
+    haveit=
+    for x in $CPPFLAGS; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+      if test "X$x" = "X$element"; then
+        haveit=yes
+        break
+      fi
+    done
+    if test -z "$haveit"; then
+      CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
+    fi
+  done
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libipt" >&5
+$as_echo_n "checking for libipt... " >&6; }
+if test "${ac_cv_libipt+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    ac_save_LIBS="$LIBS"
+    LIBS="$LIBS $LIBIPT"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include "intel-pt.h"
+int
+main ()
+{
+pt_configure (0);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_libipt=yes
+else
+  ac_cv_libipt=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+    LIBS="$ac_save_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libipt" >&5
+$as_echo "$ac_cv_libipt" >&6; }
+  if test "$ac_cv_libipt" = yes; then
+    HAVE_LIBIPT=yes
+
+$as_echo "#define HAVE_LIBIPT 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libipt" >&5
+$as_echo_n "checking how to link with libipt... " >&6; }
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBIPT" >&5
+$as_echo "$LIBIPT" >&6; }
+  else
+    HAVE_LIBIPT=no
+            CPPFLAGS="$ac_save_CPPFLAGS"
+    LIBIPT=
+    LTLIBIPT=
+  fi
+
+
+
+
+
+
+  if test "$HAVE_LIBIPT" != yes; then
+    if test "$with_intel_pt" = yes; then
+      as_fn_error "libipt is missing or unusable" "$LINENO" 5
+    else
+      { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libipt is missing or unusable; some features may be unavailable." >&5
+$as_echo "$as_me: WARNING: libipt is missing or unusable; some features may be unavailable." >&2;}
+    fi
+  fi
+fi
+
 # ------------------------- #
 # Checks for header files.  #
 # ------------------------- #
diff --git a/gdb/configure.ac b/gdb/configure.ac
index 2d73669..9ef998d 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -1223,6 +1223,26 @@ if test "$ENABLE_LIBMCHECK" = "yes" \
   AC_MSG_WARN(--enable-libmcheck may lead to spurious crashes if threads are used in python)
 fi
 
+AC_ARG_WITH(intel_pt,
+  AS_HELP_STRING([--with-intel-pt], [include Intel Processor Trace support (auto/yes/no)]),
+  [], [with_intel_pt=auto])
+AC_MSG_CHECKING([whether to use intel pt])
+AC_MSG_RESULT([$with_intel_pt])
+
+if test "${with_intel_pt}" = no; then
+  AC_MSG_WARN([Intel(R) Processor Trace support disabled; some features may be unavailable.])
+  HAVE_LIBIPT=no
+else
+  AC_LIB_HAVE_LINKFLAGS([ipt], [], [#include "intel-pt.h"], [pt_configure (0);])
+  if test "$HAVE_LIBIPT" != yes; then
+    if test "$with_intel_pt" = yes; then
+      AC_MSG_ERROR([libipt is missing or unusable])
+    else
+      AC_MSG_WARN([libipt is missing or unusable; some features may be unavailable.])
+    fi
+  fi
+fi
+
 # ------------------------- #
 # Checks for header files.  #
 # ------------------------- #
-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH v2 00/13] record btrace: prepare for a new trace format
@ 2014-11-20 10:47 Markus Metzger
  2014-11-20 10:47 ` [PATCH v2 09/13] btrace: identify cpu Markus Metzger
                   ` (12 more replies)
  0 siblings, 13 replies; 25+ messages in thread
From: Markus Metzger @ 2014-11-20 10:47 UTC (permalink / raw)
  To: palves; +Cc: gdb-patches

Changes to v1:
  - incorporated Pedro's comments
  - moved btrace-common from common/ into nat/ and renamed to x86-btrace
  - changed "btrace, linux: add perf event buffer abstraction" and
    "[wip] btrace: support Intel(R) Processor Trace" to support
    a new kernel interface: https://lkml.org/lkml/2014/11/14/292.


This is the first part of a patch series that adds support for a new trace
format.  The entire series consists of three parts:

  - preparation (this)
  - support live inferior tracing
  - support trace in core files


Besides preparing for an additional trace format, this first part adds the
following functionality:

  - allow the trace buffer size to be configured
  - indicate errors as gaps in the trace


In order to motivate some patches (e.g. identifying the cpu or changing the
btrace_compute_ftrace parameters), I also send a work-in-progress version of the
second part that adds support for the new trace format.  This patch is not part
of the series under review.


Markus Metzger (13):
  btrace: add struct btrace_data
  btrace: add format argument to supports_btrace
  btrace, linux: add perf event buffer abstraction
  record btrace: add configuration struct
  record-btrace: add bts buffer size configuration option
  btrace: update btrace_compute_ftrace parameters
  btrace: extend struct btrace_insn
  btrace: move and rename btrace-common
  btrace: identify cpu
  btrace: use the new cpu identifier
  record-btrace: indicate gaps
  configure: check for libipt
  [wip] btrace: support Intel(R) Processor Trace

 gdb/Makefile.in                                  |  16 +-
 gdb/NEWS                                         |  35 +
 gdb/btrace.c                                     | 899 ++++++++++++++++++++---
 gdb/btrace.h                                     | 100 ++-
 gdb/common/btrace-common.h                       |  90 ---
 gdb/config.in                                    |   3 +
 gdb/configure                                    | 498 +++++++++++++
 gdb/configure.ac                                 |  20 +
 gdb/doc/gdb.texinfo                              | 255 ++++++-
 gdb/features/btrace-conf.dtd                     |  14 +
 gdb/features/btrace.dtd                          |  16 +-
 gdb/gdbserver/Makefile.in                        |   8 +-
 gdb/gdbserver/linux-low.c                        | 147 +++-
 gdb/gdbserver/server.c                           | 214 +++++-
 gdb/gdbserver/target.h                           |  27 +-
 gdb/nat/linux-btrace.c                           | 722 ++++++++++++++----
 gdb/nat/linux-btrace.h                           |  86 ++-
 gdb/nat/x86-btrace.c                             | 129 ++++
 gdb/nat/x86-btrace.h                             | 224 ++++++
 gdb/record-btrace.c                              | 477 ++++++++++--
 gdb/remote.c                                     | 203 ++++-
 gdb/target-debug.h                               |   8 +
 gdb/target-delegates.c                           |  65 +-
 gdb/target.c                                     |  22 +-
 gdb/target.h                                     |  37 +-
 gdb/testsuite/gdb.btrace/buffer-size.exp         |  57 ++
 gdb/testsuite/gdb.btrace/delta.exp               |  12 +-
 gdb/testsuite/gdb.btrace/enable.exp              |   4 +-
 gdb/testsuite/gdb.btrace/finish.exp              |   3 +-
 gdb/testsuite/gdb.btrace/instruction_history.exp |   2 +-
 gdb/testsuite/gdb.btrace/next.exp                |   6 +-
 gdb/testsuite/gdb.btrace/nexti.exp               |   6 +-
 gdb/testsuite/gdb.btrace/nohist.exp              |   3 +-
 gdb/testsuite/gdb.btrace/step.exp                |   6 +-
 gdb/testsuite/gdb.btrace/stepi.exp               |   6 +-
 gdb/x86-linux-nat.c                              |  18 +-
 36 files changed, 3931 insertions(+), 507 deletions(-)
 delete mode 100644 gdb/common/btrace-common.h
 create mode 100644 gdb/features/btrace-conf.dtd
 create mode 100644 gdb/nat/x86-btrace.c
 create mode 100644 gdb/nat/x86-btrace.h
 create mode 100644 gdb/testsuite/gdb.btrace/buffer-size.exp

-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH v2 07/13] btrace: extend struct btrace_insn
  2014-11-20 10:47 [PATCH v2 00/13] record btrace: prepare for a new trace format Markus Metzger
  2014-11-20 10:47 ` [PATCH v2 09/13] btrace: identify cpu Markus Metzger
  2014-11-20 10:47 ` [PATCH v2 02/13] btrace: add format argument to supports_btrace Markus Metzger
@ 2014-11-20 10:47 ` Markus Metzger
  2014-11-20 10:47 ` [PATCH v2 04/13] record btrace: add configuration struct Markus Metzger
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Markus Metzger @ 2014-11-20 10:47 UTC (permalink / raw)
  To: palves; +Cc: gdb-patches

Add the instruction's size as well as a coarse classification to struct
btrace_insn.  Use the information in ftrace_update_function and
ftrace_find_call.

2014-11-20  Markus Metzger  <markus.t.metzger@intel.com>

	* btrace.h (btrace_insn_class): New.
	(btrace_insn) <size, iclass>: New.
	* btrace.c (ftrace_find_call): Update parameters.  Update users.
	Use instruction classification.
	(ftrace_new_return): Update parameters.  Update users.
	(ftrace_update_function): Update parameters.  Update users.  Use
	instruction classification.
	(ftrace_update_insns): Update parameters.  Update users.
	(ftrace_classify_insn): New.
	(btrace_compute_ftrace_bts): Fill in new btrace_insn fields.  Add
	TRY_CATCH around call to gdb_insn_length.
---
 gdb/btrace.c | 111 ++++++++++++++++++++++++++++++++---------------------------
 gdb/btrace.h |  22 ++++++++++++
 2 files changed, 83 insertions(+), 50 deletions(-)

diff --git a/gdb/btrace.c b/gdb/btrace.c
index d261d57..efd0572 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -330,20 +330,18 @@ ftrace_find_caller (struct btrace_function *bfun,
    tail calls ending with a jump).  */
 
 static struct btrace_function *
-ftrace_find_call (struct gdbarch *gdbarch, struct btrace_function *bfun)
+ftrace_find_call (struct btrace_function *bfun)
 {
   for (; bfun != NULL; bfun = 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));
 
       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;
     }
 
@@ -355,8 +353,7 @@ ftrace_find_call (struct gdbarch *gdbarch, struct btrace_function *bfun)
    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_function *prev,
 		   struct minimal_symbol *mfun,
 		   struct symbol *fun)
 {
@@ -391,7 +388,7 @@ 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 (prev->up);
       if (caller == NULL)
 	{
 	  /* There is no call in PREV's back trace.  We assume that the
@@ -454,8 +451,7 @@ ftrace_new_switch (struct btrace_function *prev,
    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_function *bfun, CORE_ADDR pc)
 {
   struct bound_minimal_symbol bmfun;
   struct minimal_symbol *mfun;
@@ -485,24 +481,29 @@ ftrace_update_function (struct gdbarch *gdbarch,
 
   if (last != NULL)
     {
-      CORE_ADDR lpc;
+      switch (last->iclass)
+	{
+	case BTRACE_INSN_RETURN:
+	  return ftrace_new_return (bfun, mfun, fun);
 
-      lpc = last->pc;
+	case BTRACE_INSN_CALL:
+	  /* Ignore calls to the next instruction.  They are used for PIC.  */
+	  if (last->pc + last->size == pc)
+	    break;
 
-      /* Check for returns.  */
-      if (gdbarch_insn_is_ret (gdbarch, lpc))
-	return ftrace_new_return (gdbarch, bfun, mfun, fun);
+	  return ftrace_new_call (bfun, mfun, fun);
 
-      /* Check for calls.  */
-      if (gdbarch_insn_is_call (gdbarch, lpc))
-	{
-	  int size;
+	case BTRACE_INSN_JUMP:
+	  {
+	    CORE_ADDR start;
 
-	  size = gdb_insn_length (gdbarch, lpc);
+	    start = get_pc_function_start (pc);
 
-	  /* 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 (start == 0 || start == pc)
+	      return ftrace_new_tailcall (bfun, mfun, fun);
+	  }
 	}
     }
 
@@ -514,24 +515,6 @@ ftrace_update_function (struct gdbarch *gdbarch,
 		    ftrace_print_function_name (bfun),
 		    ftrace_print_filename (bfun));
 
-      if (last != NULL)
-	{
-	  CORE_ADDR start, lpc;
-
-	  start = get_pc_function_start (pc);
-
-	  /* 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;
-
-	  lpc = last->pc;
-
-	  /* Jumps indicate optimized tail calls.  */
-	  if (start == pc && gdbarch_insn_is_jump (gdbarch, lpc))
-	    return ftrace_new_tailcall (bfun, mfun, fun);
-	}
-
       return ftrace_new_switch (bfun, mfun, fun);
     }
 
@@ -574,17 +557,37 @@ ftrace_update_lines (struct btrace_function *bfun, CORE_ADDR pc)
 /* Add the instruction at PC to BFUN's instructions.  */
 
 static void
-ftrace_update_insns (struct btrace_function *bfun, CORE_ADDR pc)
+ftrace_update_insns (struct btrace_function *bfun,
+		     const struct btrace_insn *insn)
 {
-  struct btrace_insn *insn;
-
-  insn = VEC_safe_push (btrace_insn_s, bfun->insn, NULL);
-  insn->pc = pc;
+  VEC_safe_push (btrace_insn_s, bfun->insn, insn);
 
   if (record_debug > 1)
     ftrace_debug (bfun, "update insn");
 }
 
+/* Classify the instruction at PC.  */
+
+static enum btrace_insn_class
+ftrace_classify_insn (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+  volatile struct gdb_exception error;
+  enum btrace_insn_class iclass;
+
+  iclass = BTRACE_INSN_OTHER;
+  TRY_CATCH (error, RETURN_MASK_ERROR)
+    {
+      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;
+    }
+
+  return iclass;
+}
+
 /* Compute the function branch trace from BTS trace.  */
 
 static void
@@ -616,6 +619,8 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
 
       for (;;)
 	{
+	  volatile struct gdb_exception error;
+	  struct btrace_insn insn;
 	  int size;
 
 	  /* We should hit the end of the block.  Warn if we went too far.  */
@@ -626,7 +631,7 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
 	      break;
 	    }
 
-	  end = ftrace_update_function (gdbarch, end, pc);
+	  end = ftrace_update_function (end, pc);
 	  if (begin == NULL)
 	    begin = end;
 
@@ -635,16 +640,22 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
 	  if (blk != 0)
 	    level = min (level, end->level);
 
-	  ftrace_update_insns (end, pc);
+	  size = 0;
+	  TRY_CATCH (error, RETURN_MASK_ERROR)
+	    size = gdb_insn_length (gdbarch, pc);
+
+	  insn.pc = pc;
+	  insn.size = size;
+	  insn.iclass = ftrace_classify_insn (gdbarch, pc);
+
+	  ftrace_update_insns (end, &insn);
 	  ftrace_update_lines (end, pc);
 
 	  /* 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."),
diff --git a/gdb/btrace.h b/gdb/btrace.h
index 8656cda..3de7b73 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -31,6 +31,22 @@
 struct thread_info;
 struct btrace_function;
 
+/* A coarse instruction classification.  */
+enum btrace_insn_class
+{
+  /* The instruction is something not listed below.  */
+  BTRACE_INSN_OTHER,
+
+  /* The instruction is a function call.  */
+  BTRACE_INSN_CALL,
+
+  /* The instruction is a function return.  */
+  BTRACE_INSN_RETURN,
+
+  /* The instruction is an unconditional jump.  */
+  BTRACE_INSN_JUMP
+};
+
 /* A branch trace instruction.
 
    This represents a single instruction in a branch trace.  */
@@ -38,6 +54,12 @@ struct btrace_insn
 {
   /* The address of this instruction.  */
   CORE_ADDR pc;
+
+  /* The size of this instruction in bytes.  */
+  gdb_byte size;
+
+  /* The instruction class of this instruction.  */
+  enum btrace_insn_class iclass;
 };
 
 /* A vector of branch trace instructions.  */
-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH v2 08/13] btrace: move and rename btrace-common
  2014-11-20 10:47 [PATCH v2 00/13] record btrace: prepare for a new trace format Markus Metzger
                   ` (7 preceding siblings ...)
  2014-11-20 10:47 ` [PATCH v2 03/13] btrace, linux: add perf event buffer abstraction Markus Metzger
@ 2014-11-20 10:47 ` Markus Metzger
  2015-01-27 11:25   ` Pedro Alves
  2014-11-20 10:47 ` [PATCH v2 05/13] record-btrace: add bts buffer size configuration option Markus Metzger
                   ` (3 subsequent siblings)
  12 siblings, 1 reply; 25+ messages in thread
From: Markus Metzger @ 2014-11-20 10:47 UTC (permalink / raw)
  To: palves; +Cc: gdb-patches

2014-11-20  Markus Metzger <markus.t.metzger@intel.com>

	* common/btrace-common.h: Move and rename to ...
	* nat/x86-btrace.h: ... this.  Update users.
	* common/btrace-common.c: Move and rename to ...
	* nat/x86-btrace.c: ... this.  Update Makefiles.
---
 gdb/Makefile.in            |   8 +--
 gdb/btrace.h               |   2 +-
 gdb/common/btrace-common.c |  82 ------------------------
 gdb/common/btrace-common.h | 156 ---------------------------------------------
 gdb/gdbserver/Makefile.in  |   6 +-
 gdb/gdbserver/linux-low.c  |   2 +-
 gdb/gdbserver/server.c     |   2 +-
 gdb/gdbserver/target.h     |   2 +-
 gdb/nat/linux-btrace.h     |   2 +-
 gdb/nat/x86-btrace.c       |  82 ++++++++++++++++++++++++
 gdb/nat/x86-btrace.h       | 156 +++++++++++++++++++++++++++++++++++++++++++++
 11 files changed, 250 insertions(+), 250 deletions(-)
 delete mode 100644 gdb/common/btrace-common.c
 delete mode 100644 gdb/common/btrace-common.h
 create mode 100644 gdb/nat/x86-btrace.c
 create mode 100644 gdb/nat/x86-btrace.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index e7181c4..fc9c695 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -853,7 +853,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
 	common/format.c common/filestuff.c btrace.c record-btrace.c ctf.c \
 	target/waitstatus.c common/print-utils.c common/rsp-low.c \
 	common/errors.c common/common-debug.c common/common-exceptions.c \
-	common/btrace-common.c
+	nat/x86-btrace.c
 
 LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
 
@@ -1040,7 +1040,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \
 	format.o registry.o btrace.o record-btrace.o waitstatus.o \
 	print-utils.o rsp-low.o errors.o common-debug.o debug.o \
-	common-exceptions.o btrace-common.o
+	common-exceptions.o x86-btrace.o
 
 TSOBS = inflow.o
 
@@ -2173,8 +2173,8 @@ common-exceptions.o: ${srcdir}/common/common-exceptions.c
 	$(COMPILE) $(srcdir)/common/common-exceptions.c
 	$(POSTCOMPILE)
 
-btrace-common.o: ${srcdir}/common/btrace-common.c
-	$(COMPILE) $(srcdir)/common/btrace-common.c
+x86-btrace.o: ${srcdir}/nat/x86-btrace.c
+	$(COMPILE) $(srcdir)/nat/x86-btrace.c
 	$(POSTCOMPILE)
 
 #
diff --git a/gdb/btrace.h b/gdb/btrace.h
index 3de7b73..2082bc7 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -26,7 +26,7 @@
    inferior.  For presentation purposes, the branch trace is represented as a
    list of sequential control-flow blocks, one such list per thread.  */
 
-#include "btrace-common.h"
+#include "nat/x86-btrace.h"
 
 struct thread_info;
 struct btrace_function;
diff --git a/gdb/common/btrace-common.c b/gdb/common/btrace-common.c
deleted file mode 100644
index 869d0cb..0000000
--- a/gdb/common/btrace-common.c
+++ /dev/null
@@ -1,82 +0,0 @@
-/* Copyright (C) 2014 Free Software Foundation, Inc.
-
-   Contributed by Intel Corp. <markus.t.metzger@intel.com>
-
-   This file is part of GDB.
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
-
-#include "btrace-common.h"
-
-
-/* See btrace-common.h.  */
-
-const char *
-btrace_format_string (enum btrace_format format)
-{
-  switch (format)
-    {
-    case BTRACE_FORMAT_NONE:
-      return _("No or unknown format");
-
-    case BTRACE_FORMAT_BTS:
-      return _("Intel(R) Branch Trace Store");
-    }
-
-  internal_error (__FILE__, __LINE__, _("Unknown branch trace format"));
-}
-
-/* See btrace-common.h.  */
-
-void
-btrace_data_init (struct btrace_data *data)
-{
-  data->format = BTRACE_FORMAT_NONE;
-}
-
-/* See btrace-common.h.  */
-
-void
-btrace_data_fini (struct btrace_data *data)
-{
-  switch (data->format)
-    {
-    case BTRACE_FORMAT_NONE:
-      /* Nothing to do.  */
-      return;
-
-    case BTRACE_FORMAT_BTS:
-      VEC_free (btrace_block_s, data->variant.bts.blocks);
-      return;
-    }
-
-  internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
-}
-
-/* See btrace-common.h.  */
-
-int
-btrace_data_empty (struct btrace_data *data)
-{
-  switch (data->format)
-    {
-    case BTRACE_FORMAT_NONE:
-      return 1;
-
-    case BTRACE_FORMAT_BTS:
-      return VEC_empty (btrace_block_s, data->variant.bts.blocks);
-    }
-
-  internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
-}
diff --git a/gdb/common/btrace-common.h b/gdb/common/btrace-common.h
deleted file mode 100644
index 8147e6d..0000000
--- a/gdb/common/btrace-common.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/* Branch trace support for GDB, the GNU debugger.
-
-   Copyright (C) 2013-2014 Free Software Foundation, Inc.
-
-   Contributed by Intel Corp. <markus.t.metzger@intel.com>.
-
-   This file is part of GDB.
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
-
-#ifndef BTRACE_COMMON_H
-#define BTRACE_COMMON_H
-
-/* Branch tracing (btrace) is a per-thread control-flow execution trace of the
-   inferior.  For presentation purposes, the branch trace is represented as a
-   list of sequential control-flow blocks, one such list per thread.  */
-
-#include "common-defs.h"
-#include "vec.h"
-
-/* A branch trace block.
-
-   This represents a block of sequential control-flow.  Adjacent blocks will be
-   connected via calls, returns, or jumps.  The latter can be direct or
-   indirect, conditional or unconditional.  Branches can further be
-   asynchronous, e.g. interrupts.  */
-struct btrace_block
-{
-  /* The address of the first byte of the first instruction in the block.
-     The address may be zero if we do not know the beginning of this block,
-     such as for the first block in a delta trace.  */
-  CORE_ADDR begin;
-
-  /* The address of the first byte of the last instruction in the block.  */
-  CORE_ADDR end;
-};
-
-/* Define functions operating on a vector of branch trace blocks.  */
-typedef struct btrace_block btrace_block_s;
-DEF_VEC_O (btrace_block_s);
-
-/* Enumeration of btrace formats.  */
-
-enum btrace_format
-{
-  /* No branch trace format.  */
-  BTRACE_FORMAT_NONE,
-
-  /* Branch trace is in Branch Trace Store format.
-     Actually, the format is a sequence of blocks derived from BTS.  */
-  BTRACE_FORMAT_BTS
-};
-
-/* A BTS configuration.  */
-
-struct btrace_config_bts
-{
-  /* The size of the branch trace buffer in bytes.  */
-  unsigned int size;
-};
-
-/* A branch tracing configuration.
-
-   This describes the requested configuration as well as the actually
-   obtained configuration.
-   We describe the configuration for all different formats so we can
-   easily switch between formats.  */
-
-struct btrace_config
-{
-  /* The branch tracing format.  */
-  enum btrace_format format;
-
-  /* The BTS format configuration.  */
-  struct btrace_config_bts bts;
-};
-
-/* Branch trace in BTS format.  */
-struct btrace_data_bts
-{
-  /* Branch trace is represented as a vector of branch trace blocks starting
-     with the most recent block.  */
-  VEC (btrace_block_s) *blocks;
-};
-
-/* The branch trace data.  */
-struct btrace_data
-{
-  enum btrace_format format;
-
-  union
-  {
-    /* Format == BTRACE_FORMAT_BTS.  */
-    struct btrace_data_bts bts;
-  } variant;
-};
-
-/* Target specific branch trace information.  */
-struct btrace_target_info;
-
-/* Enumeration of btrace read types.  */
-
-enum btrace_read_type
-{
-  /* Send all available trace.  */
-  BTRACE_READ_ALL,
-
-  /* Send all available trace, if it changed.  */
-  BTRACE_READ_NEW,
-
-  /* Send the trace since the last request.  This will fail if the trace
-     buffer overflowed.  */
-  BTRACE_READ_DELTA
-};
-
-/* Enumeration of btrace errors.  */
-
-enum btrace_error
-{
-  /* No error.  Everything is OK.  */
-  BTRACE_ERR_NONE,
-
-  /* An unknown error.  */
-  BTRACE_ERR_UNKNOWN,
-
-  /* Branch tracing is not supported on this system.  */
-  BTRACE_ERR_NOT_SUPPORTED,
-
-  /* The branch trace buffer overflowed; no delta read possible.  */
-  BTRACE_ERR_OVERFLOW
-};
-
-/* Return a string representation of FORMAT.  */
-extern const char *btrace_format_string (enum btrace_format format);
-
-/* Initialize DATA.  */
-extern void btrace_data_init (struct btrace_data *data);
-
-/* Cleanup DATA.  */
-extern void btrace_data_fini (struct btrace_data *data);
-
-/* Return non-zero if DATA is empty; zero otherwise.  */
-extern int btrace_data_empty (struct btrace_data *data);
-
-#endif /* BTRACE_COMMON_H */
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index 98897d9..1ce3fe4 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -173,7 +173,7 @@ SFILES=	$(srcdir)/gdbreplay.c $(srcdir)/inferiors.c $(srcdir)/dll.c \
 	$(srcdir)/common/rsp-low.c $(srcdir)/common/errors.c \
 	$(srcdir)/common/common-debug.c $(srcdir)/common/cleanups.c \
 	$(srcdir)/common/common-exceptions.c $(srcdir)/symbol.c \
-	$(srcdir)/common/btrace-common.c
+	$(srcdir)/nat/x86-btrace.c
 
 DEPFILES = @GDBSERVER_DEPFILES@
 
@@ -187,7 +187,7 @@ OBS = agent.o ax.o inferiors.o regcache.o remote-utils.o server.o signals.o \
       mem-break.o hostio.o event-loop.o tracepoint.o xml-utils.o \
       common-utils.o ptid.o buffer.o format.o filestuff.o dll.o notif.o \
       tdesc.o print-utils.o rsp-low.o errors.o common-debug.o cleanups.o \
-      common-exceptions.o symbol.o btrace-common.o \
+      common-exceptions.o symbol.o x86-btrace.o \
       $(XML_BUILTIN) $(DEPFILES) $(LIBOBJS)
 GDBREPLAY_OBS = gdbreplay.o version.o
 GDBSERVER_LIBS = @GDBSERVER_LIBS@
@@ -582,7 +582,7 @@ linux-waitpid.o: ../nat/linux-waitpid.c
 mips-linux-watch.o: ../nat/mips-linux-watch.c
 	$(COMPILE) $<
 	$(POSTCOMPILE)
-btrace-common.o: ../common/btrace-common.c
+x86-btrace.o: ../nat/x86-btrace.c
 	$(COMPILE) $<
 	$(POSTCOMPILE)
 
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 08192c5..217b3f9 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -102,7 +102,7 @@
 
 #ifdef HAVE_LINUX_BTRACE
 # include "nat/linux-btrace.h"
-# include "btrace-common.h"
+# include "nat/x86-btrace.h"
 #endif
 
 #ifndef HAVE_ELF32_AUXV_T
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index ebacd96..4ed9837 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -30,7 +30,7 @@
 #endif
 #include "gdb_vecs.h"
 #include "gdb_wait.h"
-#include "btrace-common.h"
+#include "nat/x86-btrace.h"
 #include "filestuff.h"
 #include "tracepoint.h"
 #include "dll.h"
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index b877df8..468edc6 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -26,7 +26,7 @@
 #include "target/wait.h"
 #include "target/waitstatus.h"
 #include "mem-break.h"
-#include "btrace-common.h"
+#include "nat/x86-btrace.h"
 
 struct emit_ops;
 struct buffer;
diff --git a/gdb/nat/linux-btrace.h b/gdb/nat/linux-btrace.h
index 1e9af88..1ad83f0 100644
--- a/gdb/nat/linux-btrace.h
+++ b/gdb/nat/linux-btrace.h
@@ -22,7 +22,7 @@
 #ifndef LINUX_BTRACE_H
 #define LINUX_BTRACE_H
 
-#include "btrace-common.h"
+#include "x86-btrace.h"
 #include "vec.h"
 #include <stdint.h>
 
diff --git a/gdb/nat/x86-btrace.c b/gdb/nat/x86-btrace.c
new file mode 100644
index 0000000..5ca5731
--- /dev/null
+++ b/gdb/nat/x86-btrace.c
@@ -0,0 +1,82 @@
+/* Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <markus.t.metzger@intel.com>
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "x86-btrace.h"
+
+
+/* See x86-btrace.h.  */
+
+const char *
+btrace_format_string (enum btrace_format format)
+{
+  switch (format)
+    {
+    case BTRACE_FORMAT_NONE:
+      return _("No or unknown format");
+
+    case BTRACE_FORMAT_BTS:
+      return _("Intel(R) Branch Trace Store");
+    }
+
+  internal_error (__FILE__, __LINE__, _("Unknown branch trace format"));
+}
+
+/* See x86-btrace.h.  */
+
+void
+btrace_data_init (struct btrace_data *data)
+{
+  data->format = BTRACE_FORMAT_NONE;
+}
+
+/* See x86-btrace.h.  */
+
+void
+btrace_data_fini (struct btrace_data *data)
+{
+  switch (data->format)
+    {
+    case BTRACE_FORMAT_NONE:
+      /* Nothing to do.  */
+      return;
+
+    case BTRACE_FORMAT_BTS:
+      VEC_free (btrace_block_s, data->variant.bts.blocks);
+      return;
+    }
+
+  internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
+}
+
+/* See x86-btrace.h.  */
+
+int
+btrace_data_empty (struct btrace_data *data)
+{
+  switch (data->format)
+    {
+    case BTRACE_FORMAT_NONE:
+      return 1;
+
+    case BTRACE_FORMAT_BTS:
+      return VEC_empty (btrace_block_s, data->variant.bts.blocks);
+    }
+
+  internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
+}
diff --git a/gdb/nat/x86-btrace.h b/gdb/nat/x86-btrace.h
new file mode 100644
index 0000000..48d6284
--- /dev/null
+++ b/gdb/nat/x86-btrace.h
@@ -0,0 +1,156 @@
+/* Branch trace support for GDB, the GNU debugger.
+
+   Copyright (C) 2013-2014 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <markus.t.metzger@intel.com>.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef X86_BTRACE_H
+#define X86_BTRACE_H
+
+/* Branch tracing (btrace) is a per-thread control-flow execution trace of the
+   inferior.  For presentation purposes, the branch trace is represented as a
+   list of sequential control-flow blocks, one such list per thread.  */
+
+#include "common-defs.h"
+#include "vec.h"
+
+/* A branch trace block.
+
+   This represents a block of sequential control-flow.  Adjacent blocks will be
+   connected via calls, returns, or jumps.  The latter can be direct or
+   indirect, conditional or unconditional.  Branches can further be
+   asynchronous, e.g. interrupts.  */
+struct btrace_block
+{
+  /* The address of the first byte of the first instruction in the block.
+     The address may be zero if we do not know the beginning of this block,
+     such as for the first block in a delta trace.  */
+  CORE_ADDR begin;
+
+  /* The address of the first byte of the last instruction in the block.  */
+  CORE_ADDR end;
+};
+
+/* Define functions operating on a vector of branch trace blocks.  */
+typedef struct btrace_block btrace_block_s;
+DEF_VEC_O (btrace_block_s);
+
+/* Enumeration of btrace formats.  */
+
+enum btrace_format
+{
+  /* No branch trace format.  */
+  BTRACE_FORMAT_NONE,
+
+  /* Branch trace is in Branch Trace Store format.
+     Actually, the format is a sequence of blocks derived from BTS.  */
+  BTRACE_FORMAT_BTS
+};
+
+/* A BTS configuration.  */
+
+struct btrace_config_bts
+{
+  /* The size of the branch trace buffer in bytes.  */
+  unsigned int size;
+};
+
+/* A branch tracing configuration.
+
+   This describes the requested configuration as well as the actually
+   obtained configuration.
+   We describe the configuration for all different formats so we can
+   easily switch between formats.  */
+
+struct btrace_config
+{
+  /* The branch tracing format.  */
+  enum btrace_format format;
+
+  /* The BTS format configuration.  */
+  struct btrace_config_bts bts;
+};
+
+/* Branch trace in BTS format.  */
+struct btrace_data_bts
+{
+  /* Branch trace is represented as a vector of branch trace blocks starting
+     with the most recent block.  */
+  VEC (btrace_block_s) *blocks;
+};
+
+/* The branch trace data.  */
+struct btrace_data
+{
+  enum btrace_format format;
+
+  union
+  {
+    /* Format == BTRACE_FORMAT_BTS.  */
+    struct btrace_data_bts bts;
+  } variant;
+};
+
+/* Target specific branch trace information.  */
+struct btrace_target_info;
+
+/* Enumeration of btrace read types.  */
+
+enum btrace_read_type
+{
+  /* Send all available trace.  */
+  BTRACE_READ_ALL,
+
+  /* Send all available trace, if it changed.  */
+  BTRACE_READ_NEW,
+
+  /* Send the trace since the last request.  This will fail if the trace
+     buffer overflowed.  */
+  BTRACE_READ_DELTA
+};
+
+/* Enumeration of btrace errors.  */
+
+enum btrace_error
+{
+  /* No error.  Everything is OK.  */
+  BTRACE_ERR_NONE,
+
+  /* An unknown error.  */
+  BTRACE_ERR_UNKNOWN,
+
+  /* Branch tracing is not supported on this system.  */
+  BTRACE_ERR_NOT_SUPPORTED,
+
+  /* The branch trace buffer overflowed; no delta read possible.  */
+  BTRACE_ERR_OVERFLOW
+};
+
+/* Return a string representation of FORMAT.  */
+extern const char *btrace_format_string (enum btrace_format format);
+
+/* Initialize DATA.  */
+extern void btrace_data_init (struct btrace_data *data);
+
+/* Cleanup DATA.  */
+extern void btrace_data_fini (struct btrace_data *data);
+
+/* Return non-zero if DATA is empty; zero otherwise.  */
+extern int btrace_data_empty (struct btrace_data *data);
+
+#endif /* X86_BTRACE_H */
-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH v2 02/13] btrace: add format argument to supports_btrace
  2014-11-20 10:47 [PATCH v2 00/13] record btrace: prepare for a new trace format Markus Metzger
  2014-11-20 10:47 ` [PATCH v2 09/13] btrace: identify cpu Markus Metzger
@ 2014-11-20 10:47 ` Markus Metzger
  2014-11-20 10:47 ` [PATCH v2 07/13] btrace: extend struct btrace_insn Markus Metzger
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Markus Metzger @ 2014-11-20 10:47 UTC (permalink / raw)
  To: palves; +Cc: gdb-patches

Add a format argument to the various supports_btrace functions to check
for support of a specific btrace format.  This is to prepare for a new
format.

Removed two redundant calls.  The check will be made in the subsequent
btrace_enable call.

2014-11-20  Markus Metzger <markus.t.metzger@intel.com>

	* btrace.c (btrace_enable): Pass BTRACE_FORMAT_BTS.
	* record-btrace.c (record_btrace_open): Remove call to
	target_supports_btrace.
	* remote.c (remote_supports_btrace): Update parameters.
	* target.c (target_supports_btrace): Update parameters.
	* target.h (to_supports_btrace, target_supports_btrace): Update
	parameters.
	* target-delegates.c: Regenerate.
	* target-debug.h (target_debug_print_enum_btrace_format): New.
	* nat/linux-btrace.c
	(kernel_supports_btrace): Rename into ...
	(kernel_supports_bts): ... this.  Update users.  Update warning text.
	(intel_supports_btrace): Rename into ...
	(intel_supports_bts): ... this.  Update users.
	(cpu_supports_btrace): Rename into ...
	(cpu_supports_bts): ... this.  Update users.
	(linux_supports_btrace): Update parameters.  Split into this and ...
	(linux_supports_bts): ... this.
	* nat/linux-btrace.h (linux_supports_btrace): Update parameters.

gdbserver/
	* server.c (handle_btrace_general_set): Remove call to
	target_supports_btrace.
	(supported_btrace_packets): New.
	(handle_query): Call supported_btrace_packets.
	* target.h: include btrace-common.h.
	(btrace_target_info): Removed.
	(supports_btrace, target_supports_btrace): Update parameters.
---
 gdb/btrace.c           |  2 +-
 gdb/gdbserver/server.c | 27 +++++++++++++-----------
 gdb/gdbserver/target.h |  8 +++----
 gdb/nat/linux-btrace.c | 57 ++++++++++++++++++++++++++++++++------------------
 gdb/nat/linux-btrace.h |  2 +-
 gdb/record-btrace.c    |  3 ---
 gdb/remote.c           | 15 +++++++++----
 gdb/target-debug.h     |  2 ++
 gdb/target-delegates.c | 12 ++++++-----
 gdb/target.c           |  8 +++++++
 gdb/target.h           |  5 ++---
 11 files changed, 88 insertions(+), 53 deletions(-)

diff --git a/gdb/btrace.c b/gdb/btrace.c
index 470a676..d8dae56 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -731,7 +731,7 @@ btrace_enable (struct thread_info *tp)
   if (tp->btrace.target != NULL)
     return;
 
-  if (!target_supports_btrace ())
+  if (!target_supports_btrace (BTRACE_FORMAT_BTS))
     error (_("Target does not support branch tracing."));
 
   DEBUG ("enable thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 173a22e..dae364d 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -426,12 +426,6 @@ handle_btrace_general_set (char *own_buf)
 
   op = own_buf + strlen ("Qbtrace:");
 
-  if (!target_supports_btrace ())
-    {
-      strcpy (own_buf, "E.Target does not support branch tracing.");
-      return -1;
-    }
-
   if (ptid_equal (general_thread, null_ptid)
       || ptid_equal (general_thread, minus_one_ptid))
     {
@@ -1692,6 +1686,20 @@ crc32 (CORE_ADDR base, int len, unsigned int crc)
   return (unsigned long long) crc;
 }
 
+/* Add supported btrace packets to BUF.  */
+
+static void
+supported_btrace_packets (char *buf)
+{
+  if (target_supports_btrace (BTRACE_FORMAT_BTS))
+    strcat (buf, ";Qbtrace:bts+");
+  else
+    return;
+
+  strcat (buf, ";Qbtrace:off+");
+  strcat (buf, ";qXfer:btrace:read+");
+}
+
 /* Handle all of the extended 'q' packets.  */
 
 void
@@ -1923,12 +1931,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       if (target_supports_agent ())
 	strcat (own_buf, ";QAgent+");
 
-      if (target_supports_btrace ())
-	{
-	  strcat (own_buf, ";Qbtrace:bts+");
-	  strcat (own_buf, ";Qbtrace:off+");
-	  strcat (own_buf, ";qXfer:btrace:read+");
-	}
+      supported_btrace_packets (own_buf);
 
       return;
     }
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 5e29b7f..dd5b07a 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -26,9 +26,9 @@
 #include "target/wait.h"
 #include "target/waitstatus.h"
 #include "mem-break.h"
+#include "btrace-common.h"
 
 struct emit_ops;
-struct btrace_target_info;
 struct buffer;
 struct process_info;
 
@@ -358,7 +358,7 @@ struct target_ops
   int (*supports_agent) (void);
 
   /* Check whether the target supports branch tracing.  */
-  int (*supports_btrace) (struct target_ops *);
+  int (*supports_btrace) (struct target_ops *, enum btrace_format);
 
   /* Enable branch tracing for @ptid and allocate a branch trace target
      information struct for reading and for disabling branch trace.  */
@@ -499,9 +499,9 @@ int kill_inferior (int);
   (the_target->supports_agent ? \
    (*the_target->supports_agent) () : 0)
 
-#define target_supports_btrace()			\
+#define target_supports_btrace(format)			\
   (the_target->supports_btrace				\
-   ? (*the_target->supports_btrace) (the_target) : 0)
+   ? (*the_target->supports_btrace) (the_target, format) : 0)
 
 #define target_enable_btrace(ptid) \
   (*the_target->enable_btrace) (ptid)
diff --git a/gdb/nat/linux-btrace.c b/gdb/nat/linux-btrace.c
index 916f6b3..6ff797c 100644
--- a/gdb/nat/linux-btrace.c
+++ b/gdb/nat/linux-btrace.c
@@ -248,10 +248,10 @@ perf_event_read_bts (struct btrace_target_info* tinfo, const uint8_t *begin,
   return btrace;
 }
 
-/* Check whether the kernel supports branch tracing.  */
+/* Check whether the kernel supports BTS.  */
 
 static int
-kernel_supports_btrace (void)
+kernel_supports_bts (void)
 {
   struct perf_event_attr attr;
   pid_t child, pid;
@@ -262,14 +262,14 @@ kernel_supports_btrace (void)
   switch (child)
     {
     case -1:
-      warning (_("test branch tracing: cannot fork: %s."), strerror (errno));
+      warning (_("test bts: cannot fork: %s."), strerror (errno));
       return 0;
 
     case 0:
       status = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
       if (status != 0)
 	{
-	  warning (_("test branch tracing: cannot PTRACE_TRACEME: %s."),
+	  warning (_("test bts: cannot PTRACE_TRACEME: %s."),
 		   strerror (errno));
 	  _exit (1);
 	}
@@ -277,7 +277,7 @@ kernel_supports_btrace (void)
       status = raise (SIGTRAP);
       if (status != 0)
 	{
-	  warning (_("test branch tracing: cannot raise SIGTRAP: %s."),
+	  warning (_("test bts: cannot raise SIGTRAP: %s."),
 		   strerror (errno));
 	  _exit (1);
 	}
@@ -288,14 +288,14 @@ kernel_supports_btrace (void)
       pid = waitpid (child, &status, 0);
       if (pid != child)
 	{
-	  warning (_("test branch tracing: bad pid %ld, error: %s."),
+	  warning (_("test bts: bad pid %ld, error: %s."),
 		   (long) pid, strerror (errno));
 	  return 0;
 	}
 
       if (!WIFSTOPPED (status))
 	{
-	  warning (_("test branch tracing: expected stop. status: %d."),
+	  warning (_("test bts: expected stop. status: %d."),
 		   status);
 	  return 0;
 	}
@@ -320,10 +320,10 @@ kernel_supports_btrace (void)
       pid = waitpid (child, &status, 0);
       if (pid != child)
 	{
-	  warning (_("test branch tracing: bad pid %ld, error: %s."),
+	  warning (_("test bts: bad pid %ld, error: %s."),
 		   (long) pid, strerror (errno));
 	  if (!WIFSIGNALED (status))
-	    warning (_("test branch tracing: expected killed. status: %d."),
+	    warning (_("test bts: expected killed. status: %d."),
 		     status);
 	}
 
@@ -331,10 +331,10 @@ kernel_supports_btrace (void)
     }
 }
 
-/* Check whether an Intel cpu supports branch tracing.  */
+/* Check whether an Intel cpu supports BTS.  */
 
 static int
-intel_supports_btrace (void)
+intel_supports_bts (void)
 {
   unsigned int cpuid, model, family;
 
@@ -372,10 +372,10 @@ intel_supports_btrace (void)
   return 1;
 }
 
-/* Check whether the cpu supports branch tracing.  */
+/* Check whether the cpu supports BTS.  */
 
 static int
-cpu_supports_btrace (void)
+cpu_supports_bts (void)
 {
   unsigned int ebx, ecx, edx;
 
@@ -384,24 +384,24 @@ cpu_supports_btrace (void)
 
   if (ebx == signature_INTEL_ebx && ecx == signature_INTEL_ecx
       && edx == signature_INTEL_edx)
-    return intel_supports_btrace ();
+    return intel_supports_bts ();
 
   /* Don't know about others.  Let's assume they do.  */
   return 1;
 }
 
-/* See linux-btrace.h.  */
+/* Check whether the linux target supports BTS.  */
 
-int
-linux_supports_btrace (struct target_ops *ops)
+static int
+linux_supports_bts (void)
 {
   static int cached;
 
   if (cached == 0)
     {
-      if (!kernel_supports_btrace ())
+      if (!kernel_supports_bts ())
 	cached = -1;
-      else if (!cpu_supports_btrace ())
+      else if (!cpu_supports_bts ())
 	cached = -1;
       else
 	cached = 1;
@@ -412,6 +412,23 @@ linux_supports_btrace (struct target_ops *ops)
 
 /* See linux-btrace.h.  */
 
+int
+linux_supports_btrace (struct target_ops *ops, enum btrace_format format)
+{
+  switch (format)
+    {
+    case BTRACE_FORMAT_NONE:
+      return 0;
+
+    case BTRACE_FORMAT_BTS:
+      return linux_supports_bts ();
+    }
+
+  internal_error (__FILE__, __LINE__, _("Unknown branch trace format"));
+}
+
+/* See linux-btrace.h.  */
+
 struct btrace_target_info *
 linux_enable_btrace (ptid_t ptid)
 {
@@ -602,7 +619,7 @@ linux_read_btrace (struct btrace_data *btrace,
 /* See linux-btrace.h.  */
 
 int
-linux_supports_btrace (struct target_ops *ops)
+linux_supports_btrace (struct target_ops *ops, enum btrace_format format)
 {
   return 0;
 }
diff --git a/gdb/nat/linux-btrace.h b/gdb/nat/linux-btrace.h
index 2a08df7..fc03f4c 100644
--- a/gdb/nat/linux-btrace.h
+++ b/gdb/nat/linux-btrace.h
@@ -61,7 +61,7 @@ struct btrace_target_info
 };
 
 /* See to_supports_btrace in target.h.  */
-extern int linux_supports_btrace (struct target_ops *);
+extern int linux_supports_btrace (struct target_ops *, enum btrace_format);
 
 /* See to_enable_btrace in target.h.  */
 extern struct btrace_target_info *linux_enable_btrace (ptid_t ptid);
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 65d28e3..6bc1d15 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -199,9 +199,6 @@ record_btrace_open (const char *args, int from_tty)
   if (!target_has_execution)
     error (_("The program is not being run."));
 
-  if (!target_supports_btrace ())
-    error (_("Target does not support branch tracing."));
-
   if (non_stop)
     error (_("Record btrace can't debug inferior in non-stop mode."));
 
diff --git a/gdb/remote.c b/gdb/remote.c
index 249deaa..e31698f 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -11324,16 +11324,23 @@ struct btrace_target_info
 /* Check whether the target supports branch tracing.  */
 
 static int
-remote_supports_btrace (struct target_ops *self)
+remote_supports_btrace (struct target_ops *self, enum btrace_format format)
 {
   if (packet_support (PACKET_Qbtrace_off) != PACKET_ENABLE)
     return 0;
-  if (packet_support (PACKET_Qbtrace_bts) != PACKET_ENABLE)
-    return 0;
   if (packet_support (PACKET_qXfer_btrace) != PACKET_ENABLE)
     return 0;
 
-  return 1;
+  switch (format)
+    {
+      case BTRACE_FORMAT_NONE:
+	return 0;
+
+      case BTRACE_FORMAT_BTS:
+	return (packet_support (PACKET_Qbtrace_bts) == PACKET_ENABLE);
+    }
+
+  internal_error (__FILE__, __LINE__, _("Unknown branch trace format"));
 }
 
 /* Enable branch tracing.  */
diff --git a/gdb/target-debug.h b/gdb/target-debug.h
index 60da35c..d243ad6 100644
--- a/gdb/target-debug.h
+++ b/gdb/target-debug.h
@@ -146,6 +146,8 @@
   target_debug_do_print (host_address_to_string (X))
 #define target_debug_print_struct_btrace_data_p(X)	\
   target_debug_do_print (host_address_to_string (X))
+#define target_debug_print_enum_btrace_format(X)	\
+  target_debug_do_print (plongest (X))
 
 static void
 target_debug_print_struct_target_waitstatus_p (struct target_waitstatus *status)
diff --git a/gdb/target-delegates.c b/gdb/target-delegates.c
index 00956ba..9cdb31e 100644
--- a/gdb/target-delegates.c
+++ b/gdb/target-delegates.c
@@ -3085,26 +3085,28 @@ debug_can_use_agent (struct target_ops *self)
 }
 
 static int
-delegate_supports_btrace (struct target_ops *self)
+delegate_supports_btrace (struct target_ops *self, enum btrace_format arg1)
 {
   self = self->beneath;
-  return self->to_supports_btrace (self);
+  return self->to_supports_btrace (self, arg1);
 }
 
 static int
-tdefault_supports_btrace (struct target_ops *self)
+tdefault_supports_btrace (struct target_ops *self, enum btrace_format arg1)
 {
   return 0;
 }
 
 static int
-debug_supports_btrace (struct target_ops *self)
+debug_supports_btrace (struct target_ops *self, enum btrace_format arg1)
 {
   int result;
   fprintf_unfiltered (gdb_stdlog, "-> %s->to_supports_btrace (...)\n", debug_target.to_shortname);
-  result = debug_target.to_supports_btrace (&debug_target);
+  result = debug_target.to_supports_btrace (&debug_target, arg1);
   fprintf_unfiltered (gdb_stdlog, "<- %s->to_supports_btrace (", debug_target.to_shortname);
   target_debug_print_struct_target_ops_p (&debug_target);
+  fputs_unfiltered (", ", gdb_stdlog);
+  target_debug_print_enum_btrace_format (arg1);
   fputs_unfiltered (") = ", gdb_stdlog);
   target_debug_print_int (result);
   fputs_unfiltered ("\n", gdb_stdlog);
diff --git a/gdb/target.c b/gdb/target.c
index 891183f..c847276 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -3355,6 +3355,14 @@ target_ranged_break_num_registers (void)
 
 /* See target.h.  */
 
+int
+target_supports_btrace (enum btrace_format format)
+{
+  return current_target.to_supports_btrace (&current_target, format);
+}
+
+/* See target.h.  */
+
 struct btrace_target_info *
 target_enable_btrace (ptid_t ptid)
 {
diff --git a/gdb/target.h b/gdb/target.h
index dee587f..1654df2 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -999,7 +999,7 @@ struct target_ops
       TARGET_DEFAULT_RETURN (0);
 
     /* Check whether the target supports branch tracing.  */
-    int (*to_supports_btrace) (struct target_ops *)
+    int (*to_supports_btrace) (struct target_ops *, enum btrace_format)
       TARGET_DEFAULT_RETURN (0);
 
     /* Enable branch tracing for PTID and allocate a branch trace target
@@ -2211,8 +2211,7 @@ extern void update_target_permissions (void);
 /* Imported from machine dependent code.  */
 
 /* See to_supports_btrace in struct target_ops.  */
-#define target_supports_btrace() \
-  (current_target.to_supports_btrace (&current_target))
+extern int target_supports_btrace (enum btrace_format);
 
 /* See to_enable_btrace in struct target_ops.  */
 extern struct btrace_target_info *target_enable_btrace (ptid_t ptid);
-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH v2 05/13] record-btrace: add bts buffer size configuration option
  2014-11-20 10:47 [PATCH v2 00/13] record btrace: prepare for a new trace format Markus Metzger
                   ` (8 preceding siblings ...)
  2014-11-20 10:47 ` [PATCH v2 08/13] btrace: move and rename btrace-common Markus Metzger
@ 2014-11-20 10:47 ` Markus Metzger
  2014-11-20 16:25   ` Eli Zaretskii
  2014-11-20 10:47 ` [PATCH v2 06/13] btrace: update btrace_compute_ftrace parameters Markus Metzger
                   ` (2 subsequent siblings)
  12 siblings, 1 reply; 25+ messages in thread
From: Markus Metzger @ 2014-11-20 10:47 UTC (permalink / raw)
  To: palves; +Cc: gdb-patches, Eli Zaretskii

Allow the size of the branch trace ring buffer to be defined by the
user.  The specified buffer size will be used when BTS tracing is
enabled for new threads.

The obtained buffer size may differ from the requested size.  The
actual buffer size for the current thread is shown in the "info record"
command.

Bigger buffers mean longer traces, but also longer processing time.

CC: Eli Zaretskii  <eliz@gnu.org>

2014-11-20  Markus Metzger  <markus.t.metzger@intel.com>

	* btrace.c (parse_xml_btrace_conf_bts): Add size.
	(btrace_conf_bts_attributes): New.
	(btrace_conf_children): Add attributes.
	* common/btrace-common.h (btrace_config_bts): New.
	(btrace_config)<bts>: New.
	(btrace_config): Update comment.
	* nat/linux-btrace.c (linux_enable_btrace): Use config.
	* features/btrace-conf.dtd: Increment version.  Add size
	attribute to bts element.
	* record-btrace.c (set_record_btrace_bts_cmdlist,
	show_record_btrace_bts_cmdlist): New.
	(record_btrace_adjust_size, record_btrace_print_bts_conf,
	record_btrace_print_conf, cmd_set_record_btrace_bts,
	cmd_show_record_btrace_bts): New.
	(record_btrace_info): Call record_btrace_print_conf.
	(_initialize_record_btrace): Add commands.
	* remote.c: Add PACKET_Qbtrace_conf_bts_size enum.
	(remote_protocol_features): Add Qbtrace-conf:bts:size packet.
	(btrace_sync_conf): Synchronize bts size.
	(_initialize_remote): Add Qbtrace-conf:bts:size packet.
	* NEWS: Announce new commands and new packets.

doc/
	* gdb.texinfo (Branch Trace Configuration Format): Add size.
	(Process Record and Replay): Describe new set|show commands.
	(General Query Packets): Describe Qbtrace-conf:bts:size packet.

testsuite/
	* gdb.btrace/buffer-size: New.

gdbserver/
	* linux-low.c (linux_low_btrace_conf): Print size.
	* server.c (handle_btrace_conf_general_set): New.
	(hanle_general_set): Call handle_btrace_conf_general_set.
	(handle_query): Report Qbtrace-conf:bts:size as supported.
---
 gdb/NEWS                                 |  16 +++++
 gdb/btrace.c                             |  14 +++-
 gdb/common/btrace-common.h               |  15 +++-
 gdb/doc/gdb.texinfo                      |  77 ++++++++++++++++++--
 gdb/features/btrace-conf.dtd             |   1 +
 gdb/gdbserver/linux-low.c                |   4 +-
 gdb/gdbserver/server.c                   |  60 +++++++++++++++-
 gdb/nat/linux-btrace.c                   |  34 ++++++---
 gdb/record-btrace.c                      | 120 ++++++++++++++++++++++++++++++-
 gdb/remote.c                             |  40 ++++++++++-
 gdb/testsuite/gdb.btrace/buffer-size.exp |  57 +++++++++++++++
 11 files changed, 418 insertions(+), 20 deletions(-)
 create mode 100644 gdb/testsuite/gdb.btrace/buffer-size.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index 689eaa1..b19faf5 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -31,6 +31,14 @@ record btrace bts
 record bts
   Start branch trace recording using Intel(R) Branch Trace Store format.
 
+* New options
+
+set|show record btrace bts buffer-size
+  Set and show the size of the ring buffer used for branch tracing in
+  BTS format.
+  The obtained size may differ from the requested size.  Use "info
+  record" to see the obtained buffer size.
+
 * On resume, GDB now always passes the signal the program had stopped
   for to the thread the signal was sent to, even if the user changed
   threads before resuming.  Previously GDB would often (but not
@@ -69,6 +77,14 @@ VAX running Ultrix 			vax-*-ultrix*
 qXfer:btrace-conf:read
   Return the branch trace configuration for the current thread.
 
+Qbtrace-conf:bts:size
+  Set the requested ring buffer size for branch tracing in BTS format.
+
+* The info record command now shows the recording format and the
+  branch tracing configuration for the current thread when using
+  the btrace record target.
+  For the BTS format, it shows the ring buffer size.
+
 *** Changes in GDB 7.8
 
 * New command line options
diff --git a/gdb/btrace.c b/gdb/btrace.c
index 6b50be7..3e9020a 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -1127,13 +1127,25 @@ 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->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;
 }
 
+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 },
   { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
 };
 
diff --git a/gdb/common/btrace-common.h b/gdb/common/btrace-common.h
index 1e0e595..8147e6d 100644
--- a/gdb/common/btrace-common.h
+++ b/gdb/common/btrace-common.h
@@ -62,15 +62,28 @@ enum btrace_format
   BTRACE_FORMAT_BTS
 };
 
+/* A BTS configuration.  */
+
+struct btrace_config_bts
+{
+  /* The size of the branch trace buffer in bytes.  */
+  unsigned int size;
+};
+
 /* A branch tracing configuration.
 
    This describes the requested configuration as well as the actually
-   obtained configuration.  */
+   obtained configuration.
+   We describe the configuration for all different formats so we can
+   easily switch between formats.  */
 
 struct btrace_config
 {
   /* The branch tracing format.  */
   enum btrace_format format;
+
+  /* The BTS format configuration.  */
+  struct btrace_config_bts bts;
 };
 
 /* Branch trace in BTS format.  */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 121ae5b..0dcac9b 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6525,6 +6525,29 @@ position.
 @item show record btrace replay-memory-access
 Show the current setting of @code{replay-memory-access}.
 
+@kindex set record btrace bts
+@item set record btrace bts buffer-size @var{size}
+@itemx set record btrace bts buffer-size unlimited
+Set the requested ring buffer size for branch tracing in BTS format.
+Default is 64KB.
+
+If @var{size} is a positive number, then @value{GDBN} will try to
+allocate a buffer of at least @var{size} bytes for each new thread
+that uses the btrace recording method and the BTS format.  The actually
+obtained buffer size may differ from the requested @var{size}. Use the
+@code{info record} command to see the actual buffer size for each
+thread that uses the btrace recording method and the BTS format.
+
+If @var{limit} is @code{unlimited} or zero, @value{GDBN} will try to
+allocate a buffer of 4MB.
+
+Bigger buffers mean longer traces.  On the other hand, @value{GDBN} will
+also need longer to process the branch trace data before it can be used.
+
+@item show record btrace bts buffer-size @var{size}
+Show the current setting of the requested ring buffer size for branch
+tracing in BTS format.
+
 @kindex info record
 @item info record
 Show various statistics about the recording depending on the recording
@@ -6551,9 +6574,25 @@ Maximum number of instructions that may be contained in the execution log.
 @end itemize
 
 @item btrace
-For the @code{btrace} recording method, it shows the recording format,
-the number of instructions that have been recorded and the number of blocks
-of sequential control-flow that is formed by the recorded instructions.
+For the @code{btrace} recording method, it shows:
+
+@itemize @bullet
+@item
+Recording format.
+@item
+Number of instructions that have been recorded.
+@item
+Number of blocks of sequential control-flow formed by the recorded
+instructions.
+@item
+Whether in record mode or replay mode.
+@end itemize
+
+For the @code{bts} recording format, it also shows:
+@itemize @bullet
+@item
+Size of the perf ring buffer.
+@end itemize
 @end table
 
 @kindex record delete
@@ -35451,6 +35490,11 @@ These are the currently defined stub features and their properties:
 @tab @samp{-}
 @tab Yes
 
+@item @samp{Qbtrace-conf:bts:size}
+@tab Yes
+@tab @samp{-}
+@tab Yes
+
 @item @samp{QNonStop}
 @tab No
 @tab @samp{-}
@@ -35708,6 +35752,9 @@ The remote stub understands the @samp{Qbtrace:off} packet.
 @item Qbtrace:bts
 The remote stub understands the @samp{Qbtrace:bts} packet.
 
+@item Qbtrace-conf:bts:size
+The remote stub understands the @samp{Qbtrace-conf:bts:size} packet.
+
 @end table
 
 @item qSymbol::
@@ -36147,6 +36194,18 @@ Branch tracing has been disabled.
 A badly formed request or an error was encountered.
 @end table
 
+@item Qbtrace-conf:bts:size=@var{value}
+Set the requested ring buffer size for new threads that use the
+btrace recording method in bts format.
+
+Reply:
+@table @samp
+@item OK
+The ring buffer size has been set.
+@item E.errtext
+A badly formed request or an error was encountered.
+@end table
+
 @end table
 
 @node Architecture-Specific Protocol Details
@@ -38675,7 +38734,16 @@ configuration using the @samp{qXfer:btrace-conf:read}
 (@pxref{qXfer btrace-conf read}) packet.
 
 The configuration describes the branch trace format and configuration
-settings for that format.
+settings for that format.  The following information is described:
+
+@table @code
+@item bts
+This thread uses the Branch Trace Store (BTS) format.
+@table @code
+@item size
+The size of the BTS ring buffer in bytes.
+@end table
+@end table
 
 @value{GDBN} must be linked with the Expat library to support XML
 branch trace configuration discovery.  @xref{Expat}.
@@ -38687,6 +38755,7 @@ The formal DTD for the branch trace configuration format is given below:
 <!ATTLIST btrace-conf	version	CDATA	#FIXED "1.0">
 
 <!ELEMENT bts	EMPTY>
+<!ATTLIST bts	size	CDATA	#IMPLIED>
 @end smallexample
 
 @include agentexpr.texi
diff --git a/gdb/features/btrace-conf.dtd b/gdb/features/btrace-conf.dtd
index e9cb2ef..907816c 100644
--- a/gdb/features/btrace-conf.dtd
+++ b/gdb/features/btrace-conf.dtd
@@ -8,3 +8,4 @@
 <!ATTLIST btrace-conf	version	CDATA	#FIXED "1.0">
 
 <!ELEMENT bts	EMPTY>
+<!ATTLIST bts	size	CDATA	#IMPLIED>
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 8a706a8..08192c5 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -6038,7 +6038,9 @@ linux_low_btrace_conf (const struct btrace_target_info *tinfo,
 	  break;
 
 	case BTRACE_FORMAT_BTS:
-	  buffer_xml_printf (buffer, "<bts/>\n");
+	  buffer_xml_printf (buffer, "<bts");
+	  buffer_xml_printf (buffer, " size=\"0x%x\"", conf->bts.size);
+	  buffer_xml_printf (buffer, " />\n");
 	  break;
 	}
     }
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index b8359e6..ebacd96 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -463,6 +463,58 @@ handle_btrace_general_set (char *own_buf)
   return 1;
 }
 
+/* Handle the "Qbtrace-conf" packet.  */
+
+static int
+handle_btrace_conf_general_set (char *own_buf)
+{
+  struct thread_info *thread;
+  char *op;
+
+  if (strncmp ("Qbtrace-conf:", own_buf, strlen ("Qbtrace-conf:")) != 0)
+    return 0;
+
+  op = own_buf + strlen ("Qbtrace-conf:");
+
+  if (ptid_equal (general_thread, null_ptid)
+      || ptid_equal (general_thread, minus_one_ptid))
+    {
+      strcpy (own_buf, "E.Must select a single thread.");
+      return -1;
+    }
+
+  thread = find_thread_ptid (general_thread);
+  if (thread == NULL)
+    {
+      strcpy (own_buf, "E.No such thread.");
+      return -1;
+    }
+
+  if (strncmp (op, "bts:size=", strlen ("bts:size=")) == 0)
+    {
+      unsigned long size;
+      char *endp = NULL;
+
+      errno = 0;
+      size = strtoul (op + strlen ("bts:size="), &endp, 16);
+      if (endp == NULL || *endp != 0 || errno != 0 || size > UINT_MAX)
+	{
+	  strcpy (own_buf, "E.Bad size value.");
+	  return -1;
+	}
+
+      current_btrace_conf.bts.size = (unsigned int) size;
+    }
+  else
+    {
+      strcpy (own_buf, "E.Bad Qbtrace configuration option.");
+      return -1;
+    }
+
+  write_ok (own_buf);
+  return 1;
+}
+
 /* Handle all of the extended 'Q' packets.  */
 
 static void
@@ -622,6 +674,9 @@ handle_general_set (char *own_buf)
   if (handle_btrace_general_set (own_buf))
     return;
 
+  if (handle_btrace_conf_general_set (own_buf))
+    return;
+
   /* Otherwise we didn't know what packet it was.  Say we didn't
      understand it.  */
   own_buf[0] = 0;
@@ -1761,7 +1816,10 @@ static void
 supported_btrace_packets (char *buf)
 {
   if (target_supports_btrace (BTRACE_FORMAT_BTS))
-    strcat (buf, ";Qbtrace:bts+");
+    {
+      strcat (buf, ";Qbtrace:bts+");
+      strcat (buf, ";Qbtrace-conf:bts:size+");
+    }
   else
     return;
 
diff --git a/gdb/nat/linux-btrace.c b/gdb/nat/linux-btrace.c
index c369483..66f37ea 100644
--- a/gdb/nat/linux-btrace.c
+++ b/gdb/nat/linux-btrace.c
@@ -398,11 +398,12 @@ linux_supports_btrace (struct target_ops *ops, enum btrace_format format)
 /* Enable branch tracing in BTS format.  */
 
 static struct btrace_target_info *
-linux_enable_bts (ptid_t ptid, const struct btrace_config *conf)
+linux_enable_bts (ptid_t ptid, const struct btrace_config_bts *conf)
 {
   struct perf_event_mmap_page *header;
   struct btrace_target_info *tinfo;
   struct btrace_tinfo_bts *bts;
+  unsigned int pages, size;
   int pid, pg;
 
   tinfo = xzalloc (sizeof (*tinfo));
@@ -428,18 +429,34 @@ linux_enable_bts (ptid_t ptid, const struct btrace_config *conf)
   if (pid == 0)
     pid = ptid_get_pid (ptid);
 
+  /* Cast to ULONGEST to avoid overflows before we divide by PAGE_SIZE.  */
+  size = (unsigned int) (((ULONGEST) conf->size + PAGE_SIZE - 1) / PAGE_SIZE);
+  if (size == 0)
+    {
+      errno = EINVAL;
+      goto err;
+    }
+
   errno = 0;
   bts->file = syscall (SYS_perf_event_open, &bts->attr, pid, -1, -1, 0);
   if (bts->file < 0)
     goto err;
 
-  /* We try to allocate as much buffer as we can get.
-     We could allow the user to specify the size of the buffer, but then
-     we'd leave this search for the maximum buffer size to him.  */
-  for (pg = 4; pg >= 0; --pg)
+  /* The buffer size can be requested in powers of two pages.
+     We track the number of pages we request in PAGES and its base-2 log
+     in PG.  */
+
+  /* Adjust size to the next power of two.  */
+  for (pg = 0, pages = 1; pages != size; ++pg, pages = (1u << pg))
+    if ((pages & size) != 0)
+      size += pages;
+
+  /* We try to allocate the requested size.
+     If that fails, try to get as much as we can.  */
+  for (; size > 0; size >>= 1)
     {
       /* The number of pages we request needs to be a power of two.  */
-      header = mmap (NULL, ((1 << pg) + 1) * PAGE_SIZE, PROT_READ, MAP_SHARED,
+      header = mmap (NULL, (size + 1) * PAGE_SIZE, PROT_READ, MAP_SHARED,
 		     bts->file, 0);
       if (header != MAP_FAILED)
 	break;
@@ -450,10 +467,11 @@ linux_enable_bts (ptid_t ptid, const struct btrace_config *conf)
 
   bts->header = header;
   bts->bts.mem = ((const uint8_t *) header) + PAGE_SIZE;
-  bts->bts.size = (1 << pg) * PAGE_SIZE;
+  bts->bts.size = size * PAGE_SIZE;
   bts->bts.data_head = &header->data_head;
   bts->bts.last_head = 0;
 
+  tinfo->conf.bts.size = bts->bts.size;
   return tinfo;
 
  err_file:
@@ -479,7 +497,7 @@ linux_enable_btrace (ptid_t ptid, const struct btrace_config *conf)
       break;
 
     case BTRACE_FORMAT_BTS:
-      tinfo = linux_enable_bts (ptid, conf);
+      tinfo = linux_enable_bts (ptid, &conf->bts);
       break;
     }
 
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index d9ad9d4..d9c1456 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -76,6 +76,14 @@ static struct btrace_config record_btrace_conf;
 /* Command list for "record btrace".  */
 static struct cmd_list_element *record_btrace_cmdlist;
 
+/* Command lists for "set/show record btrace".  */
+static struct cmd_list_element *set_record_btrace_cmdlist;
+static struct cmd_list_element *show_record_btrace_cmdlist;
+
+/* Command lists for "set/show record btrace bts".  */
+static struct cmd_list_element *set_record_btrace_bts_cmdlist;
+static struct cmd_list_element *show_record_btrace_bts_cmdlist;
+
 /* Print a record-btrace debug message.  Use do ... while (0) to avoid
    ambiguities when used in if statements.  */
 
@@ -269,6 +277,71 @@ record_btrace_close (struct target_ops *self)
     btrace_teardown (tp);
 }
 
+/* Adjusts the size and returns a human readable size suffix.  */
+
+static const char *
+record_btrace_adjust_size (unsigned int *size)
+{
+  unsigned int sz;
+
+  sz = *size;
+
+  if ((sz & ((1u << 30) - 1)) == 0)
+    {
+      *size = sz >> 30;
+      return "GB";
+    }
+  else if ((sz & ((1u << 20) - 1)) == 0)
+    {
+      *size = sz >> 20;
+      return "MB";
+    }
+  else if ((sz & ((1u << 10) - 1)) == 0)
+    {
+      *size = sz >> 10;
+      return "kB";
+    }
+  else
+    return "";
+}
+
+/* Print a BTS configuration.  */
+
+static void
+record_btrace_print_bts_conf (const struct btrace_config_bts *conf)
+{
+  const char *suffix;
+  unsigned int size;
+
+  size = conf->size;
+  if (size > 0)
+    {
+      suffix = record_btrace_adjust_size (&size);
+      printf_unfiltered (_("Buffer size: %u%s.\n"), size, suffix);
+    }
+}
+
+/* Print a branch tracing configuration.  */
+
+static void
+record_btrace_print_conf (const struct btrace_config *conf)
+{
+  printf_unfiltered (_("Recording format: %s.\n"),
+		     btrace_format_string (conf->format));
+
+  switch (conf->format)
+    {
+    case BTRACE_FORMAT_NONE:
+      return;
+
+    case BTRACE_FORMAT_BTS:
+      record_btrace_print_bts_conf (&conf->bts);
+      return;
+    }
+
+  internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
+}
+
 /* The to_info_record method of target record-btrace.  */
 
 static void
@@ -289,8 +362,7 @@ record_btrace_info (struct target_ops *self)
 
   conf = btrace_conf (btinfo);
   if (conf != NULL)
-    printf_unfiltered (_("Recording format: %s.\n"),
-		       btrace_format_string (conf->format));
+    record_btrace_print_conf (conf);
 
   btrace_fetch (tp);
 
@@ -2056,6 +2128,25 @@ cmd_show_replay_memory_access (struct ui_file *file, int from_tty,
 		    replay_memory_access);
 }
 
+/* The "set record btrace bts" command.  */
+
+static void
+cmd_set_record_btrace_bts (char *args, int from_tty)
+{
+  printf_unfiltered (_("\"set record btrace bts\" must be followed "
+		       "by an apporpriate subcommand.\n"));
+  help_list (set_record_btrace_bts_cmdlist, "set record btrace bts ",
+	     all_commands, gdb_stdout);
+}
+
+/* The "show record btrace bts" command.  */
+
+static void
+cmd_show_record_btrace_bts (char *args, int from_tty)
+{
+  cmd_show_list (show_record_btrace_bts_cmdlist, from_tty, "");
+}
+
 void _initialize_record_btrace (void);
 
 /* Initialize btrace commands.  */
@@ -2099,9 +2190,34 @@ replay."),
 			   &set_record_btrace_cmdlist,
 			   &show_record_btrace_cmdlist);
 
+  add_prefix_cmd ("bts", class_support, cmd_set_record_btrace_bts,
+		  _("Set record btrace bts options"),
+		  &set_record_btrace_bts_cmdlist,
+		  "set record btrace bts ", 0, &set_record_btrace_cmdlist);
+
+  add_prefix_cmd ("bts", class_support, cmd_show_record_btrace_bts,
+		  _("Show record btrace bts options"),
+		  &show_record_btrace_bts_cmdlist,
+		  "show record btrace bts ", 0, &show_record_btrace_cmdlist);
+
+  add_setshow_uinteger_cmd ("buffer-size", no_class,
+			    &record_btrace_conf.bts.size,
+			    _("Set the record/replay bts buffer size."),
+			    _("Show the record/replay bts buffer size."), _("\
+When starting recording request a trace buffer of this size.  \
+The actual buffer size may differ from the requested size.  \
+Use \"info record\" to see the actual buffer size.\n\n\
+Bigger buffers allow longer recording but also take more time to process \
+the recorded execution trace.\n\n\
+The trace buffer size may not be changed while recording."), NULL, NULL,
+			    &set_record_btrace_bts_cmdlist,
+			    &show_record_btrace_bts_cmdlist);
+
   init_record_btrace_ops ();
   add_target (&record_btrace_ops);
 
   bfcache = htab_create_alloc (50, bfcache_hash, bfcache_eq, NULL,
 			       xcalloc, xfree);
+
+  record_btrace_conf.bts.size = 64 * 1024;
 }
diff --git a/gdb/remote.c b/gdb/remote.c
index 16cb60f..e97a9f5 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1335,6 +1335,9 @@ enum {
   /* Support for the qXfer:btrace-conf:read packet.  */
   PACKET_qXfer_btrace_conf,
 
+  /* Support for the Qbtrace-conf:bts:size packet.  */
+  PACKET_Qbtrace_conf_bts_size,
+
   PACKET_MAX
 };
 
@@ -4020,7 +4023,9 @@ static const struct protocol_feature remote_protocol_features[] = {
   { "qXfer:btrace:read", PACKET_DISABLE, remote_supported_packet,
     PACKET_qXfer_btrace },
   { "qXfer:btrace-conf:read", PACKET_DISABLE, remote_supported_packet,
-    PACKET_qXfer_btrace_conf }
+    PACKET_qXfer_btrace_conf },
+  { "Qbtrace-conf:bts:size", PACKET_DISABLE, remote_supported_packet,
+    PACKET_Qbtrace_conf_bts_size }
 };
 
 static char *remote_support_xml;
@@ -11378,7 +11383,35 @@ remote_supports_btrace (struct target_ops *self, enum btrace_format format)
 static void
 btrace_sync_conf (const struct btrace_config *conf)
 {
-  /* Nothing to do for now.  */
+  struct packet_config *packet;
+  struct remote_state *rs;
+  char *buf, *pos, *endbuf;
+
+  rs = get_remote_state ();
+  buf = rs->buf;
+  endbuf = buf + get_remote_packet_size ();
+
+  packet = &remote_protocol_packets[PACKET_Qbtrace_conf_bts_size];
+  if (packet_config_support (packet) == PACKET_ENABLE
+      && conf->bts.size != rs->btrace_config.bts.size)
+    {
+      pos = buf;
+      pos += xsnprintf (pos, endbuf - pos, "%s=0x%x", packet->name,
+                        conf->bts.size);
+
+      putpkt (buf);
+      getpkt (&buf, &rs->buf_size, 0);
+
+      if (packet_ok (buf, packet) == PACKET_ERROR)
+	{
+	  if (buf[0] == 'E' && buf[1] == '.')
+	    error (_("Failed to configure the BTS buffer size: %s"), buf + 2);
+	  else
+	    error (_("Failed to configure the BTS buffer size."));
+	}
+
+      rs->btrace_config.bts.size = conf->bts.size;
+    }
 }
 
 /* Read the current thread's btrace configuration from the target and
@@ -12279,6 +12312,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_btrace_conf],
        "qXfer:btrace-conf", "read-btrace-conf", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_Qbtrace_conf_bts_size],
+       "Qbtrace-conf:bts:size", "btrace-conf-bts-size", 0);
+
   /* Assert that we've registered commands for all packet configs.  */
   {
     int i;
diff --git a/gdb/testsuite/gdb.btrace/buffer-size.exp b/gdb/testsuite/gdb.btrace/buffer-size.exp
new file mode 100644
index 0000000..9d36361
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/buffer-size.exp
@@ -0,0 +1,57 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013-2014 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile x86-record_goto.S
+if [prepare_for_testing $testfile.exp $testfile $srcfile] {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_test_no_output "set record btrace bts buffer-size 1"
+gdb_test "show record btrace bts buffer-size" "The record/replay bts buffer size is 1\.\r" "bts buffer size before recording"
+
+gdb_test_no_output "record btrace bts"
+gdb_test "show record btrace bts buffer-size" "The record/replay bts buffer size is 1\.\r" "bts buffer size while recording"
+gdb_test "info record" [join [list \
+  "Active record target: record-btrace" \
+  "Recording format: Intel\\\(R\\\) Branch Trace Store\." \
+  "Buffer size: 4kB\." \
+  "Recorded 0 instructions in 0 functions for \[^\\\r\\\n\]*" \
+  ] "\r\n"] "info record with small bts buffer"
+gdb_test "record stop" ".*" "stop recording with small bts buffer"
+
+gdb_test_no_output "set record btrace bts buffer-size 0"
+gdb_test "show record btrace bts buffer-size" "The record/replay bts buffer size is unlimited\.\r" "unlimited bts buffer size before recording"
+
+gdb_test_no_output "record btrace bts"
+gdb_test "show record btrace bts buffer-size" "The record/replay bts buffer size is unlimited\.\r" "unlimited bts buffer size while recording"
+gdb_test "info record" [join [list \
+  "Active record target: record-btrace" \
+  "Recording format: Intel\\\(R\\\) Branch Trace Store\." \
+  "Buffer size: .*\." \
+  "Recorded 0 instructions in 0 functions for \[^\\\r\\\n\]*" \
+  ] "\r\n"] "info record with unlimited bts buffer"
+gdb_test "record stop" ".*" "stop recording with unlimited bts buffer"
-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH v2 06/13] btrace: update btrace_compute_ftrace parameters
  2014-11-20 10:47 [PATCH v2 00/13] record btrace: prepare for a new trace format Markus Metzger
                   ` (9 preceding siblings ...)
  2014-11-20 10:47 ` [PATCH v2 05/13] record-btrace: add bts buffer size configuration option Markus Metzger
@ 2014-11-20 10:47 ` Markus Metzger
  2014-11-20 10:47 ` [PATCH v2 01/13] btrace: add struct btrace_data Markus Metzger
  2014-11-20 10:47 ` [PATCH v2 13/13] [wip] btrace: support Intel(R) Processor Trace Markus Metzger
  12 siblings, 0 replies; 25+ messages in thread
From: Markus Metzger @ 2014-11-20 10:47 UTC (permalink / raw)
  To: palves; +Cc: gdb-patches

Pass thread_info instead of btrace_thread_info to btrace_compute_ftrace.
We will need the thread_info in subsequent patches.

2014-11-20  Markus Metzger  <markus.t.metzger@intel.com>

	* btrace.c (btrace_compute_ftrace_bts, btrace_compute_ftrace):
	Update parameters.  Update users.
---
 gdb/btrace.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/gdb/btrace.c b/gdb/btrace.c
index 3e9020a..d261d57 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -588,15 +588,17 @@ ftrace_update_insns (struct btrace_function *bfun, CORE_ADDR pc)
 /* Compute the function branch trace from BTS trace.  */
 
 static void
-btrace_compute_ftrace_bts (struct btrace_thread_info *btinfo,
+btrace_compute_ftrace_bts (struct thread_info *tp,
 			   const struct btrace_data_bts *btrace)
 {
+  struct btrace_thread_info *btinfo;
   struct btrace_function *begin, *end;
   struct gdbarch *gdbarch;
   unsigned int blk;
   int level;
 
   gdbarch = target_gdbarch ();
+  btinfo = &tp->btrace;
   begin = btinfo->begin;
   end = btinfo->end;
   level = begin != NULL ? -btinfo->level : INT_MAX;
@@ -676,8 +678,7 @@ btrace_compute_ftrace_bts (struct btrace_thread_info *btinfo,
    a thread given by BTINFO.  */
 
 static void
-btrace_compute_ftrace (struct btrace_thread_info *btinfo,
-		       struct btrace_data *btrace)
+btrace_compute_ftrace (struct thread_info *tp, struct btrace_data *btrace)
 {
   DEBUG ("compute ftrace");
 
@@ -687,7 +688,7 @@ btrace_compute_ftrace (struct btrace_thread_info *btinfo,
       return;
 
     case BTRACE_FORMAT_BTS:
-      btrace_compute_ftrace_bts (btinfo, &btrace->variant.bts);
+      btrace_compute_ftrace_bts (tp, &btrace->variant.bts);
       return;
     }
 
@@ -718,7 +719,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);
 }
@@ -964,7 +965,7 @@ btrace_fetch (struct thread_info *tp)
   if (!btrace_data_empty (&btrace))
     {
       btrace_clear_history (btinfo);
-      btrace_compute_ftrace (btinfo, &btrace);
+      btrace_compute_ftrace (tp, &btrace);
     }
 
   do_cleanups (cleanup);
-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH v2 10/13] btrace: use the new cpu identifier
  2014-11-20 10:47 [PATCH v2 00/13] record btrace: prepare for a new trace format Markus Metzger
                   ` (3 preceding siblings ...)
  2014-11-20 10:47 ` [PATCH v2 04/13] record btrace: add configuration struct Markus Metzger
@ 2014-11-20 10:47 ` Markus Metzger
  2014-11-20 10:47 ` [PATCH v2 11/13] record-btrace: indicate gaps Markus Metzger
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 25+ messages in thread
From: Markus Metzger @ 2014-11-20 10:47 UTC (permalink / raw)
  To: palves; +Cc: gdb-patches

The preceding patch added a cpu identifier.  Use it.

2014-11-20  Markus Metzger  <markus.t.metzger@intel.com>

	* nat/linux-btrace.c: Remove include x86-cpuid.h.
	(intel_supports_bts): Update parameters.
	(cpu_supports_bts): Call btrace_this_cpu.
---
 gdb/nat/linux-btrace.c | 36 +++++++++++++-----------------------
 1 file changed, 13 insertions(+), 23 deletions(-)

diff --git a/gdb/nat/linux-btrace.c b/gdb/nat/linux-btrace.c
index 66f37ea..6948522 100644
--- a/gdb/nat/linux-btrace.c
+++ b/gdb/nat/linux-btrace.c
@@ -23,7 +23,6 @@
 #include "linux-btrace.h"
 #include "common-regcache.h"
 #include "gdb_wait.h"
-#include "x86-cpuid.h"
 
 #ifdef HAVE_SYS_SYSCALL_H
 #include <sys/syscall.h>
@@ -302,22 +301,12 @@ kernel_supports_bts (void)
 /* Check whether an Intel cpu supports BTS.  */
 
 static int
-intel_supports_bts (void)
+intel_supports_bts (const struct btrace_cpu *cpu)
 {
-  unsigned int cpuid, model, family;
-
-  if (!x86_cpuid (1, &cpuid, NULL, NULL, NULL))
-    return 0;
-
-  family = (cpuid >> 8) & 0xf;
-  model = (cpuid >> 4) & 0xf;
-
-  switch (family)
+  switch (cpu->family)
     {
     case 0x6:
-      model += (cpuid >> 12) & 0xf0;
-
-      switch (model)
+      switch (cpu->model)
 	{
 	case 0x1a: /* Nehalem */
 	case 0x1f:
@@ -345,17 +334,18 @@ intel_supports_bts (void)
 static int
 cpu_supports_bts (void)
 {
-  unsigned int ebx, ecx, edx;
+  struct btrace_cpu cpu;
 
-  if (!x86_cpuid (0, NULL, &ebx, &ecx, &edx))
-    return 0;
-
-  if (ebx == signature_INTEL_ebx && ecx == signature_INTEL_ecx
-      && edx == signature_INTEL_edx)
-    return intel_supports_bts ();
+  cpu = btrace_this_cpu ();
+  switch (cpu.vendor)
+    {
+    default:
+      /* Don't know about others.  Let's assume they do.  */
+      return 1;
 
-  /* Don't know about others.  Let's assume they do.  */
-  return 1;
+    case CV_INTEL:
+      return intel_supports_bts (&cpu);
+    }
 }
 
 /* Check whether the linux target supports BTS.  */
-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 05/13] record-btrace: add bts buffer size configuration option
  2014-11-20 10:47 ` [PATCH v2 05/13] record-btrace: add bts buffer size configuration option Markus Metzger
@ 2014-11-20 16:25   ` Eli Zaretskii
  0 siblings, 0 replies; 25+ messages in thread
From: Eli Zaretskii @ 2014-11-20 16:25 UTC (permalink / raw)
  To: Markus Metzger; +Cc: palves, gdb-patches

> From: Markus Metzger <markus.t.metzger@intel.com>
> Cc: gdb-patches@sourceware.org, Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 20 Nov 2014 11:47:16 +0100
> 
> Allow the size of the branch trace ring buffer to be defined by the
> user.  The specified buffer size will be used when BTS tracing is
> enabled for new threads.
> 
> The obtained buffer size may differ from the requested size.  The
> actual buffer size for the current thread is shown in the "info record"
> command.
> 
> Bigger buffers mean longer traces, but also longer processing time.
> 
> CC: Eli Zaretskii  <eliz@gnu.org>
> 
> 2014-11-20  Markus Metzger  <markus.t.metzger@intel.com>
> 
> 	* btrace.c (parse_xml_btrace_conf_bts): Add size.
> 	(btrace_conf_bts_attributes): New.
> 	(btrace_conf_children): Add attributes.
> 	* common/btrace-common.h (btrace_config_bts): New.
> 	(btrace_config)<bts>: New.
> 	(btrace_config): Update comment.
> 	* nat/linux-btrace.c (linux_enable_btrace): Use config.
> 	* features/btrace-conf.dtd: Increment version.  Add size
> 	attribute to bts element.
> 	* record-btrace.c (set_record_btrace_bts_cmdlist,
> 	show_record_btrace_bts_cmdlist): New.
> 	(record_btrace_adjust_size, record_btrace_print_bts_conf,
> 	record_btrace_print_conf, cmd_set_record_btrace_bts,
> 	cmd_show_record_btrace_bts): New.
> 	(record_btrace_info): Call record_btrace_print_conf.
> 	(_initialize_record_btrace): Add commands.
> 	* remote.c: Add PACKET_Qbtrace_conf_bts_size enum.
> 	(remote_protocol_features): Add Qbtrace-conf:bts:size packet.
> 	(btrace_sync_conf): Synchronize bts size.
> 	(_initialize_remote): Add Qbtrace-conf:bts:size packet.
> 	* NEWS: Announce new commands and new packets.
> 
> doc/
> 	* gdb.texinfo (Branch Trace Configuration Format): Add size.
> 	(Process Record and Replay): Describe new set|show commands.
> 	(General Query Packets): Describe Qbtrace-conf:bts:size packet.
> 
> testsuite/
> 	* gdb.btrace/buffer-size: New.
> 
> gdbserver/
> 	* linux-low.c (linux_low_btrace_conf): Print size.
> 	* server.c (handle_btrace_conf_general_set): New.
> 	(hanle_general_set): Call handle_btrace_conf_general_set.
> 	(handle_query): Report Qbtrace-conf:bts:size as supported.

OK for the documentation parts.

Thanks.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 13/13] [wip] btrace: support Intel(R) Processor Trace
  2014-11-20 10:47 ` [PATCH v2 13/13] [wip] btrace: support Intel(R) Processor Trace Markus Metzger
@ 2014-11-20 16:32   ` Eli Zaretskii
  0 siblings, 0 replies; 25+ messages in thread
From: Eli Zaretskii @ 2014-11-20 16:32 UTC (permalink / raw)
  To: Markus Metzger; +Cc: palves, gdb-patches

> From: Markus Metzger <markus.t.metzger@intel.com>
> Cc: gdb-patches@sourceware.org
> Date: Thu, 20 Nov 2014 11:47:24 +0100
> 
> Adds a new command "record btrace pt" to configure the kernel to use
> Intel Processor Trace instead of Intel Branch Trace Strore.

Thanks.

> +      warning (_("decode error near offset 0x%" PRIx64

Warning messages like this are sub-optimal, because they don't give
enough context to understand what happens.  The user will likely
wonder what was GDB decoding when it hit that error.

> +    error (_("Failed to configure trace decoder: %s."),
> +	   pt_errstr (pt_errcode (errcode)));

Likewise here: what "trace decoder" is being referred to?

I suggest to add text that provides the missing context, like "Failed
to configure trace decoder for Intel Process Trace."

> +If @var{size} is a positive number, then @value{GDBN} will try to
> +allocate a buffer of at least @var{size} bytes for each new thread
> +that uses the btrace recording method and the Intel Processor Trace
> +format.  The actually obtained buffer size may differ from the
> +requested @var{size}. Use the @code{info record} command to see the
                       ^^
Two spaces here.

The documentation parts are OK with that fixed.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* RE: [PATCH v2 11/13] record-btrace: indicate gaps
  2014-11-20 10:47 ` [PATCH v2 11/13] record-btrace: indicate gaps Markus Metzger
@ 2014-11-20 17:32   ` Metzger, Markus T
  2014-11-20 17:36     ` Eli Zaretskii
  0 siblings, 1 reply; 25+ messages in thread
From: Metzger, Markus T @ 2014-11-20 17:32 UTC (permalink / raw)
  To: Eli Zaretskii (eliz@gnu.org); +Cc: gdb-patches, palves

Hello Eli,

I forgot to CC you on this patch.  It contains some user-visible strings.

Thanks for your feedback on the other patch.

Thanks,
Markus.

> -----Original Message-----
> From: Metzger, Markus T
> Sent: Thursday, November 20, 2014 11:47 AM
> To: palves@redhat.com
> Cc: gdb-patches@sourceware.org
> Subject: [PATCH v2 11/13] record-btrace: indicate gaps
> 
> Indicate gaps in the trace due to decode errors.  Internally, a gap is
> represented as a btrace function segment without instructions and with a
> non-zero format-specific error code.
> 
> Show the gap when traversing the instruction or function call history.
> Also indicate gaps in "info record".
> 
> It looks like this:
> 
>   (gdb) info record
>   Active record target: record-btrace
>   Recording format: Intel(R) Branch Trace Store.
>   Buffer size: 64KB.
>   Recorded 32 instructions in 5 functions (1 gaps) for thread 1 (process 7182).
>   (gdb) record function-call-history /cli
>   1	fib	inst 1,9	at src/fib.c:9,14
>   2	  fib	inst 10,20	at src/fib.c:6,14
>   3	[decode error (1): instruction overflow]
>   4	fib	inst 21,28	at src/fib.c:11,14
>   5	  fib	inst 29,33	at src/fib.c:6,9
>   (gdb) record instruction-history 20,22
>   20	   0x000000000040062f <fib+47>:	sub    $0x1,%rax
>   [decode error (1): instruction overflow]
>   21	   0x0000000000400613 <fib+19>:	add    $0x1,%rax
>   22	   0x0000000000400617 <fib+23>:	mov    %rax,0x200a3a(%rip)
>   (gdb)
> 
> Gaps are ignored during reverse execution and replay.
> 
> 2014-11-20  Markus Metzger  <markus.t.metzger@intel.com>
> 
> 	* btrace.c (ftrace_find_call): Skip gaps.
> 	(ftrace_new_gap): New.
> 	(ftrace_update_function): Create new function after gap.
> 	(btrace_compute_ftrace_bts): Create gap on error.
> 	(btrace_clear): Reset the number of gaps.
> 	(btrace_insn_get): Return NULL if the iterator points to a gap.
> 	(btrace_insn_number): Return zero if the iterator points to a gap.
> 	(btrace_insn_end): Assert that the last function is not empty.
> 	(btrace_insn_next, btrace_insn_prev, btrace_insn_cmp): Handle
> gaps.
> 	(btrace_find_insn_by_number): Assert that the found iterator does
> 	not point to a gap.
> 	(btrace_call_next, btrace_call_prev): Assert that the last function
> 	is not a gap.
> 	* btrace.h (btrace_bts_error): New.
> 	(btrace_function): Update comment.
> 	(btrace_function) <insn, insn_offset, number>: Update comment.
> 	(btrace_function) <errcode>: New.
> 	(btrace_thread_info) <ngaps>: New.
> 	(btrace_thread_info) <replay>: Update comment.
> 	(btrace_insn_get): Update comment.
> 	* record-btrace.c (btrace_ui_out_decode_error): New.
> 	(record_btrace_info): Print number of gaps.
> 	(btrace_insn_history, btrace_call_history): Call
> 	btrace_ui_out_decode_error for gaps.
> 	(record_btrace_step_thread): Skip gaps.
> 
> testsuite/
> 	* gdb.btrace/buffer-size.exp: Update "info record" output.
> 	* gdb.btrace/delta.exp: Update "info record" output.
> 	* gdb.btrace/enable.exp: Update "info record" output.
> 	* gdb.btrace/finish.exp: Update "info record" output.
> 	* gdb.btrace/instruction_history.exp: Update "info record" output.
> 	* gdb.btrace/next.exp: Update "info record" output.
> 	* gdb.btrace/nexti.exp: Update "info record" output.
> 	* gdb.btrace/step.exp: Update "info record" output.
> 	* gdb.btrace/stepi.exp: Update "info record" output.
> 	* gdb.btrace/nohist.exp: Update "info record" output.
> ---
>  gdb/btrace.c                                     | 135 +++++++++++++++++--
>  gdb/btrace.h                                     |  40 +++++-
>  gdb/record-btrace.c                              | 160 ++++++++++++++++++-----
>  gdb/testsuite/gdb.btrace/buffer-size.exp         |   4 +-
>  gdb/testsuite/gdb.btrace/delta.exp               |   8 +-
>  gdb/testsuite/gdb.btrace/enable.exp              |   2 +-
>  gdb/testsuite/gdb.btrace/finish.exp              |   2 +-
>  gdb/testsuite/gdb.btrace/instruction_history.exp |   2 +-
>  gdb/testsuite/gdb.btrace/next.exp                |   4 +-
>  gdb/testsuite/gdb.btrace/nexti.exp               |   4 +-
>  gdb/testsuite/gdb.btrace/nohist.exp              |   2 +-
>  gdb/testsuite/gdb.btrace/step.exp                |   4 +-
>  gdb/testsuite/gdb.btrace/stepi.exp               |   4 +-
>  13 files changed, 306 insertions(+), 65 deletions(-)
> 
> diff --git a/gdb/btrace.c b/gdb/btrace.c
> index efd0572..91df7a0 100644
> --- a/gdb/btrace.c
> +++ b/gdb/btrace.c
> @@ -336,8 +336,9 @@ ftrace_find_call (struct btrace_function *bfun)
>      {
>        struct btrace_insn *last;
> 
> -      /* 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);
> 
> @@ -446,6 +447,32 @@ ftrace_new_switch (struct btrace_function *prev,
>    return bfun;
>  }
> 
> +/* Add a new function segment for a gap in the trace due to a decode error.
> +   PREV is the chronologically preceding function segment.
> +   ERRCODE is the format-specific error code.  */
> +
> +static struct btrace_function *
> +ftrace_new_gap (struct btrace_function *prev, int errcode)
> +{
> +  struct btrace_function *bfun;
> +
> +  /* We hijack prev if it was empty.  */
> +  if (prev != NULL && prev->errcode == 0
> +      && VEC_empty (btrace_insn_s, prev->insn))
> +    bfun = prev;
> +  else
> +    bfun = ftrace_new_function (prev, NULL, NULL);
> +
> +  /* This is a gap in the trace.  The call stack will likely be wrong at this
> +     point.  We keep the function level, though.  */
> +  bfun->level = prev != NULL ? prev->level : 0;
> +  bfun->errcode = errcode;
> +
> +  ftrace_debug (bfun, "new gap");
> +
> +  return bfun;
> +}
> +
>  /* Update BFUN with respect to the instruction at PC.  This may create new
>     function segments.
>     Return the chronologically latest function segment, never NULL.  */
> @@ -468,8 +495,8 @@ ftrace_update_function (struct btrace_function
> *bfun, CORE_ADDR pc)
>    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)
> +  /* If we didn't have a function or if we had a gap before, we create one.
> */
> +  if (bfun == NULL || bfun->errcode != 0)
>      return ftrace_new_function (bfun, mfun, fun);
> 
>    /* Check the last instruction, if we have one.
> @@ -597,13 +624,14 @@ btrace_compute_ftrace_bts (struct thread_info
> *tp,
>    struct btrace_thread_info *btinfo;
>    struct btrace_function *begin, *end;
>    struct gdbarch *gdbarch;
> -  unsigned int blk;
> +  unsigned int blk, ngaps;
>    int level;
> 
>    gdbarch = target_gdbarch ();
>    btinfo = &tp->btrace;
>    begin = btinfo->begin;
>    end = btinfo->end;
> +  ngaps = btinfo->ngaps;
>    level = begin != NULL ? -btinfo->level : INT_MAX;
>    blk = VEC_length (btrace_block_s, btrace->blocks);
> 
> @@ -628,6 +656,11 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
>  	    {
>  	      warning (_("Recorded trace may be corrupted around %s."),
>  		       core_addr_to_string_nz (pc));
> +
> +	      /* Indicate the gap in the trace.  */
> +	      end = ftrace_new_gap (end, BDE_BTS_OVERFLOW);
> +	      ngaps += 1;
> +
>  	      break;
>  	    }
> 
> @@ -660,6 +693,11 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
>  	    {
>  	      warning (_("Recorded trace may be incomplete around %s."),
>  		       core_addr_to_string_nz (pc));
> +
> +	      /* Indicate the gap in the trace.  */
> +	      end = ftrace_new_gap (end, BDE_BTS_INSN_SIZE);
> +	      ngaps += 1;
> +
>  	      break;
>  	    }
> 
> @@ -678,6 +716,7 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
> 
>    btinfo->begin = begin;
>    btinfo->end = end;
> +  btinfo->ngaps = ngaps;
> 
>    /* LEVEL is the minimal function level of all btrace function segments.
>       Define the global level offset to -LEVEL so all function levels are
> @@ -1009,6 +1048,7 @@ btrace_clear (struct thread_info *tp)
> 
>    btinfo->begin = NULL;
>    btinfo->end = NULL;
> +  btinfo->ngaps = 0;
> 
>    btrace_clear_history (btinfo);
>  }
> @@ -1206,6 +1246,10 @@ btrace_insn_get (const struct btrace_insn_iterator
> *it)
>    index = it->index;
>    bfun = it->function;
> 
> +  /* 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);
>    gdb_assert (0 < end);
> @@ -1222,6 +1266,11 @@ btrace_insn_number (const struct
> btrace_insn_iterator *it)
>    const struct btrace_function *bfun;
> 
>    bfun = it->function;
> +
> +  /* Return zero if the iterator points to a gap in the trace.  */
> +  if (bfun->errcode != 0)
> +    return 0;
> +
>    return bfun->insn_offset + it->index;
>  }
> 
> @@ -1257,6 +1306,7 @@ btrace_insn_end (struct btrace_insn_iterator *it,
>    /* 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.  */
>    length = VEC_length (btrace_insn_s, bfun->insn);
> +  gdb_assert (length > 0);
> 
>    it->function = bfun;
>    it->index = length - 1;
> @@ -1280,6 +1330,23 @@ 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)
> +	{
> +	  stride -= 1;
> +	  steps += 1;
> +
> +	  bfun = bfun->flow.next;
> +	  index = 0;
> +
> +	  /* There won't be a gap at the end since we will always add
> +	     an entry for the current PC.  */
> +	  gdb_assert (bfun != NULL);
> +
> +	  continue;
> +	}
> +
>        gdb_assert (0 < end);
>        gdb_assert (index < end);
> 
> @@ -1354,12 +1421,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);
> +
>        stride -= adv;
>        index -= adv;
>        steps += adv;
> @@ -1386,6 +1461,37 @@ btrace_insn_cmp (const struct
> btrace_insn_iterator *lhs,
>    lnum = btrace_insn_number (lhs);
>    rnum = btrace_insn_number (rhs);
> 
> +  /* A gap has an instruction number of zero.  Things are getting more
> +     complicated if gaps are involved.
> +
> +     We take the instruction number offset from the iterator's function.
> +     This is the number of the first instruction after the gap.
> +
> +     This is OK as long as both lhs and rhs point to gaps.  If only one of
> +     them does, we need to adjust the number based on the other's regular
> +     instruction number.  Otherwise, a gap might compare equal to an
> +     instruction.  */
> +
> +  if (lnum == 0 && rnum == 0)
> +    {
> +      lnum = lhs->function->insn_offset;
> +      rnum = rhs->function->insn_offset;
> +    }
> +  else if (lnum == 0)
> +    {
> +      lnum = lhs->function->insn_offset;
> +
> +      if (lnum == rnum)
> +	lnum -= 1;
> +    }
> +  else if (rnum == 0)
> +    {
> +      rnum = rhs->function->insn_offset;
> +
> +      if (rnum == lnum)
> +	rnum -= 1;
> +    }
> +
>    return (int) (lnum - rnum);
>  }
> 
> @@ -1397,7 +1503,7 @@ btrace_find_insn_by_number (struct
> btrace_insn_iterator *it,
>  			    unsigned int number)
>  {
>    const struct btrace_function *bfun;
> -  unsigned int end;
> +  unsigned int end, length;
> 
>    for (bfun = btinfo->end; bfun != NULL; bfun = bfun->flow.prev)
>      if (bfun->insn_offset <= number)
> @@ -1406,7 +1512,12 @@ btrace_find_insn_by_number (struct
> btrace_insn_iterator *it,
>    if (bfun == NULL)
>      return 0;
> 
> -  end = bfun->insn_offset + VEC_length (btrace_insn_s, bfun->insn);
> +  /* Since we're searching backwards from the end, we will never find gaps;
> +     we will already stop at the function segment succeeding a gap.  */
> +  length = VEC_length (btrace_insn_s, bfun->insn);
> +  gdb_assert (length > 0);
> +
> +  end = bfun->insn_offset + length;
>    if (end <= number)
>      return 0;
> 
> @@ -1508,6 +1619,9 @@ btrace_call_next (struct btrace_call_iterator *it,
> unsigned int stride)
>  	  insns = VEC_length (btrace_insn_s, bfun->insn);
>  	  if (insns == 1)
>  	    steps -= 1;
> +
> +	  /* We won't have gaps at the end.  */
> +	  gdb_assert (insns > 0);
>  	}
> 
>        if (stride == steps)
> @@ -1548,6 +1662,9 @@ btrace_call_prev (struct btrace_call_iterator *it,
> unsigned int stride)
>        if (insns == 1)
>  	bfun = bfun->flow.prev;
> 
> +      /* We won't have gaps at the end.  */
> +      gdb_assert (insns > 0);
> +
>        if (bfun == NULL)
>  	return 0;
> 
> diff --git a/gdb/btrace.h b/gdb/btrace.h
> index 2082bc7..71be12e 100644
> --- a/gdb/btrace.h
> +++ b/gdb/btrace.h
> @@ -86,12 +86,25 @@ enum btrace_function_flag
>    BFUN_UP_LINKS_TO_TAILCALL = (1 << 1)
>  };
> 
> +/* Decode errors for the BTS recording format.  */
> +enum btrace_bts_error
> +{
> +  /* The instruction trace overflowed the end of the trace block.  */
> +  BDE_BTS_OVERFLOW = 1,
> +
> +  /* The instruction size could not be determined.  */
> +  BDE_BTS_INSN_SIZE
> +};
> +
>  /* A branch trace function segment.
> 
>     This represents a function segment in a branch trace, i.e. a consecutive
>     number of instructions belonging to the same function.
> 
> -   We do not allow function segments without any instructions.  */
> +   In case of decode errors, we add an empty function segment to indicate
> +   the gap in the trace.
> +
> +   We do not allow function segments without instructions otherwise.  */
>  struct btrace_function
>  {
>    /* The full and minimal symbol for the function.  Both may be NULL.  */
> @@ -110,14 +123,23 @@ struct btrace_function
>    struct btrace_function *up;
> 
>    /* The instructions in this function segment.
> -     The instruction vector will never be empty.  */
> +     The instruction vector will be empty if the function segment
> +     represents a decode error.  */
>    VEC (btrace_insn_s) *insn;
> 
> +  /* The error code of a decode error that led to a gap.
> +     Must be zero unless INSN is empty; non-zero otherwise.  */
> +  int errcode;
> +
>    /* The instruction number offset for the first instruction in this
> -     function segment.  */
> +     function segment.
> +     If INSN is empty this is the insn_offset of the succeding function
> +     segment in control-flow order.  */
>    unsigned int insn_offset;
> 
> -  /* The function number in control-flow order.  */
> +  /* The function number in control-flow order.
> +     If INSN is empty indicating a gap in the trace due to a decode error,
> +     we still count the gap as a function.  */
>    unsigned int number;
> 
>    /* The function level in a back trace across the entire branch trace.
> @@ -223,6 +245,9 @@ struct btrace_thread_info
>       becomes zero.  */
>    int level;
> 
> +  /* The number of gaps in the trace.  */
> +  unsigned int ngaps;
> +
>    /* A bit-vector of btrace_thread_flag.  */
>    enum btrace_thread_flag flags;
> 
> @@ -232,7 +257,9 @@ struct btrace_thread_info
>    /* The function call history iterator.  */
>    struct btrace_call_history *call_history;
> 
> -  /* The current replay position.  NULL if not replaying.  */
> +  /* The current replay position.  NULL if not replaying.
> +     Gaps are skipped during replay, so REPLAY always points to a valid
> +     instruction.  */
>    struct btrace_insn_iterator *replay;
>  };
> 
> @@ -270,7 +297,8 @@ extern void parse_xml_btrace (struct btrace_data
> *data, const char *xml);
>  extern void parse_xml_btrace_conf (struct btrace_config *conf, const char
> *xml);
> 
>  /* Dereference a branch trace instruction iterator.  Return a pointer to the
> -   instruction the iterator points to.  */
> +   instruction the iterator points to.
> +   May return NULL if the iterator points to a gap in the trace.  */
>  extern const struct btrace_insn *
>    btrace_insn_get (const struct btrace_insn_iterator *);
> 
> diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
> index d9c1456..3075b8e 100644
> --- a/gdb/record-btrace.c
> +++ b/gdb/record-btrace.c
> @@ -350,7 +350,7 @@ record_btrace_info (struct target_ops *self)
>    struct btrace_thread_info *btinfo;
>    const struct btrace_config *conf;
>    struct thread_info *tp;
> -  unsigned int insns, calls;
> +  unsigned int insns, calls, gaps;
> 
>    DEBUG ("info");
> 
> @@ -368,6 +368,7 @@ record_btrace_info (struct target_ops *self)
> 
>    insns = 0;
>    calls = 0;
> +  gaps = 0;
> 
>    if (!btrace_is_empty (tp))
>      {
> @@ -379,19 +380,67 @@ record_btrace_info (struct target_ops *self)
>        calls = btrace_call_number (&call);
> 
>        btrace_insn_end (&insn, btinfo);
> -      btrace_insn_prev (&insn, 1);
>        insns = btrace_insn_number (&insn);
> +      /* The last instruction does not really belong to the trace.  */
> +      insns -= 1;
> +
> +      gaps = btinfo->ngaps;
>      }
> 
> -  printf_unfiltered (_("Recorded %u instructions in %u functions for thread "
> -		       "%d (%s).\n"), insns, calls, tp->num,
> -		     target_pid_to_str (tp->ptid));
> +  printf_unfiltered (_("Recorded %u instructions in %u functions (%u gaps) "
> +		       "for thread %d (%s).\n"), insns, calls, gaps,
> +		     tp->num, target_pid_to_str (tp->ptid));
> 
>    if (btrace_is_replaying (tp))
>      printf_unfiltered (_("Replay in progress.  At instruction %u.\n"),
>  		       btrace_insn_number (btinfo->replay));
>  }
> 
> +/* Print a decode error.  */
> +
> +static void
> +btrace_ui_out_decode_error (struct ui_out *uiout, int errcode,
> +			    enum btrace_format format)
> +{
> +  const char *errstr;
> +  int is_error;
> +
> +  errstr = _("unknown");
> +  is_error = 1;
> +
> +  switch (format)
> +    {
> +    default:
> +      break;
> +
> +    case BTRACE_FORMAT_BTS:
> +      switch (errcode)
> +	{
> +	default:
> +	  break;
> +
> +	case BDE_BTS_OVERFLOW:
> +	  errstr = _("instruction overflow");
> +	  break;
> +
> +	case BDE_BTS_INSN_SIZE:
> +	  errstr = _("unknown instruction");
> +	  break;
> +	}
> +      break;
> +    }
> +
> +  ui_out_text (uiout, _("["));
> +  if (is_error)
> +    {
> +      ui_out_text (uiout, _("decode error ("));
> +      ui_out_field_int (uiout, "errcode", errcode);
> +      ui_out_text (uiout, _("): "));
> +    }
> +  ui_out_text (uiout, errstr);
> +  ui_out_text (uiout, _("]\n"));
> +}
> +
>  /* Print an unsigned int.  */
> 
>  static void
> @@ -404,6 +453,7 @@ ui_out_field_uint (struct ui_out *uiout, const char
> *fld, unsigned int val)
> 
>  static void
>  btrace_insn_history (struct ui_out *uiout,
> +		     const struct btrace_thread_info *btinfo,
>  		     const struct btrace_insn_iterator *begin,
>  		     const struct btrace_insn_iterator *end, int flags)
>  {
> @@ -421,13 +471,30 @@ btrace_insn_history (struct ui_out *uiout,
> 
>        insn = btrace_insn_get (&it);
> 
> -      /* Print the instruction index.  */
> -      ui_out_field_uint (uiout, "index", btrace_insn_number (&it));
> -      ui_out_text (uiout, "\t");
> +      /* A NULL instruction indicates a gap in the trace.  */
> +      if (insn == NULL)
> +	{
> +	  const struct btrace_config *conf;
> +
> +	  conf = btrace_conf (btinfo);
> 
> -      /* Disassembly with '/m' flag may not produce the expected result.
> -	 See PR gdb/11833.  */
> -      gdb_disassembly (gdbarch, uiout, NULL, flags, 1, insn->pc, insn->pc + 1);
> +	  /* We have trace so we must have a configuration.  */
> +	  gdb_assert (conf != NULL);
> +
> +	  btrace_ui_out_decode_error (uiout, it.function->errcode,
> +				      conf->format);
> +	}
> +      else
> +	{
> +	  /* Print the instruction index.  */
> +	  ui_out_field_uint (uiout, "index", btrace_insn_number (&it));
> +	  ui_out_text (uiout, "\t");
> +
> +	  /* Disassembly with '/m' flag may not produce the expected result.
> +	     See PR gdb/11833.  */
> +	  gdb_disassembly (gdbarch, uiout, NULL, flags, 1, insn->pc,
> +			   insn->pc + 1);
> +	}
>      }
>  }
> 
> @@ -504,7 +571,7 @@ record_btrace_insn_history (struct target_ops *self,
> int size, int flags)
>      }
> 
>    if (covered > 0)
> -    btrace_insn_history (uiout, &begin, &end, flags);
> +    btrace_insn_history (uiout, btinfo, &begin, &end, flags);
>    else
>      {
>        if (size < 0)
> @@ -564,7 +631,7 @@ record_btrace_insn_history_range (struct target_ops
> *self,
>        btrace_insn_next (&end, 1);
>      }
> 
> -  btrace_insn_history (uiout, &begin, &end, flags);
> +  btrace_insn_history (uiout, btinfo, &begin, &end, flags);
>    btrace_set_insn_history (btinfo, &begin, &end);
> 
>    do_cleanups (uiout_cleanup);
> @@ -705,6 +772,21 @@ btrace_call_history (struct ui_out *uiout,
>        ui_out_field_uint (uiout, "index", bfun->number);
>        ui_out_text (uiout, "\t");
> 
> +      /* Indicate gaps in the trace.  */
> +      if (bfun->errcode != 0)
> +	{
> +	  const struct btrace_config *conf;
> +
> +	  conf = btrace_conf (btinfo);
> +
> +	  /* We have trace so we must have a configuration.  */
> +	  gdb_assert (conf != NULL);
> +
> +	  btrace_ui_out_decode_error (uiout, bfun->errcode, conf->format);
> +
> +	  continue;
> +	}
> +
>        if ((flags & RECORD_PRINT_INDENT_CALLS) != 0)
>  	{
>  	  int level = bfun->level + btinfo->level, i;
> @@ -1713,9 +1795,13 @@ record_btrace_step_thread (struct thread_info
> *tp)
>        if (replay == NULL)
>  	return btrace_step_no_history ();
> 
> -      /* We are always able to step at least once.  */
> -      steps = btrace_insn_next (replay, 1);
> -      gdb_assert (steps == 1);
> +      /* We are always able to step at least once - to the last instruction.
> +	 Skip gaps during replay.  */
> +      do
> +	{
> +	  steps = btrace_insn_next (replay, 1);
> +	  gdb_assert (steps == 1);
> +	} while (btrace_insn_get (replay) == NULL);
> 
>        /* Determine the end of the instruction trace.  */
>        btrace_insn_end (&end, btinfo);
> @@ -1731,10 +1817,14 @@ record_btrace_step_thread (struct thread_info
> *tp)
>        if (replay == NULL)
>  	replay = record_btrace_start_replaying (tp);
> 
> -      /* If we can't step any further, we reached the end of the history.  */
> -      steps = btrace_insn_prev (replay, 1);
> -      if (steps == 0)
> -	return btrace_step_no_history ();
> +      /* If we can't step any further, we reached the end of the history.
> +	 Skip gaps during replay.  */
> +      do
> +	{
> +	  steps = btrace_insn_prev (replay, 1);
> +	  if (steps == 0)
> +	    return btrace_step_no_history ();
> +	} while (btrace_insn_get (replay) == NULL);
> 
>        return btrace_step_stopped ();
> 
> @@ -1753,9 +1843,15 @@ record_btrace_step_thread (struct thread_info
> *tp)
>  	{
>  	  const struct btrace_insn *insn;
> 
> -	  /* We are always able to step at least once.  */
> -	  steps = btrace_insn_next (replay, 1);
> -	  gdb_assert (steps == 1);
> +	  /* We are always able to step at least once - to the last instruction.
> +	     Skip gaps during replay.  */
> +	  do
> +	    {
> +	      steps = btrace_insn_next (replay, 1);
> +	      gdb_assert (steps == 1);
> +
> +	      insn = btrace_insn_get (replay);
> +	    } while (insn == NULL);
> 
>  	  /* We stop replaying if we reached the end of the trace.  */
>  	  if (btrace_insn_cmp (replay, &end) == 0)
> @@ -1764,9 +1860,6 @@ record_btrace_step_thread (struct thread_info
> *tp)
>  	      return btrace_step_no_history ();
>  	    }
> 
> -	  insn = btrace_insn_get (replay);
> -	  gdb_assert (insn);
> -
>  	  DEBUG ("stepping %d (%s) ... %s", tp->num,
>  		 target_pid_to_str (tp->ptid),
>  		 core_addr_to_string_nz (insn->pc));
> @@ -1787,13 +1880,16 @@ record_btrace_step_thread (struct thread_info
> *tp)
>  	{
>  	  const struct btrace_insn *insn;
> 
> -	  /* If we can't step any further, we're done.  */
> -	  steps = btrace_insn_prev (replay, 1);
> -	  if (steps == 0)
> -	    return btrace_step_no_history ();
> +	  /* If we can't step any further, we reached the end of the history.
> +	     Skip gaps during replay.  */
> +	  do
> +	    {
> +	      steps = btrace_insn_prev (replay, 1);
> +	      if (steps == 0)
> +		return btrace_step_no_history ();
> 
> -	  insn = btrace_insn_get (replay);
> -	  gdb_assert (insn);
> +	      insn = btrace_insn_get (replay);
> +	    } while (insn == NULL);
> 
>  	  DEBUG ("reverse-stepping %d (%s) ... %s", tp->num,
>  		 target_pid_to_str (tp->ptid),
> diff --git a/gdb/testsuite/gdb.btrace/buffer-size.exp
> b/gdb/testsuite/gdb.btrace/buffer-size.exp
> index 9d36361..51d861c 100644
> --- a/gdb/testsuite/gdb.btrace/buffer-size.exp
> +++ b/gdb/testsuite/gdb.btrace/buffer-size.exp
> @@ -39,7 +39,7 @@ gdb_test "info record" [join [list \
>    "Active record target: record-btrace" \
>    "Recording format: Intel\\\(R\\\) Branch Trace Store\." \
>    "Buffer size: 4kB\." \
> -  "Recorded 0 instructions in 0 functions for \[^\\\r\\\n\]*" \
> +  "Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
>    ] "\r\n"] "info record with small bts buffer"
>  gdb_test "record stop" ".*" "stop recording with small bts buffer"
> 
> @@ -52,6 +52,6 @@ gdb_test "info record" [join [list \
>    "Active record target: record-btrace" \
>    "Recording format: Intel\\\(R\\\) Branch Trace Store\." \
>    "Buffer size: .*\." \
> -  "Recorded 0 instructions in 0 functions for \[^\\\r\\\n\]*" \
> +  "Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
>    ] "\r\n"] "info record with unlimited bts buffer"
>  gdb_test "record stop" ".*" "stop recording with unlimited bts buffer"
> diff --git a/gdb/testsuite/gdb.btrace/delta.exp
> b/gdb/testsuite/gdb.btrace/delta.exp
> index 71ccf07..d594102 100644
> --- a/gdb/testsuite/gdb.btrace/delta.exp
> +++ b/gdb/testsuite/gdb.btrace/delta.exp
> @@ -40,7 +40,7 @@ with_test_prefix "no trace" {
>    gdb_test "info record" [join [list \
>      "Active record target: record-btrace" \
>      "Recording format: .*" \
> -    "Recorded 0 instructions in 0 functions for .*" \
> +    "Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for .*" \
>      ] "\r\n"]
>    gdb_test "record instruction-history" "No trace\."
>    gdb_test "record function-call-history" "No trace\."
> @@ -53,7 +53,7 @@ proc check_trace {} {
>    gdb_test "info record" [join [list \
>      "Active record target: record-btrace" \
>      "Recording format: .*" \
> -    "Recorded 1 instructions in 1 functions for .*" \
> +    "Recorded 1 instructions in 1 functions \\\(0 gaps\\\) for .*" \
>      ] "\r\n"]
>    gdb_test "record instruction-history /f 1" \
>      "1\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tmov *\\\$0x0,%eax\r"
> @@ -74,7 +74,7 @@ gdb_test "reverse-stepi"
>  gdb_test "info record" [join [list \
>    "Active record target: record-btrace" \
>    "Recording format: .*" \
> -  "Recorded 1 instructions in 1 functions for .*" \
> +  "Recorded 1 instructions in 1 functions \\\(0 gaps\\\) for .*" \
>    "Replay in progress\.  At instruction 1\." \
>    ] "\r\n"] "reverse-stepi"
> 
> @@ -83,5 +83,5 @@ gdb_test "stepi"
>  gdb_test "info record" [join [list \
>    "Active record target: record-btrace" \
>    "Recording format: .*" \
> -  "Recorded 1 instructions in 1 functions for .*" \
> +  "Recorded 1 instructions in 1 functions \\\(0 gaps\\\) for .*" \
>    ] "\r\n"] "and back"
> diff --git a/gdb/testsuite/gdb.btrace/enable.exp
> b/gdb/testsuite/gdb.btrace/enable.exp
> index 2926c0f..37203a3 100644
> --- a/gdb/testsuite/gdb.btrace/enable.exp
> +++ b/gdb/testsuite/gdb.btrace/enable.exp
> @@ -58,7 +58,7 @@ gdb_test "record full" "The process is already being
> recorded\\.  Use \"record s
>  # no trace recorded yet
>  gdb_test "info record" "Active record target: record-btrace\r
>  .*\r
> -Recorded 0 instructions in 0 functions for thread 1.*\\." "info record without
> trace"
> +Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for thread 1.*\\." "info
> record without trace"
> 
>  # stop btrace record
>  gdb_test "record stop" "Process record is stopped and all execution logs are
> deleted\\." "record stop"
> diff --git a/gdb/testsuite/gdb.btrace/finish.exp
> b/gdb/testsuite/gdb.btrace/finish.exp
> index 593055b..c55c9de 100644
> --- a/gdb/testsuite/gdb.btrace/finish.exp
> +++ b/gdb/testsuite/gdb.btrace/finish.exp
> @@ -38,7 +38,7 @@ proc check_replay_at { insn } {
>    gdb_test "info record" [join [list \
>      "Active record target: record-btrace" \
>      "Recording format: .*" \
> -    "Recorded 40 instructions in 16 functions for .*" \
> +    "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
>      "Replay in progress\.  At instruction $insn\." \
>      ] "\r\n"]
>  }
> diff --git a/gdb/testsuite/gdb.btrace/instruction_history.exp
> b/gdb/testsuite/gdb.btrace/instruction_history.exp
> index 5a77357..e61a297 100644
> --- a/gdb/testsuite/gdb.btrace/instruction_history.exp
> +++ b/gdb/testsuite/gdb.btrace/instruction_history.exp
> @@ -50,7 +50,7 @@ gdb_continue_to_breakpoint "cont to $bp_location"
> ".*$srcfile2:$bp_location.*"
>  set traced {}
>  set testname "determine number of recorded instructions"
>  gdb_test_multiple "info record" $testname {
> -    -re "Active record target: record-btrace\r\n.*\r\nRecorded \(\[0-9\]*\)
> instructions in \(\[0-9\]*\) functions for thread 1 .*\\.\r\n$gdb_prompt $" {
> +    -re "Active record target: record-btrace\r\n.*\r\nRecorded \(\[0-9\]*\)
> instructions in \(\[0-9\]*\) functions \\\(0 gaps\\\) for thread 1
> .*\\.\r\n$gdb_prompt $" {
>          set traced $expect_out(1,string)
>          pass $testname
>      }
> diff --git a/gdb/testsuite/gdb.btrace/next.exp
> b/gdb/testsuite/gdb.btrace/next.exp
> index 1bc8125..7bf7cc9 100644
> --- a/gdb/testsuite/gdb.btrace/next.exp
> +++ b/gdb/testsuite/gdb.btrace/next.exp
> @@ -38,7 +38,7 @@ proc check_replay_at { insn } {
>    gdb_test "info record" [join [list \
>      "Active record target: record-btrace" \
>      "Recording format: .*" \
> -    "Recorded 40 instructions in 16 functions for .*" \
> +    "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
>      "Replay in progress\.  At instruction $insn\." \
>      ] "\r\n"]
>  }
> @@ -57,7 +57,7 @@ gdb_test "next" ".*main\.3.*"
>  gdb_test "info record" [join [list \
>    "Active record target: record-btrace" \
>    "Recording format: .*" \
> -  "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
> +  "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*"
> \
>    ] "\r\n"] "next back"
> 
>  # let's go somewhere where we can step some more
> diff --git a/gdb/testsuite/gdb.btrace/nexti.exp
> b/gdb/testsuite/gdb.btrace/nexti.exp
> index a263607..2cdaf73 100644
> --- a/gdb/testsuite/gdb.btrace/nexti.exp
> +++ b/gdb/testsuite/gdb.btrace/nexti.exp
> @@ -38,7 +38,7 @@ proc check_replay_at { insn } {
>    gdb_test "info record" [join [list \
>      "Active record target: record-btrace" \
>      "Recording format: .*" \
> -    "Recorded 40 instructions in 16 functions for .*" \
> +    "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
>      "Replay in progress\.  At instruction $insn\." \
>      ] "\r\n"]
>  }
> @@ -57,7 +57,7 @@ gdb_test "nexti" ".*main\.3.*" "next, 1.5"
>  gdb_test "info record" [join [list \
>    "Active record target: record-btrace" \
>    "Recording format: .*" \
> -  "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
> +  "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*"
> \
>    ] "\r\n"] "nexti back"
> 
>  # let's go somewhere where we can step some more
> diff --git a/gdb/testsuite/gdb.btrace/nohist.exp
> b/gdb/testsuite/gdb.btrace/nohist.exp
> index 4c1d875..6925193 100644
> --- a/gdb/testsuite/gdb.btrace/nohist.exp
> +++ b/gdb/testsuite/gdb.btrace/nohist.exp
> @@ -34,7 +34,7 @@ proc check_not_replaying {} {
>    gdb_test "info record" [join [list \
>      "Active record target: record-btrace" \
>      "Recording format: .*" \
> -	"Recorded 0 instructions in 0 functions for \[^\\\r\\\n\]*" \
> +	"Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for
> \[^\\\r\\\n\]*" \
>      ] "\r\n"]
>  }
> 
> diff --git a/gdb/testsuite/gdb.btrace/step.exp
> b/gdb/testsuite/gdb.btrace/step.exp
> index 483166b..86ffa0a 100644
> --- a/gdb/testsuite/gdb.btrace/step.exp
> +++ b/gdb/testsuite/gdb.btrace/step.exp
> @@ -38,7 +38,7 @@ proc check_replay_at { insn } {
>    gdb_test "info record" [join [list \
>      "Active record target: record-btrace" \
>      "Recording format: .*" \
> -    "Recorded 40 instructions in 16 functions for .*" \
> +    "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
>      "Replay in progress\.  At instruction $insn\." \
>      ] "\r\n"]
>  }
> @@ -87,5 +87,5 @@ gdb_test "step" ".*main\.3.*"
>  gdb_test "info record" [join [list \
>    "Active record target: record-btrace" \
>    "Recording format: .*" \
> -  "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
> +  "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*"
> \
>    ] "\r\n"] "step to live"
> diff --git a/gdb/testsuite/gdb.btrace/stepi.exp
> b/gdb/testsuite/gdb.btrace/stepi.exp
> index cce41e6..5c9625c 100644
> --- a/gdb/testsuite/gdb.btrace/stepi.exp
> +++ b/gdb/testsuite/gdb.btrace/stepi.exp
> @@ -36,7 +36,7 @@ proc check_replay_at { insn } {
>    gdb_test "info record" [join [list \
>      "Active record target: record-btrace" \
>      "Recording format: .*" \
> -    "Recorded 40 instructions in 16 functions for .*" \
> +    "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
>      "Replay in progress\.  At instruction $insn\." \
>      ] "\r\n"]
>  }
> @@ -61,7 +61,7 @@ gdb_test "stepi" ".*main\.3.*"
>  gdb_test "info record" [join [list \
>    "Active record target: record-btrace" \
>    "Recording format: .*" \
> -  "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
> +  "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*"
> \
>    ] "\r\n"] "stepi to live"
> 
>  # let's step from a goto position somewhere in the middle
> --
> 1.8.3.1

Intel GmbH
Dornacher Strasse 1
85622 Feldkirchen/Muenchen, Deutschland
Sitz der Gesellschaft: Feldkirchen bei Muenchen
Geschaeftsfuehrer: Christian Lamprechter, Hannes Schwaderer, Douglas Lusk
Registergericht: Muenchen HRB 47456
Ust.-IdNr./VAT Registration No.: DE129385895
Citibank Frankfurt a.M. (BLZ 502 109 00) 600119052

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 11/13] record-btrace: indicate gaps
  2014-11-20 17:32   ` Metzger, Markus T
@ 2014-11-20 17:36     ` Eli Zaretskii
  0 siblings, 0 replies; 25+ messages in thread
From: Eli Zaretskii @ 2014-11-20 17:36 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: gdb-patches, palves

> From: "Metzger, Markus T" <markus.t.metzger@intel.com>
> CC: "gdb-patches@sourceware.org" <gdb-patches@sourceware.org>,
> 	"palves@redhat.com" <palves@redhat.com>
> Date: Thu, 20 Nov 2014 17:30:10 +0000
> 
> Hello Eli,
> 
> I forgot to CC you on this patch.  It contains some user-visible strings.

I don't review those strings, except occasionally, with one exception:
the doc strings of the commands and options.  So no harm done.

Thanks.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 08/13] btrace: move and rename btrace-common
  2014-11-20 10:47 ` [PATCH v2 08/13] btrace: move and rename btrace-common Markus Metzger
@ 2015-01-27 11:25   ` Pedro Alves
  2015-01-28 17:58     ` Metzger, Markus T
  0 siblings, 1 reply; 25+ messages in thread
From: Pedro Alves @ 2015-01-27 11:25 UTC (permalink / raw)
  To: Markus Metzger; +Cc: gdb-patches

On 11/20/2014 10:47 AM, Markus Metzger wrote:
> diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
> index ebacd96..4ed9837 100644
> --- a/gdb/gdbserver/server.c
> +++ b/gdb/gdbserver/server.c
> @@ -30,7 +30,7 @@
>  #endif
>  #include "gdb_vecs.h"
>  #include "gdb_wait.h"
> -#include "btrace-common.h"
> +#include "nat/x86-btrace.h"
>  #include "filestuff.h"
>  #include "tracepoint.h"
>  #include "dll.h"

This still looks like layering violation this way.
server.c should not really have x86 specific bits either.

My original comment on v1 was:

>> diff --git a/gdb/common/btrace-common.c b/gdb/common/btrace-common.c
>> > index 90774a2..178ad35 100644
>> > --- a/gdb/common/btrace-common.c
>> > +++ b/gdb/common/btrace-common.c
>> > @@ -18,10 +18,47 @@
>> >     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
>> >
>> >  #include "btrace-common.h"
>> > +#include "nat/i386-cpuid.h"
> Hm, this is a layering violation.
> common/ files must not include nat/ files.
> If btrace-common.c is only used by native code, then it should
> itself be in nat/ too.

So, what does btrace-common.h actually contain that server.c needs?
"common" as a moniker doesn't really help much, as it doesn't
really indicate what the code is about.  Is it just common
definitions that target-independent parts of gdb and/or gdbserver
use?  How about something like leaving those parts in common/ (bonus
points of naming the file something more indicative or what is
contains), and then the native bits, like the x86 probing would
be put under nat/x86-btrace.h|c.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 01/13] btrace: add struct btrace_data
  2014-11-20 10:47 ` [PATCH v2 01/13] btrace: add struct btrace_data Markus Metzger
@ 2015-01-27 11:29   ` Pedro Alves
  0 siblings, 0 replies; 25+ messages in thread
From: Pedro Alves @ 2015-01-27 11:29 UTC (permalink / raw)
  To: Markus Metzger; +Cc: gdb-patches

On 11/20/2014 10:47 AM, Markus Metzger wrote:

> diff --git a/gdb/common/btrace-common.c b/gdb/common/btrace-common.c
> new file mode 100644
> index 0000000..869d0cb
> --- /dev/null
> +++ b/gdb/common/btrace-common.c
> @@ -0,0 +1,82 @@
> +/* Copyright (C) 2014 Free Software Foundation, Inc.
> +
> +   Contributed by Intel Corp. <markus.t.metzger@intel.com>
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +#include "btrace-common.h"

https://sourceware.org/gdb/wiki/Internals%20GDB-C-Coding-Standards#Include_Files

 "All .c files under gdb/common/, gdb/nat/ and gdb/target/ must include common-defs.h
 as their first non-comment line."

> diff --git a/gdb/common/btrace-common.h b/gdb/common/btrace-common.h
> index 339e684..27e7a95 100644
> --- a/gdb/common/btrace-common.h
> +++ b/gdb/common/btrace-common.h
> @@ -26,6 +26,7 @@
>     inferior.  For presentation purposes, the branch trace is represented as a
>     list of sequential control-flow blocks, one such list per thread.  */
>  
> +#include "common-defs.h"
>  #include "vec.h"

 "No .h file should include defs.h, server.h or common-defs.h."

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 03/13] btrace, linux: add perf event buffer abstraction
  2014-11-20 10:47 ` [PATCH v2 03/13] btrace, linux: add perf event buffer abstraction Markus Metzger
@ 2015-01-27 15:31   ` Pedro Alves
  2015-01-27 18:14     ` Metzger, Markus T
  0 siblings, 1 reply; 25+ messages in thread
From: Pedro Alves @ 2015-01-27 15:31 UTC (permalink / raw)
  To: Markus Metzger; +Cc: gdb-patches

On 11/20/2014 10:47 AM, Markus Metzger wrote:

> -  volatile struct perf_event_mmap_page *header;
> +  struct perf_event_buffer *pevent;
>    const uint8_t *begin, *end, *start;
> -  unsigned long data_head, data_tail, retries = 5;
> -  size_t buffer_size, size;
> +  unsigned long long data_head, data_tail, buffer_size, size;
> +  size_t retries = 5;

I notice that this is changing types from long to long long.
Also, a bit odd that retries is now size_t, given it's just a counter.

> +#if HAVE_LINUX_PERF_EVENT_H
> +/* A Linux perf event buffer.  */
> +struct perf_event_buffer
> +{
> +  /* The mapped memory.  */
> +  const uint8_t *mem;
> +
> +  /* The size of the mapped memory in bytes.  */
> +  unsigned long long size;
> +
> +  /* A pointer to the data_head field for this buffer. */
> +  volatile unsigned long long *data_head;
> +
> +  /* The data_head value from the last read.  */
> +  unsigned long long last_head;
> +};

Isn't there a better type to use here instead of "long long"?
Why not size_t, if a host buffer size; uint64_t, if it's a fixed
buffer format, assuming 64-bit here; or ULONGEST, the widest
integer we support?

> +#endif /* HAVE_LINUX_PERF_EVENT_H */


Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 25+ messages in thread

* RE: [PATCH v2 03/13] btrace, linux: add perf event buffer abstraction
  2015-01-27 15:31   ` Pedro Alves
@ 2015-01-27 18:14     ` Metzger, Markus T
  2015-01-27 18:15       ` Pedro Alves
  0 siblings, 1 reply; 25+ messages in thread
From: Metzger, Markus T @ 2015-01-27 18:14 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Tuesday, January 27, 2015 12:18 PM
> To: Metzger, Markus T
> Cc: gdb-patches@sourceware.org
> Subject: Re: [PATCH v2 03/13] btrace, linux: add perf event buffer abstraction
> 
> On 11/20/2014 10:47 AM, Markus Metzger wrote:
> 
> > -  volatile struct perf_event_mmap_page *header;
> > +  struct perf_event_buffer *pevent;
> >    const uint8_t *begin, *end, *start;
> > -  unsigned long data_head, data_tail, retries = 5;
> > -  size_t buffer_size, size;
> > +  unsigned long long data_head, data_tail, buffer_size, size;
> > +  size_t retries = 5;
> 
> I notice that this is changing types from long to long long.
> Also, a bit odd that retries is now size_t, given it's just a counter.

I changed retries to unsigned int.

data_head and data_tail correspond to respective fields in
struct perf_event_mmap_page, a kernel data structure.
buffer_size corresponds to the data_size field in the same
struct.

I can't say why they had been unsigned long before.  It was likely
a bug.


> > +#if HAVE_LINUX_PERF_EVENT_H
> > +/* A Linux perf event buffer.  */
> > +struct perf_event_buffer
> > +{
> > +  /* The mapped memory.  */
> > +  const uint8_t *mem;
> > +
> > +  /* The size of the mapped memory in bytes.  */
> > +  unsigned long long size;
> > +
> > +  /* A pointer to the data_head field for this buffer. */
> > +  volatile unsigned long long *data_head;
> > +
> > +  /* The data_head value from the last read.  */
> > +  unsigned long long last_head;
> > +};
> 
> Isn't there a better type to use here instead of "long long"?
> Why not size_t, if a host buffer size; uint64_t, if it's a fixed
> buffer format, assuming 64-bit here; or ULONGEST, the widest
> integer we support?

I'm using the same data type as the respective fields in
struct perf_event_mmap_page  (after resolving a few typedefs).

I'm also fine to directly use __u64 or to use uint64_t.

Thanks,
Markus.

Intel GmbH
Dornacher Strasse 1
85622 Feldkirchen/Muenchen, Deutschland
Sitz der Gesellschaft: Feldkirchen bei Muenchen
Geschaeftsfuehrer: Christian Lamprechter, Hannes Schwaderer, Douglas Lusk
Registergericht: Muenchen HRB 47456
Ust.-IdNr./VAT Registration No.: DE129385895
Citibank Frankfurt a.M. (BLZ 502 109 00) 600119052

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH v2 03/13] btrace, linux: add perf event buffer abstraction
  2015-01-27 18:14     ` Metzger, Markus T
@ 2015-01-27 18:15       ` Pedro Alves
  2015-01-28 16:50         ` Metzger, Markus T
  0 siblings, 1 reply; 25+ messages in thread
From: Pedro Alves @ 2015-01-27 18:15 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: gdb-patches

On 01/27/2015 03:31 PM, Metzger, Markus T wrote:

>> Isn't there a better type to use here instead of "long long"?
>> Why not size_t, if a host buffer size; uint64_t, if it's a fixed
>> buffer format, assuming 64-bit here; or ULONGEST, the widest
>> integer we support?
> 
> I'm using the same data type as the respective fields in
> struct perf_event_mmap_page  (after resolving a few typedefs).
> 
> I'm also fine to directly use __u64 or to use uint64_t.

Ah, so fits in "fixed format" category.  Let's go with
uint64_t then.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 25+ messages in thread

* RE: [PATCH v2 03/13] btrace, linux: add perf event buffer abstraction
  2015-01-27 18:15       ` Pedro Alves
@ 2015-01-28 16:50         ` Metzger, Markus T
  0 siblings, 0 replies; 25+ messages in thread
From: Metzger, Markus T @ 2015-01-28 16:50 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Tuesday, January 27, 2015 4:50 PM
> To: Metzger, Markus T
> Cc: gdb-patches@sourceware.org
> Subject: Re: [PATCH v2 03/13] btrace, linux: add perf event buffer abstraction
> 
> On 01/27/2015 03:31 PM, Metzger, Markus T wrote:
> 
> >> Isn't there a better type to use here instead of "long long"?
> >> Why not size_t, if a host buffer size; uint64_t, if it's a fixed
> >> buffer format, assuming 64-bit here; or ULONGEST, the widest
> >> integer we support?
> >
> > I'm using the same data type as the respective fields in
> > struct perf_event_mmap_page  (after resolving a few typedefs).
> >
> > I'm also fine to directly use __u64 or to use uint64_t.
> 
> Ah, so fits in "fixed format" category.  Let's go with
> uint64_t then.

gdb/nat/linux-btrace.c:450:24: error: assignment from incompatible pointer type [-Werror]
   tinfo->bts.data_head = &header->data_head;
                        ^
cc1: all warnings being treated as errors

The problem is that stdint.h defines uint64_t as unsigned int.  We know
it's the same size on 64-bit systems so we could cast the pointer...

I'd rather stay with unsigned long long, though.

Markus.
Intel GmbH
Dornacher Strasse 1
85622 Feldkirchen/Muenchen, Deutschland
Sitz der Gesellschaft: Feldkirchen bei Muenchen
Geschaeftsfuehrer: Christian Lamprechter, Hannes Schwaderer, Douglas Lusk
Registergericht: Muenchen HRB 47456
Ust.-IdNr./VAT Registration No.: DE129385895
Citibank Frankfurt a.M. (BLZ 502 109 00) 600119052

^ permalink raw reply	[flat|nested] 25+ messages in thread

* RE: [PATCH v2 08/13] btrace: move and rename btrace-common
  2015-01-27 11:25   ` Pedro Alves
@ 2015-01-28 17:58     ` Metzger, Markus T
  0 siblings, 0 replies; 25+ messages in thread
From: Metzger, Markus T @ 2015-01-28 17:58 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

> -----Original Message-----
> From: gdb-patches-owner@sourceware.org [mailto:gdb-patches-
> owner@sourceware.org] On Behalf Of Pedro Alves
> Sent: Tuesday, January 27, 2015 12:17 PM
> To: Metzger, Markus T
> Cc: gdb-patches@sourceware.org
> Subject: Re: [PATCH v2 08/13] btrace: move and rename btrace-common
> 
> On 11/20/2014 10:47 AM, Markus Metzger wrote:
> > diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
> > index ebacd96..4ed9837 100644
> > --- a/gdb/gdbserver/server.c
> > +++ b/gdb/gdbserver/server.c
> > @@ -30,7 +30,7 @@
> >  #endif
> >  #include "gdb_vecs.h"
> >  #include "gdb_wait.h"
> > -#include "btrace-common.h"
> > +#include "nat/x86-btrace.h"
> >  #include "filestuff.h"
> >  #include "tracepoint.h"
> >  #include "dll.h"
> 
> This still looks like layering violation this way.
> server.c should not really have x86 specific bits either.
> 
> My original comment on v1 was:
> 
> >> diff --git a/gdb/common/btrace-common.c b/gdb/common/btrace-
> common.c
> >> > index 90774a2..178ad35 100644
> >> > --- a/gdb/common/btrace-common.c
> >> > +++ b/gdb/common/btrace-common.c
> >> > @@ -18,10 +18,47 @@
> >> >     along with this program.  If not, see <http://www.gnu.org/licenses/>.
> */
> >> >
> >> >  #include "btrace-common.h"
> >> > +#include "nat/i386-cpuid.h"
> > Hm, this is a layering violation.
> > common/ files must not include nat/ files.
> > If btrace-common.c is only used by native code, then it should
> > itself be in nat/ too.
> 
> So, what does btrace-common.h actually contain that server.c needs?
> "common" as a moniker doesn't really help much, as it doesn't
> really indicate what the code is about.  Is it just common
> definitions that target-independent parts of gdb and/or gdbserver
> use?  How about something like leaving those parts in common/ (bonus
> points of naming the file something more indicative or what is
> contains), and then the native bits, like the x86 probing would
> be put under nat/x86-btrace.h|c.

What introduced the x86 dependency was the new function
btrace_this_cpu, which needs x86-cpuid.h.

I put the declaration of struct btrace_cpu in btrace-common.h
because it will be needed for struct btrace_data_pt_config, which will
be needed by struct btrace_data.

The new function btrace_this_cpu goes into nat/linux-btrace.c where
it is used.  Now nat/linux-btrace.c has an x86 dependency.  Otoh, the
entire feature is x86-dependent so we might as well rename it to
nat/x86-linux-btrace.c, if you want.

I dropped this patch and merged the two cpu identification patches.

Regarding a better name, the file contains configuration and data
representation stuff.  It could be split into btrace-config.h and
btrace-data.h.  Or we could just leave it as btrace-common.h.

regards,
markus.

Intel GmbH
Dornacher Strasse 1
85622 Feldkirchen/Muenchen, Deutschland
Sitz der Gesellschaft: Feldkirchen bei Muenchen
Geschaeftsfuehrer: Christian Lamprechter, Hannes Schwaderer, Douglas Lusk
Registergericht: Muenchen HRB 47456
Ust.-IdNr./VAT Registration No.: DE129385895
Citibank Frankfurt a.M. (BLZ 502 109 00) 600119052

^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2015-01-28 14:32 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-11-20 10:47 [PATCH v2 00/13] record btrace: prepare for a new trace format Markus Metzger
2014-11-20 10:47 ` [PATCH v2 09/13] btrace: identify cpu Markus Metzger
2014-11-20 10:47 ` [PATCH v2 02/13] btrace: add format argument to supports_btrace Markus Metzger
2014-11-20 10:47 ` [PATCH v2 07/13] btrace: extend struct btrace_insn Markus Metzger
2014-11-20 10:47 ` [PATCH v2 04/13] record btrace: add configuration struct Markus Metzger
2014-11-20 10:47 ` [PATCH v2 10/13] btrace: use the new cpu identifier Markus Metzger
2014-11-20 10:47 ` [PATCH v2 11/13] record-btrace: indicate gaps Markus Metzger
2014-11-20 17:32   ` Metzger, Markus T
2014-11-20 17:36     ` Eli Zaretskii
2014-11-20 10:47 ` [PATCH v2 12/13] configure: check for libipt Markus Metzger
2014-11-20 10:47 ` [PATCH v2 03/13] btrace, linux: add perf event buffer abstraction Markus Metzger
2015-01-27 15:31   ` Pedro Alves
2015-01-27 18:14     ` Metzger, Markus T
2015-01-27 18:15       ` Pedro Alves
2015-01-28 16:50         ` Metzger, Markus T
2014-11-20 10:47 ` [PATCH v2 08/13] btrace: move and rename btrace-common Markus Metzger
2015-01-27 11:25   ` Pedro Alves
2015-01-28 17:58     ` Metzger, Markus T
2014-11-20 10:47 ` [PATCH v2 05/13] record-btrace: add bts buffer size configuration option Markus Metzger
2014-11-20 16:25   ` Eli Zaretskii
2014-11-20 10:47 ` [PATCH v2 06/13] btrace: update btrace_compute_ftrace parameters Markus Metzger
2014-11-20 10:47 ` [PATCH v2 01/13] btrace: add struct btrace_data Markus Metzger
2015-01-27 11:29   ` Pedro Alves
2014-11-20 10:47 ` [PATCH v2 13/13] [wip] btrace: support Intel(R) Processor Trace Markus Metzger
2014-11-20 16:32   ` Eli Zaretskii

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).