[PATCH v2 13/13] [wip] btrace: support Intel(R) Processor Trace

Markus Metzger
2014-11-20T10:47:52Z

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, " \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, " \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