Patchwork [v2,2/2] btrace: set/show record btrace cpu

login
register
mail settings
Submitter Metzger, Markus T
Date March 5, 2018, 10:59 a.m.
Message ID <1520247555-4643-2-git-send-email-markus.t.metzger@intel.com>
Download mbox | patch
Permalink /patch/26198/
State New
Headers show

Comments

Metzger, Markus T - March 5, 2018, 10:59 a.m.
Add new set/show commands to set the processor that is used for enabling
errata workarounds when decoding branch trace.

The general format is "<vendor>:<identifier>" but we also allow two
special values "auto" and "none".

The default is "auto", which is the current behaviour of having GDB
determine the processor on which the trace was recorded.

If that cpu is not known to the trace decoder, e.g. when using an old
decoder on a new system, decode may fail with "unknown cpu".  In most
cases it should suffice to 'downgrade' decode to assume an older cpu.
Unfortunately, we can't do this automatically.

The other special value, "none", disables errata workarounds.

2018-03-05  Markus Metzger  <markus.t.metzger@intel.com>

gdb/
	* NEWS (New options): announce set/show record btrace cpu.
	* btrace.c: Include record-btrace.h.
	(btrace_compute_ftrace_pt): Skip enabling errata workarounds if the
	vendor is unknown.
	(btrace_compute_ftrace_1): Add cpu parameter.  Update callers.
	Maybe overwrite the btrace configuration's cpu.
	(btrace_compute_ftrace): Add cpu parameter.  Update callers.
	(btrace_fetch): Add cpu parameter.  Update callers.
	(btrace_maint_update_pt_packets): Call record_btrace_get_cpu.  Maybe
	overwrite the btrace configuration's cpu.  Skip enabling errata
	workarounds if the vendor is unknown.
	* python/py-record-btrace.c: Include record-btrace.h.
	(recpy_bt_begin, recpy_bt_end, recpy_bt_instruction_history)
	(recpy_bt_function_call_history): Call record_btrace_get_cpu.
	* record-btrace.c (record_btrace_cpu_state_kind): New.
	(record_btrace_cpu): New.
	(set_record_btrace_cpu_cmdlist): New.
	(record_btrace_get_cpu): New.
	(require_btrace_thread, record_btrace_info)
	(record_btrace_resume_thread): Call record_btrace_get_cpu.
	(cmd_set_record_btrace_cpu_none): New.
	(cmd_set_record_btrace_cpu_auto): New.
	(cmd_set_record_btrace_cpu): New.
	(cmd_show_record_btrace_cpu): New.
	(_initialize_record_btrace): Initialize set/show record btrace cpu
	commands.
	* record-btrace.h (record_btrace_get_cpu): New.

testsuite/
	* gdb.btrace/cpu.exp: New.

doc/
	* gdb.texinfo: Document set/show record btrace cpu.
---
 gdb/NEWS                         |   5 ++
 gdb/btrace.c                     |  72 +++++++++++-----
 gdb/btrace.h                     |   6 +-
 gdb/doc/gdb.texinfo              |  63 ++++++++++++++
 gdb/python/py-record-btrace.c    |   9 +-
 gdb/record-btrace.c              | 177 ++++++++++++++++++++++++++++++++++++++-
 gdb/record-btrace.h              |   4 +
 gdb/testsuite/gdb.btrace/cpu.exp |  76 +++++++++++++++++
 8 files changed, 379 insertions(+), 33 deletions(-)
 create mode 100644 gdb/testsuite/gdb.btrace/cpu.exp
Eli Zaretskii - March 5, 2018, 4:16 p.m.
> From: Markus Metzger <markus.t.metzger@intel.com>
> Date: Mon,  5 Mar 2018 11:59:15 +0100
> 
> 2018-03-05  Markus Metzger  <markus.t.metzger@intel.com>
> 
> gdb/
> 	* NEWS (New options): announce set/show record btrace cpu.
> 	* btrace.c: Include record-btrace.h.
> 	(btrace_compute_ftrace_pt): Skip enabling errata workarounds if the
> 	vendor is unknown.
> 	(btrace_compute_ftrace_1): Add cpu parameter.  Update callers.
> 	Maybe overwrite the btrace configuration's cpu.
> 	(btrace_compute_ftrace): Add cpu parameter.  Update callers.
> 	(btrace_fetch): Add cpu parameter.  Update callers.
> 	(btrace_maint_update_pt_packets): Call record_btrace_get_cpu.  Maybe
> 	overwrite the btrace configuration's cpu.  Skip enabling errata
> 	workarounds if the vendor is unknown.
> 	* python/py-record-btrace.c: Include record-btrace.h.
> 	(recpy_bt_begin, recpy_bt_end, recpy_bt_instruction_history)
> 	(recpy_bt_function_call_history): Call record_btrace_get_cpu.
> 	* record-btrace.c (record_btrace_cpu_state_kind): New.
> 	(record_btrace_cpu): New.
> 	(set_record_btrace_cpu_cmdlist): New.
> 	(record_btrace_get_cpu): New.
> 	(require_btrace_thread, record_btrace_info)
> 	(record_btrace_resume_thread): Call record_btrace_get_cpu.
> 	(cmd_set_record_btrace_cpu_none): New.
> 	(cmd_set_record_btrace_cpu_auto): New.
> 	(cmd_set_record_btrace_cpu): New.
> 	(cmd_show_record_btrace_cpu): New.
> 	(_initialize_record_btrace): Initialize set/show record btrace cpu
> 	commands.
> 	* record-btrace.h (record_btrace_get_cpu): New.
> 
> testsuite/
> 	* gdb.btrace/cpu.exp: New.
> 
> doc/
> 	* gdb.texinfo: Document set/show record btrace cpu.

OK for the documentation parts.

Thanks.

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index 867e268..7815ad8 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -12,6 +12,11 @@  set debug fbsd-nat
 show debug fbsd-nat
   Control display of debugging info regarding the FreeBSD native target.
 
+set record btrace cpu
+show record btrace cpu
+  Controls the processor to be used for enabling errata workarounds for branch
+  trace decode.
+
 *** Changes in GDB 8.1
 
 * GDB now supports dynamically creating arbitrary register groups specified
diff --git a/gdb/btrace.c b/gdb/btrace.c
index 158d03c..269ee51 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -35,6 +35,9 @@ 
 #include "gdbcmd.h"
 #include "cli/cli-utils.h"
 
+/* For maintenance commands.  */
+#include "record-btrace.h"
+
 #include <inttypes.h>
 #include <ctype.h>
 #include <algorithm>
@@ -1428,15 +1431,20 @@  btrace_compute_ftrace_pt (struct thread_info *tp,
   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;
+  /* We treat an unknown vendor as 'no errata'.  */
+  if (btrace->config.cpu.vendor != CV_UNKNOWN)
+    {
+      config.cpu.vendor
+	= pt_translate_cpu_vendor (btrace->config.cpu.vendor);
+      config.cpu.family = btrace->config.cpu.family;
+      config.cpu.model = btrace->config.cpu.model;
+      config.cpu.stepping = btrace->config.cpu.stepping;
 
-  errcode = pt_cpu_errata (&config.errata, &config.cpu);
-  if (errcode < 0)
-    error (_("Failed to configure the Intel Processor Trace decoder: %s."),
-	   pt_errstr (pt_errcode (errcode)));
+      errcode = pt_cpu_errata (&config.errata, &config.cpu);
+      if (errcode < 0)
+	error (_("Failed to configure the Intel Processor Trace "
+		 "decoder: %s."), pt_errstr (pt_errcode (errcode)));
+    }
 
   decoder = pt_insn_alloc_decoder (&config);
   if (decoder == NULL)
@@ -1485,10 +1493,14 @@  btrace_compute_ftrace_pt (struct thread_info *tp,
 #endif /* defined (HAVE_LIBIPT)  */
 
 /* Compute the function branch trace from a block branch trace BTRACE for
-   a thread given by BTINFO.  */
+   a thread given by BTINFO.  If CPU is not NULL, overwrite the cpu in the
+   branch trace configuration.  This is currently only used for the PT
+   format.  */
 
 static void
-btrace_compute_ftrace_1 (struct thread_info *tp, struct btrace_data *btrace,
+btrace_compute_ftrace_1 (struct thread_info *tp,
+			 struct btrace_data *btrace,
+			 const struct btrace_cpu *cpu,
 			 std::vector<unsigned int> &gaps)
 {
   DEBUG ("compute ftrace");
@@ -1503,6 +1515,10 @@  btrace_compute_ftrace_1 (struct thread_info *tp, struct btrace_data *btrace,
       return;
 
     case BTRACE_FORMAT_PT:
+      /* Overwrite the cpu we use for enabling errata workarounds.  */
+      if (cpu != nullptr)
+	btrace->variant.pt.config.cpu = *cpu;
+
       btrace_compute_ftrace_pt (tp, &btrace->variant.pt, gaps);
       return;
     }
@@ -1521,13 +1537,14 @@  btrace_finalize_ftrace (struct thread_info *tp, std::vector<unsigned int> &gaps)
 }
 
 static void
-btrace_compute_ftrace (struct thread_info *tp, struct btrace_data *btrace)
+btrace_compute_ftrace (struct thread_info *tp, struct btrace_data *btrace,
+		       const struct btrace_cpu *cpu)
 {
   std::vector<unsigned int> gaps;
 
   TRY
     {
-      btrace_compute_ftrace_1 (tp, btrace, gaps);
+      btrace_compute_ftrace_1 (tp, btrace, cpu, gaps);
     }
   CATCH (error, RETURN_MASK_ALL)
     {
@@ -1564,7 +1581,7 @@  btrace_add_pc (struct thread_info *tp)
   block->begin = pc;
   block->end = pc;
 
-  btrace_compute_ftrace (tp, &btrace);
+  btrace_compute_ftrace (tp, &btrace, NULL);
 
   do_cleanups (cleanup);
 }
@@ -1872,7 +1889,7 @@  btrace_decode_error (enum btrace_format format, int errcode)
 /* See btrace.h.  */
 
 void
-btrace_fetch (struct thread_info *tp)
+btrace_fetch (struct thread_info *tp, const struct btrace_cpu *cpu)
 {
   struct btrace_thread_info *btinfo;
   struct btrace_target_info *tinfo;
@@ -1948,7 +1965,7 @@  btrace_fetch (struct thread_info *tp)
       btrace_maint_clear (btinfo);
 
       btrace_clear_history (btinfo);
-      btrace_compute_ftrace (tp, &btrace);
+      btrace_compute_ftrace (tp, &btrace, cpu);
     }
 
   do_cleanups (cleanup);
@@ -3028,6 +3045,7 @@  static void
 btrace_maint_update_pt_packets (struct btrace_thread_info *btinfo)
 {
   struct pt_packet_decoder *decoder;
+  const struct btrace_cpu *cpu;
   struct btrace_data_pt *pt;
   struct pt_config config;
   int errcode;
@@ -3044,15 +3062,23 @@  btrace_maint_update_pt_packets (struct btrace_thread_info *btinfo)
   config.begin = pt->data;
   config.end = pt->data + pt->size;
 
-  config.cpu.vendor = pt_translate_cpu_vendor (pt->config.cpu.vendor);
-  config.cpu.family = pt->config.cpu.family;
-  config.cpu.model = pt->config.cpu.model;
-  config.cpu.stepping = pt->config.cpu.stepping;
+  cpu = record_btrace_get_cpu ();
+  if (cpu == nullptr)
+    cpu = &pt->config.cpu;
+
+  /* We treat an unknown vendor as 'no errata'.  */
+  if (cpu->vendor != CV_UNKNOWN)
+    {
+      config.cpu.vendor = pt_translate_cpu_vendor (cpu->vendor);
+      config.cpu.family = cpu->family;
+      config.cpu.model = cpu->model;
+      config.cpu.stepping = cpu->stepping;
 
-  errcode = pt_cpu_errata (&config.errata, &config.cpu);
-  if (errcode < 0)
-    error (_("Failed to configure the Intel Processor Trace decoder: %s."),
-	   pt_errstr (pt_errcode (errcode)));
+      errcode = pt_cpu_errata (&config.errata, &config.cpu);
+      if (errcode < 0)
+	error (_("Failed to configure the Intel Processor Trace "
+		 "decoder: %s."), pt_errstr (pt_errcode (errcode)));
+    }
 
   decoder = pt_pkt_alloc_decoder (&config);
   if (decoder == NULL)
diff --git a/gdb/btrace.h b/gdb/btrace.h
index 5c3f21b..4d57edb 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -385,8 +385,10 @@  extern void btrace_teardown (struct thread_info *);
 
 extern const char *btrace_decode_error (enum btrace_format format, int errcode);
 
-/* Fetch the branch trace for a single thread.  */
-extern void btrace_fetch (struct thread_info *);
+/* Fetch the branch trace for a single thread.  If CPU is not NULL, assume
+   CPU for trace decode.  */
+extern void btrace_fetch (struct thread_info *,
+			  const struct btrace_cpu *cpu);
 
 /* Clear the branch trace for a single thread.  */
 extern void btrace_clear (struct thread_info *);
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 74e0fdb..4592455 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6952,10 +6952,73 @@  and to read-write memory.  Beware that the accessed memory corresponds
 to the live target and not necessarily to the current replay
 position.
 
+@item set record btrace cpu @var{identifier}
+Set the processor to be used for enabling workarounds for processor
+errata when decoding the trace.
+
+Processor errata are defects in processor operation, caused by its
+design or manufacture.  They can cause a trace not to match the
+specification.  This, in turn, may cause trace decode to fail.
+@value{GDBN} can detect erroneous trace packets and correct them, thus
+avoiding the decoding failures.  These corrections are known as
+@dfn{errata workarounds}, and are enabled based on the processor on
+which the trace was recorded.
+
+By default, @value{GDBN} attempts to detect the processor
+automatically, and apply the necessary workarounds for it.  However,
+you may need to specify the processor if @value{GDBN} does not yet
+support it.  This command allows you to do that, and also allows to
+disable the workarounds.
+
+The argument @var{identifier} identifies the @sc{cpu} and is of the
+form: @code{@var{vendor}:@var{procesor identifier}}.  In addition,
+there are two special identifiers, @code{none} and @code{auto}
+(default).
+
+The following vendor identifiers and corresponding processor
+identifiers are currently supported:
+
+@multitable @columnfractions .1 .9
+
+@item @code{intel}
+@tab @var{family}/@var{model}[/@var{stepping}]
+
+@end multitable
+
+On GNU/Linux systems, the processor @var{family}, @var{model}, and
+@var{stepping} can be obtained from @code{/proc/cpuinfo}.
+
+If @var{identifier} is @code{auto}, enable errata workarounds for the
+processor on which the trace was recorded.  If @var{identifier} is
+@code{none}, errata workarounds are disabled.
+
+For example, when using an old @value{GDBN} on a new system, decode
+may fail because @value{GDBN} does not support the new processor.  It
+often suffices to specify an older processor that @value{GDBN}
+supports.
+
+@smallexample
+(gdb) info record
+Active record target: record-btrace
+Recording format: Intel Processor Trace.
+Buffer size: 16kB.
+Failed to configure the Intel Processor Trace decoder: unknown cpu.
+(gdb) set record btrace cpu intel:6/158
+(gdb) info record
+Active record target: record-btrace
+Recording format: Intel Processor Trace.
+Buffer size: 16kB.
+Recorded 84872 instructions in 3189 functions (0 gaps) for thread 1 (...).
+@end smallexample
+
 @kindex show record btrace
 @item show record btrace replay-memory-access
 Show the current setting of @code{replay-memory-access}.
 
+@item show record btrace cpu
+Show the processor to be used for enabling trace decode errata
+workarounds.
+
 @kindex set record btrace bts
 @item set record btrace bts buffer-size @var{size}
 @itemx set record btrace bts buffer-size unlimited
diff --git a/gdb/python/py-record-btrace.c b/gdb/python/py-record-btrace.c
index 35828a6..d78df7f 100644
--- a/gdb/python/py-record-btrace.c
+++ b/gdb/python/py-record-btrace.c
@@ -24,6 +24,7 @@ 
 #include "btrace.h"
 #include "py-record.h"
 #include "py-record-btrace.h"
+#include "record-btrace.h"
 #include "disasm.h"
 
 #if defined (IS_PY3K)
@@ -678,7 +679,7 @@  recpy_bt_begin (PyObject *self, void *closure)
   if (tinfo == NULL)
     Py_RETURN_NONE;
 
-  btrace_fetch (tinfo);
+  btrace_fetch (tinfo, record_btrace_get_cpu ());
 
   if (btrace_is_empty (tinfo))
     Py_RETURN_NONE;
@@ -700,7 +701,7 @@  recpy_bt_end (PyObject *self, void *closure)
   if (tinfo == NULL)
     Py_RETURN_NONE;
 
-  btrace_fetch (tinfo);
+  btrace_fetch (tinfo, record_btrace_get_cpu ());
 
   if (btrace_is_empty (tinfo))
     Py_RETURN_NONE;
@@ -724,7 +725,7 @@  recpy_bt_instruction_history (PyObject *self, void *closure)
    if (tinfo == NULL)
      Py_RETURN_NONE;
 
-   btrace_fetch (tinfo);
+   btrace_fetch (tinfo, record_btrace_get_cpu ());
 
    if (btrace_is_empty (tinfo))
      Py_RETURN_NONE;
@@ -753,7 +754,7 @@  recpy_bt_function_call_history (PyObject *self, void *closure)
   if (tinfo == NULL)
     Py_RETURN_NONE;
 
-  btrace_fetch (tinfo);
+  btrace_fetch (tinfo, record_btrace_get_cpu ());
 
   if (btrace_is_empty (tinfo))
     Py_RETURN_NONE;
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 905f0c4..8205e97 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -60,6 +60,20 @@  static const char *const replay_memory_access_types[] =
 /* The currently allowed replay memory access type.  */
 static const char *replay_memory_access = replay_memory_access_read_only;
 
+/* The cpu state kinds.  */
+enum record_btrace_cpu_state_kind
+{
+  CS_AUTO,
+  CS_NONE,
+  CS_CPU
+};
+
+/* The current cpu state.  */
+static enum record_btrace_cpu_state_kind record_btrace_cpu_state = CS_AUTO;
+
+/* The current cpu for trace decode.  */
+static struct btrace_cpu record_btrace_cpu;
+
 /* Command lists for "set/show record btrace".  */
 static struct cmd_list_element *set_record_btrace_cmdlist;
 static struct cmd_list_element *show_record_btrace_cmdlist;
@@ -87,6 +101,9 @@  static struct cmd_list_element *show_record_btrace_bts_cmdlist;
 static struct cmd_list_element *set_record_btrace_pt_cmdlist;
 static struct cmd_list_element *show_record_btrace_pt_cmdlist;
 
+/* Command list for "set record btrace cpu".  */
+static struct cmd_list_element *set_record_btrace_cpu_cmdlist;
+
 /* Print a record-btrace debug message.  Use do ... while (0) to avoid
    ambiguities when used in if statements.  */
 
@@ -100,6 +117,26 @@  static struct cmd_list_element *show_record_btrace_pt_cmdlist;
   while (0)
 
 
+/* Return the cpu configured by the user.  Returns NULL if the cpu was
+   configured as auto.  */
+const struct btrace_cpu *
+record_btrace_get_cpu (void)
+{
+  switch (record_btrace_cpu_state)
+    {
+    case CS_AUTO:
+      return nullptr;
+
+    case CS_NONE:
+      record_btrace_cpu.vendor = CV_UNKNOWN;
+      /* Fall through.  */
+    case CS_CPU:
+      return &record_btrace_cpu;
+    }
+
+  error (_("Internal error: bad record btrace cpu state."));
+}
+
 /* Update the branch trace for the current thread and return a pointer to its
    thread_info.
 
@@ -119,7 +156,7 @@  require_btrace_thread (void)
 
   validate_registers_access ();
 
-  btrace_fetch (tp);
+  btrace_fetch (tp, record_btrace_get_cpu ());
 
   if (btrace_is_empty (tp))
     error (_("No trace."));
@@ -427,7 +464,7 @@  record_btrace_info (struct target_ops *self)
   if (conf != NULL)
     record_btrace_print_conf (conf);
 
-  btrace_fetch (tp);
+  btrace_fetch (tp, record_btrace_get_cpu ());
 
   insns = 0;
   calls = 0;
@@ -1841,7 +1878,7 @@  record_btrace_resume_thread (struct thread_info *tp,
   btinfo = &tp->btrace;
 
   /* Fetch the latest branch trace.  */
-  btrace_fetch (tp);
+  btrace_fetch (tp, record_btrace_get_cpu ());
 
   /* A resume request overwrites a preceding resume or stop request.  */
   btinfo->flags &= ~(BTHR_MOVE | BTHR_STOP);
@@ -2977,7 +3014,113 @@  cmd_show_replay_memory_access (struct ui_file *file, int from_tty,
 		    replay_memory_access);
 }
 
-/* The "set record btrace bts" command.  */
+/* The "set record btrace cpu none" command.  */
+
+static void
+cmd_set_record_btrace_cpu_none (const char *args, int from_tty)
+{
+  if (args != nullptr && *args != 0)
+    error (_("Trailing junk: '%s'."), args);
+
+  record_btrace_cpu_state = CS_NONE;
+}
+
+/* The "set record btrace cpu auto" command.  */
+
+static void
+cmd_set_record_btrace_cpu_auto (const char *args, int from_tty)
+{
+  if (args != nullptr && *args != 0)
+    error (_("Trailing junk: '%s'."), args);
+
+  record_btrace_cpu_state = CS_AUTO;
+}
+
+/* The "set record btrace cpu" command.  */
+
+static void
+cmd_set_record_btrace_cpu (const char *args, int from_tty)
+{
+  if (args == nullptr)
+    args = "";
+
+  /* We use a hard-coded vendor string for now.  */
+  unsigned int family, model, stepping;
+  int l1, l2, matches = sscanf (args, "intel: %u/%u%n/%u%n", &family,
+				&model, &l1, &stepping, &l2);
+  if (matches == 3)
+    {
+      if (strlen (args) != l2)
+	error (_("Trailing junk: '%s'."), args + l2);
+    }
+  else if (matches == 2)
+    {
+      if (strlen (args) != l1)
+	error (_("Trailing junk: '%s'."), args + l1);
+
+      stepping = 0;
+    }
+  else
+    error (_("Bad format.  See \"help set record btrace cpu\"."));
+
+  if (USHRT_MAX < family)
+    error (_("Cpu family too big."));
+
+  if (UCHAR_MAX < model)
+    error (_("Cpu model too big."));
+
+  if (UCHAR_MAX < stepping)
+    error (_("Cpu stepping too big."));
+
+  record_btrace_cpu.vendor = CV_INTEL;
+  record_btrace_cpu.family = family;
+  record_btrace_cpu.model = model;
+  record_btrace_cpu.stepping = stepping;
+
+  record_btrace_cpu_state = CS_CPU;
+}
+
+/* The "show record btrace cpu" command.  */
+
+static void
+cmd_show_record_btrace_cpu (const char *args, int from_tty)
+{
+  const char *cpu;
+
+  if (args != nullptr && *args != 0)
+    error (_("Trailing junk: '%s'."), args);
+
+  switch (record_btrace_cpu_state)
+    {
+    case CS_AUTO:
+      printf_unfiltered (_("btrace cpu is 'auto'.\n"));
+      return;
+
+    case CS_NONE:
+      printf_unfiltered (_("btrace cpu is 'none'.\n"));
+      return;
+
+    case CS_CPU:
+      switch (record_btrace_cpu.vendor)
+	{
+	case CV_INTEL:
+	  if (record_btrace_cpu.stepping == 0)
+	    printf_unfiltered (_("btrace cpu is 'intel: %u/%u'.\n"),
+			       record_btrace_cpu.family,
+			       record_btrace_cpu.model);
+	  else
+	    printf_unfiltered (_("btrace cpu is 'intel: %u/%u/%u'.\n"),
+			       record_btrace_cpu.family,
+			       record_btrace_cpu.model,
+			       record_btrace_cpu.stepping);
+	  return;
+	}
+    }
+
+  error (_("Internal error: bad cpu state."));
+}
+
+/* The "s record btrace bts" command.  */
 
 static void
 cmd_set_record_btrace_bts (const char *args, int from_tty)
@@ -3085,6 +3228,32 @@  replay."),
 			   &set_record_btrace_cmdlist,
 			   &show_record_btrace_cmdlist);
 
+  add_prefix_cmd ("cpu", class_support, cmd_set_record_btrace_cpu,
+		  _("\
+Set the cpu to be used for trace decode.\n\n\
+The format is \"<vendor>:<identifier>\" or \"none\" or \"auto\" (default).\n\
+For vendor \"intel\" the format is \"<family>/<model>[/<stepping>]\".\n\n\
+When decoding branch trace, enable errata workarounds for the specified cpu.\n\
+The default is \"auto\", which uses the cpu on which the trace was recorded.\n\
+When GDB does not support that cpu, this option can be used to enable\n\
+workarounds for a similar cpu that GDB supports.\n\n\
+When set to \"none\", errata workarounds are disabled."),
+		  &set_record_btrace_cpu_cmdlist,
+		  _("set record btrace cpu "), 1,
+		  &set_record_btrace_cmdlist);
+
+  add_cmd ("auto", class_support, cmd_set_record_btrace_cpu_auto, _("\
+Automatically determine the cpu to be used for trace decode."),
+	   &set_record_btrace_cpu_cmdlist);
+
+  add_cmd ("none", class_support, cmd_set_record_btrace_cpu_none, _("\
+Do not enable errata workarounds for trace decode."),
+	   &set_record_btrace_cpu_cmdlist);
+
+  add_cmd ("cpu", class_support, cmd_show_record_btrace_cpu, _("\
+Show the cpu to be used for trace decode."),
+	   &show_record_btrace_cmdlist);
+
   add_prefix_cmd ("bts", class_support, cmd_set_record_btrace_bts,
 		  _("Set record btrace bts options"),
 		  &set_record_btrace_bts_cmdlist,
diff --git a/gdb/record-btrace.h b/gdb/record-btrace.h
index ba66719..e17b643 100644
--- a/gdb/record-btrace.h
+++ b/gdb/record-btrace.h
@@ -25,4 +25,8 @@ 
 /* Push the record_btrace target.  */
 extern void record_btrace_push_target (void);
 
+/* Return the cpu configured by the user via "set btrace cpu".  Returns
+   NULL if the cpu was configured as auto.  */
+extern const struct btrace_cpu *record_btrace_get_cpu (void);
+
 #endif /* RECORD_BTRACE_H */
diff --git a/gdb/testsuite/gdb.btrace/cpu.exp b/gdb/testsuite/gdb.btrace/cpu.exp
new file mode 100644
index 0000000..bd4245c
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/cpu.exp
@@ -0,0 +1,76 @@ 
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2018 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+gdb_start
+
+proc test_good { arg } {
+    gdb_test_no_output "set record btrace cpu $arg" "set cpu $arg"
+    gdb_test "show record btrace cpu" "btrace cpu is '$arg'\." \
+        "show cpu $arg"
+}
+
+proc test_bad { arg current } {
+    gdb_test "set record btrace cpu $arg" \
+        "Bad format\.  See \"help set record btrace cpu\"\." \
+        "set cpu $arg"
+    gdb_test "show record btrace cpu" "btrace cpu is '$current'\." \
+        "show cpu $arg"
+}
+
+proc test_junk { arg junk current } {
+    gdb_test "set record btrace cpu $arg" \
+        "Trailing junk: '$junk'\." \
+        "set cpu $arg"
+    gdb_test "show record btrace cpu" "btrace cpu is '$current'\." \
+        "show cpu $arg"
+}
+
+gdb_test "show record btrace cpu" "btrace cpu is 'auto'\." "default cpu"
+
+gdb_test "set record" \
+    "\"set record\" must be followed by an apporpriate subcommand.*" \
+    "set record"
+gdb_test "set record btrace" \
+    "\"set record btrace\" must be followed by an apporpriate subcommand.*" \
+    "set record btrace"
+test_bad "" "auto"
+
+test_good "intel: 0/0"
+test_good "intel: 0/0/1"
+
+# We omit a zero stepping in the output.
+gdb_test_no_output "set record btrace cpu intel: 0/0/0" \
+    "set cpu intel: 0/0/0"
+gdb_test "show record btrace cpu" "btrace cpu is 'intel: 0/0'\." \
+    "show cpu intel: 0/0/0"
+
+test_good "auto"
+test_good "none"
+
+test_bad "intel: foo" "none"
+test_bad "intel: 0" "none"
+test_bad "intel: 0/" "none"
+test_bad "intel: 0/foo" "none"
+test_bad "intel: foo/bar" "none"
+test_bad "intel: foo/0" "none"
+test_bad "intel: 0x0/0" "none"
+
+test_junk "intel: 0/0 foo" " foo" "none"
+test_junk "intel: 0/0x0" "x0" "none"
+test_junk "intel: 0/0/foo" "/foo" "none"
+test_junk "intel: 0/0/0 foo" " foo" "none"
+test_junk "intel: 0/0/0x0" "x0" "none"