This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH 12/12] [wip] btrace: support Intel(R) Processor Trace
- From: Markus Metzger <markus dot t dot metzger at intel dot com>
- To: palves at redhat dot com
- Cc: gdb-patches at sourceware dot org
- Date: Mon, 14 Jul 2014 15:56:36 +0200
- Subject: [PATCH 12/12] [wip] btrace: support Intel(R) Processor Trace
- Authentication-results: sourceware.org; auth=none
- References: <1405346196-1804-1-git-send-email-markus dot t dot metzger at intel dot com>
Adds a new command "record btrace pt" to configure perf to use Intel Processor
Trace instead of Intel Branch Trace Strore.
The "record btrace" command chooses the tracing format automatically.
2014-07-14 Markus Metzger <markus.t.metzger@intel.com>
---
gdb/NEWS | 10 +
gdb/btrace.c | 486 ++++++++++++++++++++++++++++++++++++++++++-
gdb/btrace.h | 19 ++
gdb/common/btrace-common.c | 59 ++++++
gdb/common/btrace-common.h | 99 ++++++++-
gdb/doc/gdb.texinfo | 50 ++++-
gdb/features/btrace-conf.dtd | 10 +-
gdb/features/btrace.dtd | 18 +-
gdb/gdbserver/linux-low.c | 119 ++++++++++-
gdb/gdbserver/server.c | 70 ++++++-
gdb/nat/linux-btrace.c | 459 +++++++++++++++++++++++++++++++++++++++-
gdb/nat/linux-btrace.h | 19 ++
gdb/record-btrace.c | 151 ++++++++++++++
gdb/remote.c | 100 ++++++++-
14 files changed, 1641 insertions(+), 28 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index d8ee284..14a6237 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -9,6 +9,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
@@ -17,6 +21,12 @@ set|show record btrace bts buffer-size
The obtained size may differ from the requested size. Use "info
record" to see the obtained buffer size.
+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 517c73a..7639aa4 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. */
@@ -722,6 +727,245 @@ 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, CORE_ADDR pc,
+ void *ignored)
+{
+ 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)
+ {
+ errcode = pt_insn_add_callback(decoder, 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. */
@@ -738,6 +982,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."));
@@ -909,6 +1157,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.
@@ -930,6 +1188,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."));
@@ -1075,7 +1336,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);
}
@@ -1115,12 +1376,171 @@ 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 perf-sideband "raw" xml record. */
+
+static void
+parse_xml_btrace_pt_perf_sideband_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.sideband.variant.perf.data,
+ &btrace->variant.pt.sideband.variant.perf.size);
+}
+
+/* Parse a btrace pt "perf-sideband" xml record. */
+
+static void
+parse_xml_btrace_pt_perf_sideband (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->variant.pt.sideband.format = BTRACE_PT_SIDEBAND_PERF;
+ btrace->variant.pt.sideband.variant.perf.data = NULL;
+ btrace->variant.pt.sideband.variant.perf.size = 0;
+}
+
+/* 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;
+ btrace->variant.pt.sideband.format = BTRACE_PT_SIDEBAND_NONE;
+}
+
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_perf_sideband_children[] = {
+ { "raw", NULL, NULL, GDB_XML_EF_OPTIONAL, NULL,
+ parse_xml_btrace_pt_perf_sideband_raw },
+ { 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 },
+ { "perf-sideband", NULL, btrace_pt_perf_sideband_children,
+ GDB_XML_EF_OPTIONAL, parse_xml_btrace_pt_perf_sideband, NULL },
+ { 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 }
@@ -1129,6 +1549,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 }
};
@@ -1186,9 +1608,67 @@ 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 "perf-sideband" xml record. */
+
+static void
+parse_xml_btrace_conf_perf_sideband (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->pt.sideband.format = BTRACE_PT_SIDEBAND_PERF;
+ conf->pt.sideband.perf.size = 0;
+
+ size = xml_find_attribute (attributes, "size");
+ if (size != NULL)
+ conf->pt.sideband.perf.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;
+ /* We initialize the sideband format to 'no sideband'. This will be
+ overwritten by sideband elements. */
+ conf->pt.sideband.format = BTRACE_PT_SIDEBAND_NONE;
+ 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_perf_sideband_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_pt_children[] = {
+ { "perf-sideband", btrace_conf_perf_sideband_attributes, NULL,
+ GDB_XML_EF_OPTIONAL, parse_xml_btrace_conf_perf_sideband, NULL },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+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 }
@@ -1197,6 +1677,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, btrace_conf_pt_children,
+ 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 7b524fa..b0ccb2c 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -28,6 +28,10 @@
#include "btrace-common.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/common/btrace-common.c b/gdb/common/btrace-common.c
index 178ad35..c12df95 100644
--- a/gdb/common/btrace-common.c
+++ b/gdb/common/btrace-common.c
@@ -66,6 +66,7 @@ btrace_format_string (enum btrace_format format)
{
case BTRACE_FORMAT_NONE: return "No or unknown 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"));
@@ -73,12 +74,44 @@ btrace_format_string (enum btrace_format format)
/* See btrace-common.h. */
+const char *
+btrace_pt_sideband_format_string (enum btrace_pt_sideband_format format)
+{
+ switch (format)
+ {
+ case BTRACE_PT_SIDEBAND_NONE: return "none";
+ case BTRACE_PT_SIDEBAND_PERF: return "perf";
+ }
+
+ internal_error (__FILE__, __LINE__, _("Unknown sideband format"));
+}
+
+/* See btrace-common.h. */
+
void
btrace_data_init (struct btrace_data *data)
{
data->format = BTRACE_FORMAT_NONE;
}
+/* Destroy sideband data. */
+
+static void
+btrace_data_pt_sideband_fini (struct btrace_data_pt_sideband *sideband)
+{
+ switch (sideband->format)
+ {
+ case BTRACE_PT_SIDEBAND_NONE:
+ return;
+
+ case BTRACE_PT_SIDEBAND_PERF:
+ xfree (sideband->variant.perf.data);
+ return;
+ }
+
+ internal_error (__FILE__, __LINE__, _("Unknown sideband format"));
+}
+
/* See btrace-common.h. */
void
@@ -93,11 +126,33 @@ 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);
+ btrace_data_pt_sideband_fini (&data->variant.pt.sideband);
+ return;
}
internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
}
+/* Return non-zero if SIDEBAND is empty; zero otherwise. */
+
+static int
+btrace_data_pt_sideband_empty (struct btrace_data_pt_sideband *sideband)
+{
+ switch (sideband->format)
+ {
+ case BTRACE_PT_SIDEBAND_NONE:
+ return 1;
+
+ case BTRACE_PT_SIDEBAND_PERF:
+ return sideband->variant.perf.size == 0;
+ }
+
+ internal_error (__FILE__, __LINE__, _("Unknown sideband format"));
+}
+
/* See btrace-common.h. */
int
@@ -110,6 +165,10 @@ 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
+ && btrace_data_pt_sideband_empty (&data->variant.pt.sideband));
}
internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
diff --git a/gdb/common/btrace-common.h b/gdb/common/btrace-common.h
index 5fa9806..7717b8f 100644
--- a/gdb/common/btrace-common.h
+++ b/gdb/common/btrace-common.h
@@ -36,7 +36,21 @@ 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
+};
+
+/* Enumeration of Intel Processor Trace sideband formats. */
+
+enum btrace_pt_sideband_format
+{
+ /* No sideband information. */
+ BTRACE_PT_SIDEBAND_NONE,
+
+ /* Linux perf-event sideband information. */
+ BTRACE_PT_SIDEBAND_PERF
};
/* Target specific branch trace information. */
@@ -110,6 +124,36 @@ struct btrace_config_bts
unsigned int size;
};
+/* A PERF sideband configuration. */
+
+struct btrace_config_pt_perf
+{
+ /* The size of the sideband buffer in bytes. */
+ unsigned int size;
+};
+
+/* An Intel Processor Trace sideband configuration. */
+
+struct btrace_config_pt_sideband
+{
+ /* The sideband format. */
+ enum btrace_pt_sideband_format format;
+
+ /* The configuration for perf sideband data. */
+ struct btrace_config_pt_perf perf;
+};
+
+/* An Intel Processor Trace configuration. */
+
+struct btrace_config_pt
+{
+ /* The size of the branch trace buffer in bytes. */
+ unsigned int size;
+
+ /* The sideband configuration. */
+ struct btrace_config_pt_sideband sideband;
+};
+
/* A branch tracing configuration.
This describes the requested configuration as well as the actually
@@ -124,6 +168,9 @@ struct btrace_config
/* The BTS format configuration. */
struct btrace_config_bts bts;
+
+ /* The Intel Processor Trace format configuration. */
+ struct btrace_config_pt pt;
};
#ifdef GDBSERVER
@@ -163,6 +210,49 @@ 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;
+};
+
+/* Intel Processor Trace sideband data. */
+struct btrace_data_pt_sideband
+{
+ /* The sideband format. */
+ enum btrace_pt_sideband_format format;
+
+ union
+ {
+ /* Format == BTRACE_PT_SIDEBAND_PERF. */
+ struct
+ {
+ /* The sideband data. */
+ gdb_byte *data;
+
+ /* The size of DATA in bytes. */
+ unsigned long size;
+ } perf;
+ } variant;
+};
+
+/* 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 sideband data. */
+ struct btrace_data_pt_sideband sideband;
+};
+
/* The branch trace data. */
struct btrace_data
{
@@ -172,6 +262,9 @@ struct btrace_data
{
/* Format == BTRACE_FORMAT_BTS. */
struct btrace_data_bts bts;
+
+ /* Format == BTRACE_FORMAT_PT. */
+ struct btrace_data_pt pt;
} variant;
};
@@ -181,6 +274,10 @@ extern struct btrace_cpu btrace_this_cpu (void);
/* Return a string representation of FORMAT. */
extern const char *btrace_format_string (enum btrace_format format);
+/* Return a string representation of FORMAT. */
+extern const char *
+ btrace_pt_sideband_format_string (enum btrace_pt_sideband_format format);
+
/* Initialize DATA. */
extern void btrace_data_init (struct btrace_data *data);
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index e27233a..35a4244 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6284,12 +6284,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
@@ -6317,6 +6321,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.
@@ -6327,9 +6345,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
@@ -6494,6 +6509,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
@@ -6539,6 +6577,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
diff --git a/gdb/features/btrace-conf.dtd b/gdb/features/btrace-conf.dtd
index 9a865d4..b8d1744 100644
--- a/gdb/features/btrace-conf.dtd
+++ b/gdb/features/btrace-conf.dtd
@@ -4,8 +4,14 @@
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.1">
+<!ELEMENT btrace-conf (bts?, pt?)>
+<!ATTLIST btrace-conf version CDATA #FIXED "1.2">
<!ELEMENT bts EMPTY>
<!ATTLIST bts size CDATA #IMPLIED>
+
+<!ELEMENT pt (perf-sideband?)>
+<!ATTLIST pt size CDATA #IMPLIED>
+
+<!ELEMENT perf-sideband EMPTY>
+<!ATTLIST perf-sideband size CDATA #IMPLIED>
diff --git a/gdb/features/btrace.dtd b/gdb/features/btrace.dtd
index a3193b0..ae93845 100644
--- a/gdb/features/btrace.dtd
+++ b/gdb/features/btrace.dtd
@@ -4,9 +4,23 @@
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?, perf-sideband?)>
+
+<!ELEMENT pt-config (cpu?)>
+
+<!ELEMENT cpu EMPTY>
+<!ATTLIST cpu vendor CDATA #REQUIRED
+ family CDATA #REQUIRED
+ model CDATA #REQUIRED
+ stepping CDATA #REQUIRED>
+
+<!ELEMENT perf-sideband (raw?)>
+
+<!ELEMENT raw (#PCDATA)>
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index d68e730..53e29bf 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"
@@ -5968,6 +5969,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
@@ -5989,15 +6039,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");
@@ -6012,15 +6061,50 @@ 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);
+
+ linux_low_encode_raw (buffer, btrace.variant.pt.data,
+ btrace.variant.pt.size);
+
+ switch (btrace.variant.pt.sideband.format)
+ {
+ case BTRACE_PT_SIDEBAND_NONE:
+ break;
+
+ case BTRACE_PT_SIDEBAND_PERF:
+ buffer_grow_str (buffer, "<perf-sideband>\n");
+ linux_low_encode_raw (buffer,
+ btrace.variant.pt.sideband.variant.perf.data,
+ btrace.variant.pt.sideband.variant.perf.size);
+ buffer_grow_str (buffer, "</perf-sideband>\n");
+ break;
+
+ default:
+ buffer_free (buffer);
+ buffer_grow_str0 (buffer, "E.Unsupported sideband format.");
+ goto err;
+ }
+
+ buffer_grow_str (buffer, "</pt>\n");
+ buffer_grow_str0 (buffer, "</btrace>\n");
+ break;
- btrace_data_fini (&btrace);
- return -1;
+ 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. */
@@ -6047,6 +6131,27 @@ 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");
+
+ switch (conf->pt.sideband.format)
+ {
+ default:
+ break;
+
+ case BTRACE_PT_SIDEBAND_PERF:
+ buffer_xml_printf (buffer, "<perf-sideband");
+ buffer_xml_printf (buffer, " size=\"0x%x\"",
+ conf->pt.sideband.perf.size);
+ buffer_xml_printf (buffer, "/>\n");
+ break;
+ }
+
+ buffer_xml_printf (buffer, "</pt>\n");
+ break;
}
}
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 1994c8c..88cf0a6 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -418,6 +418,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,
+ ¤t_btrace_conf);
+ if (thread->btrace == NULL)
+ return "E.Could not enable btrace.";
+
+ return NULL;
+}
+
/* Handle btrace disabling. */
static const char *
@@ -466,10 +483,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);
@@ -521,6 +540,37 @@ 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 if (strncmp (op, "pt:perf-sideband:size=",
+ strlen ("pt:perf-sideband:size=")) == 0)
+ {
+ unsigned long size;
+ char *endp = NULL;
+
+ errno = 0;
+ size = strtoul (op + strlen ("pt:perf-sideband: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.sideband.perf.size = (unsigned int) size;
+ }
else
{
strcpy (own_buf, "E.Bad Qbtrace configuration option.");
@@ -1831,12 +1881,26 @@ 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 (own_buf, ";Qbtrace-conf:bts:size+");
+ 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+");
+ strcat (buf, ";Qbtrace-conf:pt:perf-sideband: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 b52106e..67c4fad 100644
--- a/gdb/nat/linux-btrace.c
+++ b/gdb/nat/linux-btrace.c
@@ -166,6 +166,123 @@ perf_event_new_data (const struct perf_event_buffer *pev)
return header->data_head != pev->data_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 = perf_event_buffer_size (pev);
+ begin = perf_event_buffer_begin (pev);
+ 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)
+{
+ volatile struct perf_event_mmap_page *header;
+ unsigned long data_head, size;
+
+ header = perf_event_header (pev);
+ data_head = header->data_head;
+
+ size = perf_event_buffer_size (pev);
+ if (data_head < size)
+ size = data_head;
+
+ *data = perf_event_read (pev, data_head, size);
+ *psize = size;
+
+ pev->data_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)
+{
+ volatile struct perf_event_mmap_page *header;
+ unsigned long data_head, data_tail, size, buffer_size;
+
+ header = perf_event_header (pev);
+ data_head = header->data_head;
+ data_tail = pev->data_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 = perf_event_buffer_size (pev);
+ size = data_head - data_tail;
+ if (buffer_size < size)
+ return BTRACE_ERR_OVERFLOW;
+
+ *data = perf_event_read (pev, data_head, size);
+ *psize = size;
+
+ pev->data_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
@@ -401,6 +518,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
@@ -471,6 +675,28 @@ 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;
+
+#ifndef PERF_FLAG_FD_ITRACE
+ return 0;
+#endif
+
+ if (cached == 0)
+ {
+ if (!kernel_supports_pt ())
+ cached = -1;
+ else
+ cached = 1;
+ }
+
+ return cached > 0;
+}
+
/* See linux-btrace.h. */
int
@@ -483,6 +709,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"));
@@ -525,13 +754,13 @@ linux_enable_bts (ptid_t ptid, const struct btrace_config_bts *conf)
pages = (unsigned int) (((ULONGEST) conf->size + PAGE_SIZE - 1) / PAGE_SIZE);
if (pages == 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;
errcode = perf_event_map_buffer (&bts->bts, bts->file, 0, pages);
if (errcode == 0)
@@ -544,11 +773,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;
+}
+
+/* Enable branch tracing in Intel Processor Trace format. */
+
+#if defined(PERF_FLAG_FD_ITRACE)
+
+static struct btrace_target_info *
+linux_enable_pt (ptid_t ptid, const struct btrace_config_pt *conf)
+{
+ struct btrace_target_info *tinfo;
+ struct btrace_tinfo_pt *pt;
+ unsigned int pt_pages, sb_pages;
+ int pid, errcode, type;
+
+ pt_pages = (conf->size + PAGE_SIZE - 1) / PAGE_SIZE;
+ /* We do require a trace buffer, we're OK without a sideband buffer. */
+ if (pt_pages == 0)
+ return NULL;
+
+ switch (conf->sideband.format)
+ {
+ case BTRACE_PT_SIDEBAND_PERF:
+ sb_pages = (conf->sideband.perf.size + PAGE_SIZE - 1) / PAGE_SIZE;
+ break;
+
+ default:
+ sb_pages = 0;
+ break;
+ }
+
+ 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;
+
+ 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;
+
+ tinfo->ptr_bits = 0;
+
+ errno = 0;
+ pt->sb_file = syscall (SYS_perf_event_open, &pt->attr, pid, -1, -1, 0);
+ if (pt->sb_file < 0)
+ goto err;
+
+ pt->pt_file = syscall (SYS_perf_event_open, NULL, -1, -1, pt->sb_file,
+ PERF_FLAG_FD_ITRACE);
+ if (pt->sb_file < 0)
+ goto err_pt_file;
+
+ errcode = perf_event_map_buffer (&pt->pt, pt->pt_file, 0, pt_pages);
+ if (errcode != 0)
+ goto err_sb_file;
+
+ tinfo->conf.format = BTRACE_FORMAT_PT;
+ tinfo->conf.pt.size = pt->pt.size * PAGE_SIZE;
+
+ if (sb_pages == 0)
+ tinfo->conf.pt.sideband.format = BTRACE_PT_SIDEBAND_NONE;
+ else
+ {
+ errcode = perf_event_map_buffer (&pt->sideband, pt->sb_file, 0, sb_pages);
+ if (errcode != 0)
+ goto err_mmap;
+
+ tinfo->conf.pt.sideband.format = BTRACE_PT_SIDEBAND_PERF;
+ tinfo->conf.pt.sideband.perf.size = pt->sideband.size * PAGE_SIZE;
+ }
+
+ return tinfo;
+
+ err_mmap:
+ perf_event_unmap_buffer (&pt->pt);
+
+ err_pt_file:
+ close (pt->pt_file);
+
+ err_sb_file:
+ close (pt->sb_file);
+
err:
xfree (tinfo);
return NULL;
}
+#else /* defined(PERF_FLAG_FD_ITRACE) */
+
+static struct btrace_target_info *
+linux_enable_pt (ptid_t ptid, const struct btrace_config_pt *conf)
+{
+ return NULL;
+}
+
+#endif /* defined(PERF_FLAG_FD_ITRACE) */
+
/* See linux-btrace.h. */
struct btrace_target_info *
@@ -565,6 +899,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;
@@ -586,6 +924,25 @@ 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)
+{
+ int errcode;
+
+ errcode = perf_event_unmap_buffer (&tinfo->pt);
+ if (errcode != 0)
+ return BTRACE_ERR_UNKNOWN;
+
+ (void) perf_event_unmap_buffer (&tinfo->sideband);
+
+ close (tinfo->pt_file);
+ close (tinfo->sb_file);
+
+ return BTRACE_ERR_NONE;
+}
+
/* See linux-btrace.h. */
enum btrace_error
@@ -602,6 +959,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)
@@ -701,6 +1062,92 @@ 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, *sideband;
+ enum btrace_error errcode;
+
+ pt = &tinfo->variant.pt.pt;
+
+ linux_fill_btrace_pt_config (&btrace->config);
+
+ switch (tinfo->conf.pt.sideband.format)
+ {
+ case BTRACE_PT_SIDEBAND_PERF:
+ sideband = &tinfo->variant.pt.sideband;
+ break;
+
+ case BTRACE_PT_SIDEBAND_NONE:
+ sideband = NULL;
+ break;
+
+ default:
+ return BTRACE_ERR_NOT_SUPPORTED;
+ }
+
+ switch (type)
+ {
+ case BTRACE_READ_DELTA:
+ errcode = perf_event_read_new (pt, &btrace->data, &btrace->size);
+ if (errcode != BTRACE_ERR_NONE)
+ return errcode;
+
+ if (sideband != NULL)
+ {
+ btrace->sideband.format = BTRACE_PT_SIDEBAND_PERF;
+ errcode = perf_event_read_new (sideband,
+ &btrace->sideband.variant.perf.data,
+ &btrace->sideband.variant.perf.size);
+
+ /* If we run into any errors when reading sideband information, we
+ fall back to a full read.
+ We will still be able to stitch traces since the trace buffer did
+ not overflow. */
+ if (errcode != BTRACE_ERR_NONE)
+ perf_event_read_all (sideband,
+ &btrace->sideband.variant.perf.data,
+ &btrace->sideband.variant.perf.size);
+ }
+
+ return BTRACE_ERR_NONE;
+
+ case BTRACE_READ_NEW:
+ if (!perf_event_new_data (pt)
+ && (sideband == NULL || !perf_event_new_data (sideband)))
+ return BTRACE_ERR_NONE;
+
+ /* Fall through. */
+ case BTRACE_READ_ALL:
+ perf_event_read_all (pt, &btrace->data, &btrace->size);
+
+ if (sideband != NULL)
+ {
+ btrace->sideband.format = BTRACE_PT_SIDEBAND_PERF;
+ perf_event_read_all (sideband,
+ &btrace->sideband.variant.perf.data,
+ &btrace->sideband.variant.perf.size);
+ }
+
+ return BTRACE_ERR_NONE;
+ }
+
+ internal_error (__FILE__, __LINE__, _("Unkown btrace read type."));
+}
+
/* See linux-btrace.h. */
enum btrace_error
@@ -719,6 +1166,14 @@ 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;
+ btrace->variant.pt.sideband.format = BTRACE_PT_SIDEBAND_NONE;
+
+ 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 8ceeabf..b4d7ab8 100644
--- a/gdb/nat/linux-btrace.h
+++ b/gdb/nat/linux-btrace.h
@@ -60,6 +60,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 trace and sideband files. */
+ int pt_file, sb_file;
+
+ /* The trace perf event buffer. */
+ struct perf_event_buffer pt;
+
+ /* The sideband perf event buffer. */
+ struct perf_event_buffer sideband;
+};
#endif /* HAVE_LINUX_PERF_EVENT_H */
/* Branch trace target information per thread. */
@@ -77,6 +93,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/record-btrace.c b/gdb/record-btrace.c
index 418bb92..4c3b443 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -85,6 +85,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. */
@@ -322,6 +326,42 @@ 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)
+{
+ enum btrace_pt_sideband_format format;
+ 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);
+ }
+
+ format = conf->sideband.format;
+ switch (format)
+ {
+ case BTRACE_PT_SIDEBAND_PERF:
+ printf_unfiltered (_("Sideband format: %s.\n"),
+ btrace_pt_sideband_format_string (format));
+
+ size = conf->sideband.perf.size;
+ if (size > 0)
+ {
+ suffix = record_btrace_adjust_size (&size);
+ printf_unfiltered (_("Sideband buffer size: %u%s.\n"), size, suffix);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
/* Print a branch tracing configuration. */
static void
@@ -338,6 +378,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."));
@@ -429,6 +473,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, _("["));
@@ -2209,6 +2280,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
@@ -2219,6 +2312,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)
@@ -2276,6 +2377,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. */
@@ -2296,6 +2416,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);
@@ -2340,6 +2467,27 @@ to see the actual buffer size."), 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);
@@ -2347,4 +2495,7 @@ to see the actual buffer size."), NULL, NULL,
xcalloc, xfree);
record_btrace_conf.bts.size = 64 * 1024;
+
+ record_btrace_conf.pt.size = 16 * 1024;
+ record_btrace_conf.pt.sideband.format = BTRACE_PT_SIDEBAND_NONE;
}
diff --git a/gdb/remote.c b/gdb/remote.c
index 60e4e0e..1dcfa08 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1315,6 +1315,7 @@ enum {
PACKET_QTBuffer_size,
PACKET_Qbtrace_off,
PACKET_Qbtrace_bts,
+ PACKET_Qbtrace_pt,
PACKET_qXfer_btrace,
/* Support for the QNonStop packet. */
@@ -1343,6 +1344,12 @@ 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,
+
+ /* Support for the Qbtrace-conf:pt:perf-sideband:size packet. */
+ PACKET_Qbtrace_conf_pt_perf_size,
+
PACKET_MAX
};
@@ -3938,12 +3945,17 @@ 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 },
+ { "Qbtrace-conf:pt:perf-sideband:size", PACKET_DISABLE,
+ remote_supported_packet, PACKET_Qbtrace_conf_pt_perf_size }
};
static char *remote_support_xml;
@@ -11292,6 +11304,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"));
@@ -11330,6 +11351,52 @@ static void btrace_sync_conf (const struct btrace_config *conf)
btrace_target_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 != btrace_target_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."));
+ }
+
+ btrace_target_config.pt.size = conf->pt.size;
+ }
+
+ packet = &remote_protocol_packets[PACKET_Qbtrace_conf_pt_perf_size];
+ if (packet_config_support (packet) == PACKET_ENABLE
+ && (conf->pt.sideband.perf.size
+ != btrace_target_config.pt.sideband.perf.size))
+ {
+ pos = buf;
+ pos += xsnprintf (pos, endbuf - pos, "%s=0x%x", packet->name,
+ conf->pt.sideband.perf.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 sideband buffer "
+ "size: %s"), buf + 2);
+ else
+ error (_("Failed to configure the sideband buffer size."));
+ }
+
+ btrace_target_config.pt.sideband.perf.size = conf->pt.sideband.perf.size;
+ }
}
/* Read the current thread's btrace configuration from the target and
@@ -11341,7 +11408,7 @@ static void btrace_read_config (struct btrace_config *conf)
char *xml;
xml = target_read_stralloc (¤t_target,
- TARGET_OBJECT_BTRACE_CONF, "");
+ TARGET_OBJECT_BTRACE_CONF, "");
if (xml != NULL)
{
@@ -11358,13 +11425,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);
@@ -11481,7 +11559,7 @@ remote_read_btrace (struct target_ops *self,
}
xml = target_read_stralloc (¤t_target,
- TARGET_OBJECT_BTRACE, annex);
+ TARGET_OBJECT_BTRACE, annex);
if (xml == NULL)
return BTRACE_ERR_UNKNOWN;
@@ -12221,7 +12299,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);
@@ -12232,6 +12313,13 @@ 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);
+
+ add_packet_config_cmd (
+ &remote_protocol_packets[PACKET_Qbtrace_conf_pt_perf_size],
+ "Qbtrace-conf:pt:perf-sideband:size", "btrace-conf-pt-perf-size", 0);
+
/* Assert that we've registered commands for all packet configs. */
{
int i;
--
1.8.3.1