public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [patch v8 14/24] record-btrace: supply register target methods
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
  2013-12-12  9:15 ` [patch v8 03/24] gdbarch: add instruction predicate methods Markus Metzger
  2013-12-12  9:15 ` [patch v8 01/24] btrace, linux: fix memory leak when reading branch trace Markus Metzger
@ 2013-12-12  9:15 ` Markus Metzger
  2013-12-12  9:15 ` [patch v8 16/24] record-btrace, frame: supply target-specific unwinder Markus Metzger
                   ` (21 subsequent siblings)
  24 siblings, 0 replies; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:15 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

Supply target methods to allow reading the PC.  Forbid anything else.

2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

	* record-btrace.c (record_btrace_fetch_registers,
	record_btrace_store_registers,
	record_btrace_to_prepare_to_store): New.
	(init_record_btrace_ops): Add the above.


---
 gdb/record-btrace.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 92 insertions(+)

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index fc692aa..26ae6d8 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -32,6 +32,7 @@
 #include "ui-out.h"
 #include "symtab.h"
 #include "filenames.h"
+#include "regcache.h"
 
 /* The target_ops of record-btrace.  */
 static struct target_ops record_btrace_ops;
@@ -751,6 +752,94 @@ record_btrace_is_replaying (void)
   return 0;
 }
 
+/* The to_fetch_registers method of target record-btrace.  */
+
+static void
+record_btrace_fetch_registers (struct target_ops *ops,
+			       struct regcache *regcache, int regno)
+{
+  struct btrace_insn_iterator *replay;
+  struct thread_info *tp;
+
+  tp = find_thread_ptid (inferior_ptid);
+  gdb_assert (tp != NULL);
+
+  replay = tp->btrace.replay;
+  if (replay != NULL)
+    {
+      const struct btrace_insn *insn;
+      struct gdbarch *gdbarch;
+      int pcreg;
+
+      gdbarch = get_regcache_arch (regcache);
+      pcreg = gdbarch_pc_regnum (gdbarch);
+      if (pcreg < 0)
+	return;
+
+      /* We can only provide the PC register.  */
+      if (regno >= 0 && regno != pcreg)
+	return;
+
+      insn = btrace_insn_get (replay);
+      gdb_assert (insn != NULL);
+
+      regcache_raw_supply (regcache, regno, &insn->pc);
+    }
+  else
+    {
+      struct target_ops *t;
+
+      for (t = ops->beneath; t != NULL; t = t->beneath)
+	if (t->to_fetch_registers != NULL)
+	  {
+	    t->to_fetch_registers (t, regcache, regno);
+	    break;
+	  }
+    }
+}
+
+/* The to_store_registers method of target record-btrace.  */
+
+static void
+record_btrace_store_registers (struct target_ops *ops,
+			       struct regcache *regcache, int regno)
+{
+  struct target_ops *t;
+
+  if (record_btrace_is_replaying ())
+    error (_("This record target does not allow writing registers."));
+
+  gdb_assert (may_write_registers != 0);
+
+  for (t = ops->beneath; t != NULL; t = t->beneath)
+    if (t->to_store_registers != NULL)
+      {
+	t->to_store_registers (t, regcache, regno);
+	return;
+      }
+
+  noprocess ();
+}
+
+/* The to_prepare_to_store method of target record-btrace.  */
+
+static void
+record_btrace_prepare_to_store (struct target_ops *ops,
+				struct regcache *regcache)
+{
+  struct target_ops *t;
+
+  if (record_btrace_is_replaying ())
+    return;
+
+  for (t = ops->beneath; t != NULL; t = t->beneath)
+    if (t->to_prepare_to_store != NULL)
+      {
+	t->to_prepare_to_store (t, regcache);
+	return;
+      }
+}
+
 /* Initialize the record-btrace target ops.  */
 
 static void
@@ -778,6 +867,9 @@ init_record_btrace_ops (void)
   ops->to_call_history_from = record_btrace_call_history_from;
   ops->to_call_history_range = record_btrace_call_history_range;
   ops->to_record_is_replaying = record_btrace_is_replaying;
+  ops->to_fetch_registers = record_btrace_fetch_registers;
+  ops->to_store_registers = record_btrace_store_registers;
+  ops->to_prepare_to_store = record_btrace_prepare_to_store;
   ops->to_stratum = record_stratum;
   ops->to_magic = OPS_MAGIC;
 }
-- 
1.8.3.1

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

* [patch v8 03/24] gdbarch: add instruction predicate methods
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
@ 2013-12-12  9:15 ` Markus Metzger
  2013-12-12  9:15 ` [patch v8 01/24] btrace, linux: fix memory leak when reading branch trace Markus Metzger
                   ` (23 subsequent siblings)
  24 siblings, 0 replies; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:15 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches, Pedro Alves

Add new methods to gdbarch for analyzing the instruction at a given address.
Implement those methods for i386 and amd64 architectures.

This is needed by "record btrace" to detect function calls in the
execution trace.

CC:  Pedro Alves  <palves@redhat.com>

2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

	* amd64-tdep.c (amd64_classify_insn_at, amd64_insn_is_call,
	amd64_insn_is_ret, amd64_insn_is_jump, amd64_jmp_p): New.
	(amd64_init_abi): Add insn_is_call, insn_is_ret, and insn_is_jump
	to gdbarch.
	* i386-tdep.c (i386_insn_is_call, i386_insn_is_ret,
	i386_insn_is_jump, i386_jmp_p): New.
	(i386_gdbarch_init): Add insn_is_call, insn_is_ret, and
	insn_is_jump to gdbarch.
	* gdbarch.sh (insn_is_call, insn_is_ret, insn_is_jump): New.
	* gdbarch.h: Regenerated.
	* gdbarch.c: Regenerated.
	* arch-utils.h (default_insn_is_call, default_insn_is_ret,
	default_insn_is_jump): New.
	* arch-utils.c (default_insn_is_call, default_insn_is_ret,
	default_insn_is_jump): New.


---
 gdb/amd64-tdep.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/arch-utils.c | 15 ++++++++++++
 gdb/arch-utils.h |  4 +++
 gdb/gdbarch.c    | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/gdbarch.h    | 18 ++++++++++++++
 gdb/gdbarch.sh   |  9 +++++++
 gdb/i386-tdep.c  | 59 ++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 247 insertions(+)

diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 19968fc..c6098fa 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -1345,6 +1345,24 @@ amd64_absolute_jmp_p (const struct amd64_insn *details)
   return 0;
 }
 
+/* Return non-zero if the instruction DETAILS is a jump, zero otherwise.  */
+
+static int
+amd64_jmp_p (const struct amd64_insn *details)
+{
+  const gdb_byte *insn = &details->raw_insn[details->opcode_offset];
+
+  /* jump short, relative.  */
+  if (insn[0] == 0xeb)
+    return 1;
+
+  /* jump near, relative.  */
+  if (insn[0] == 0xe9)
+    return 1;
+
+  return amd64_absolute_jmp_p (details);
+}
+
 static int
 amd64_absolute_call_p (const struct amd64_insn *details)
 {
@@ -1416,6 +1434,52 @@ amd64_syscall_p (const struct amd64_insn *details, int *lengthp)
   return 0;
 }
 
+/* Classify the instruction at ADDR using PRED.
+   Throw an error if the memory can't be read.  */
+
+static int
+amd64_classify_insn_at (struct gdbarch *gdbarch, CORE_ADDR addr,
+			int (*pred) (const struct amd64_insn *))
+{
+  struct amd64_insn details;
+  gdb_byte *buf;
+  int len, classification;
+
+  len = gdbarch_max_insn_length (gdbarch);
+  buf = alloca (len);
+
+  read_code (addr, buf, len);
+  amd64_get_insn_details (buf, &details);
+
+  classification = pred (&details);
+
+  return classification;
+}
+
+/* The gdbarch insn_is_call method.  */
+
+static int
+amd64_insn_is_call (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  return amd64_classify_insn_at (gdbarch, addr, amd64_call_p);
+}
+
+/* The gdbarch insn_is_ret method.  */
+
+static int
+amd64_insn_is_ret (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  return amd64_classify_insn_at (gdbarch, addr, amd64_ret_p);
+}
+
+/* The gdbarch insn_is_jump method.  */
+
+static int
+amd64_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  return amd64_classify_insn_at (gdbarch, addr, amd64_jmp_p);
+}
+
 /* Fix up the state of registers and memory after having single-stepped
    a displaced instruction.  */
 
@@ -2952,6 +3016,9 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 				      i386_stap_is_single_operand);
   set_gdbarch_stap_parse_special_token (gdbarch,
 					i386_stap_parse_special_token);
+  set_gdbarch_insn_is_call (gdbarch, amd64_insn_is_call);
+  set_gdbarch_insn_is_ret (gdbarch, amd64_insn_is_ret);
+  set_gdbarch_insn_is_jump (gdbarch, amd64_insn_is_jump);
 }
 \f
 
diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c
index cb566ad..8fcfe91 100644
--- a/gdb/arch-utils.c
+++ b/gdb/arch-utils.c
@@ -804,6 +804,21 @@ default_return_in_first_hidden_param_p (struct gdbarch *gdbarch,
   return language_pass_by_reference (type);
 }
 
+int default_insn_is_call (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  return 0;
+}
+
+int default_insn_is_ret (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  return 0;
+}
+
+int default_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  return 0;
+}
+
 /* */
 
 /* -Wmissing-prototypes */
diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h
index 3f0e64f..2cf83d4 100644
--- a/gdb/arch-utils.h
+++ b/gdb/arch-utils.h
@@ -170,4 +170,8 @@ extern const char *default_auto_wide_charset (void);
 
 extern int default_return_in_first_hidden_param_p (struct gdbarch *,
 						   struct type *);
+
+extern int default_insn_is_call (struct gdbarch *, CORE_ADDR);
+extern int default_insn_is_ret (struct gdbarch *, CORE_ADDR);
+extern int default_insn_is_jump (struct gdbarch *, CORE_ADDR);
 #endif
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index fb3595f..66a3c39 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -288,6 +288,9 @@ struct gdbarch
   gdbarch_core_info_proc_ftype *core_info_proc;
   gdbarch_iterate_over_objfiles_in_search_order_ftype *iterate_over_objfiles_in_search_order;
   struct ravenscar_arch_ops * ravenscar_ops;
+  gdbarch_insn_is_call_ftype *insn_is_call;
+  gdbarch_insn_is_ret_ftype *insn_is_ret;
+  gdbarch_insn_is_jump_ftype *insn_is_jump;
 };
 
 
@@ -461,6 +464,9 @@ struct gdbarch startup_gdbarch =
   0,  /* core_info_proc */
   default_iterate_over_objfiles_in_search_order,  /* iterate_over_objfiles_in_search_order */
   NULL,  /* ravenscar_ops */
+  default_insn_is_call,  /* insn_is_call */
+  default_insn_is_ret,  /* insn_is_ret */
+  default_insn_is_jump,  /* insn_is_jump */
   /* startup_gdbarch() */
 };
 
@@ -552,6 +558,9 @@ gdbarch_alloc (const struct gdbarch_info *info,
   gdbarch->gen_return_address = default_gen_return_address;
   gdbarch->iterate_over_objfiles_in_search_order = default_iterate_over_objfiles_in_search_order;
   gdbarch->ravenscar_ops = NULL;
+  gdbarch->insn_is_call = default_insn_is_call;
+  gdbarch->insn_is_ret = default_insn_is_ret;
+  gdbarch->insn_is_jump = default_insn_is_jump;
   /* gdbarch_alloc() */
 
   return gdbarch;
@@ -766,6 +775,12 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of core_info_proc, has predicate.  */
   /* Skip verify of iterate_over_objfiles_in_search_order, invalid_p == 0 */
   /* Skip verify of ravenscar_ops, invalid_p == 0 */
+  if (gdbarch->insn_is_call == default_insn_is_call)
+    fprintf_unfiltered (log, "\n\tinsn_is_call");
+  if (gdbarch->insn_is_ret == default_insn_is_ret)
+    fprintf_unfiltered (log, "\n\tinsn_is_ret");
+  if (gdbarch->insn_is_jump == default_insn_is_jump)
+    fprintf_unfiltered (log, "\n\tinsn_is_jump");
   buf = ui_file_xstrdup (log, &length);
   make_cleanup (xfree, buf);
   if (length > 0)
@@ -1099,6 +1114,15 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
                       "gdbarch_dump: inner_than = <%s>\n",
                       host_address_to_string (gdbarch->inner_than));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: insn_is_call = <%s>\n",
+                      host_address_to_string (gdbarch->insn_is_call));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: insn_is_jump = <%s>\n",
+                      host_address_to_string (gdbarch->insn_is_jump));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: insn_is_ret = <%s>\n",
+                      host_address_to_string (gdbarch->insn_is_ret));
+  fprintf_unfiltered (file,
                       "gdbarch_dump: int_bit = %s\n",
                       plongest (gdbarch->int_bit));
   fprintf_unfiltered (file,
@@ -4422,6 +4446,57 @@ set_gdbarch_ravenscar_ops (struct gdbarch *gdbarch,
   gdbarch->ravenscar_ops = ravenscar_ops;
 }
 
+int
+gdbarch_insn_is_call (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->insn_is_call != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_insn_is_call called\n");
+  return gdbarch->insn_is_call (gdbarch, addr);
+}
+
+void
+set_gdbarch_insn_is_call (struct gdbarch *gdbarch,
+                          gdbarch_insn_is_call_ftype insn_is_call)
+{
+  gdbarch->insn_is_call = insn_is_call;
+}
+
+int
+gdbarch_insn_is_ret (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->insn_is_ret != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_insn_is_ret called\n");
+  return gdbarch->insn_is_ret (gdbarch, addr);
+}
+
+void
+set_gdbarch_insn_is_ret (struct gdbarch *gdbarch,
+                         gdbarch_insn_is_ret_ftype insn_is_ret)
+{
+  gdbarch->insn_is_ret = insn_is_ret;
+}
+
+int
+gdbarch_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->insn_is_jump != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_insn_is_jump called\n");
+  return gdbarch->insn_is_jump (gdbarch, addr);
+}
+
+void
+set_gdbarch_insn_is_jump (struct gdbarch *gdbarch,
+                          gdbarch_insn_is_jump_ftype insn_is_jump)
+{
+  gdbarch->insn_is_jump = insn_is_jump;
+}
+
 
 /* Keep a registry of per-architecture data-pointers required by GDB
    modules.  */
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index b58efc8..c8a8a12 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -1261,6 +1261,24 @@ extern void set_gdbarch_iterate_over_objfiles_in_search_order (struct gdbarch *g
 extern struct ravenscar_arch_ops * gdbarch_ravenscar_ops (struct gdbarch *gdbarch);
 extern void set_gdbarch_ravenscar_ops (struct gdbarch *gdbarch, struct ravenscar_arch_ops * ravenscar_ops);
 
+/* Return non-zero if the instruction at ADDR is a call; zero otherwise. */
+
+typedef int (gdbarch_insn_is_call_ftype) (struct gdbarch *gdbarch, CORE_ADDR addr);
+extern int gdbarch_insn_is_call (struct gdbarch *gdbarch, CORE_ADDR addr);
+extern void set_gdbarch_insn_is_call (struct gdbarch *gdbarch, gdbarch_insn_is_call_ftype *insn_is_call);
+
+/* Return non-zero if the instruction at ADDR is a return; zero otherwise. */
+
+typedef int (gdbarch_insn_is_ret_ftype) (struct gdbarch *gdbarch, CORE_ADDR addr);
+extern int gdbarch_insn_is_ret (struct gdbarch *gdbarch, CORE_ADDR addr);
+extern void set_gdbarch_insn_is_ret (struct gdbarch *gdbarch, gdbarch_insn_is_ret_ftype *insn_is_ret);
+
+/* Return non-zero if the instruction at ADDR is a jump; zero otherwise. */
+
+typedef int (gdbarch_insn_is_jump_ftype) (struct gdbarch *gdbarch, CORE_ADDR addr);
+extern int gdbarch_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr);
+extern void set_gdbarch_insn_is_jump (struct gdbarch *gdbarch, gdbarch_insn_is_jump_ftype *insn_is_jump);
+
 /* Definition for an unknown syscall, used basically in error-cases.  */
 #define UNKNOWN_SYSCALL (-1)
 
diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh
index a678a78..a9e08ba 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -988,6 +988,15 @@ m:void:iterate_over_objfiles_in_search_order:iterate_over_objfiles_in_search_ord
 
 # Ravenscar arch-dependent ops.
 v:struct ravenscar_arch_ops *:ravenscar_ops:::NULL:NULL::0:host_address_to_string (gdbarch->ravenscar_ops)
+
+# Return non-zero if the instruction at ADDR is a call; zero otherwise.
+m:int:insn_is_call:CORE_ADDR addr:addr::default_insn_is_call
+
+# Return non-zero if the instruction at ADDR is a return; zero otherwise.
+m:int:insn_is_ret:CORE_ADDR addr:addr::default_insn_is_ret
+
+# Return non-zero if the instruction at ADDR is a jump; zero otherwise.
+m:int:insn_is_jump:CORE_ADDR addr:addr::default_insn_is_jump
 EOF
 }
 
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index a1a4453..326e4c3 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -530,6 +530,22 @@ i386_absolute_jmp_p (const gdb_byte *insn)
   return 0;
 }
 
+/* Return non-zero if INSN is a jump, zero otherwise.  */
+
+static int
+i386_jmp_p (const gdb_byte *insn)
+{
+  /* jump short, relative.  */
+  if (insn[0] == 0xeb)
+    return 1;
+
+  /* jump near, relative.  */
+  if (insn[0] == 0xe9)
+    return 1;
+
+  return i386_absolute_jmp_p (insn);
+}
+
 static int
 i386_absolute_call_p (const gdb_byte *insn)
 {
@@ -601,6 +617,45 @@ i386_syscall_p (const gdb_byte *insn, int *lengthp)
   return 0;
 }
 
+/* The gdbarch insn_is_call method.  */
+
+static int
+i386_insn_is_call (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  gdb_byte buf[I386_MAX_INSN_LEN], *insn;
+
+  read_code (addr, buf, I386_MAX_INSN_LEN);
+  insn = i386_skip_prefixes (buf, I386_MAX_INSN_LEN);
+
+  return i386_call_p (insn);
+}
+
+/* The gdbarch insn_is_ret method.  */
+
+static int
+i386_insn_is_ret (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  gdb_byte buf[I386_MAX_INSN_LEN], *insn;
+
+  read_code (addr, buf, I386_MAX_INSN_LEN);
+  insn = i386_skip_prefixes (buf, I386_MAX_INSN_LEN);
+
+  return i386_ret_p (insn);
+}
+
+/* The gdbarch insn_is_jump method.  */
+
+static int
+i386_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  gdb_byte buf[I386_MAX_INSN_LEN], *insn;
+
+  read_code (addr, buf, I386_MAX_INSN_LEN);
+  insn = i386_skip_prefixes (buf, I386_MAX_INSN_LEN);
+
+  return i386_jmp_p (insn);
+}
+
 /* Some kernels may run one past a syscall insn, so we have to cope.
    Otherwise this is just simple_displaced_step_copy_insn.  */
 
@@ -7966,6 +8021,10 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 
   set_gdbarch_gen_return_address (gdbarch, i386_gen_return_address);
 
+  set_gdbarch_insn_is_call (gdbarch, i386_insn_is_call);
+  set_gdbarch_insn_is_ret (gdbarch, i386_insn_is_ret);
+  set_gdbarch_insn_is_jump (gdbarch, i386_insn_is_jump);
+
   /* Hook in ABI-specific overrides, if they have been registered.  */
   info.tdep_info = (void *) tdesc_data;
   gdbarch_init_osabi (info, gdbarch);
-- 
1.8.3.1

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

* [patch v8 16/24] record-btrace, frame: supply target-specific unwinder
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (2 preceding siblings ...)
  2013-12-12  9:15 ` [patch v8 14/24] record-btrace: supply register target methods Markus Metzger
@ 2013-12-12  9:15 ` Markus Metzger
  2013-12-12  9:16 ` [patch v8 06/24] btrace: change branch trace data structure Markus Metzger
                   ` (20 subsequent siblings)
  24 siblings, 0 replies; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:15 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

Supply a target-specific frame unwinder for the record-btrace target that does
not allow unwinding while replaying.

2013-02-11  Jan Kratochvil  <jan.kratochvil@redhat.com>
            Markus Metzger  <markus.t.metzger@intel.com>

gdb/
	* record-btrace.c: Include frame-unwind.h.
	(record_btrace_frame_unwind_stop_reason,
	record_btrace_frame_this_id, record_btrace_frame_prev_register,
	record_btrace_frame_sniffer, record_btrace_frame_unwind):
	New.
	(init_record_btrace_ops): Install it.


---
 gdb/record-btrace.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 26ae6d8..cf30e17 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -33,6 +33,7 @@
 #include "symtab.h"
 #include "filenames.h"
 #include "regcache.h"
+#include "frame-unwind.h"
 
 /* The target_ops of record-btrace.  */
 static struct target_ops record_btrace_ops;
@@ -840,6 +841,68 @@ record_btrace_prepare_to_store (struct target_ops *ops,
       }
 }
 
+/* Implement stop_reason method for record_btrace_frame_unwind.  */
+
+static enum unwind_stop_reason
+record_btrace_frame_unwind_stop_reason (struct frame_info *this_frame,
+					void **this_cache)
+{
+  return UNWIND_UNAVAILABLE;
+}
+
+/* Implement this_id method for record_btrace_frame_unwind.  */
+
+static void
+record_btrace_frame_this_id (struct frame_info *this_frame, void **this_cache,
+			     struct frame_id *this_id)
+{
+  /* Leave there the outer_frame_id value.  */
+}
+
+/* Implement prev_register method for record_btrace_frame_unwind.  */
+
+static struct value *
+record_btrace_frame_prev_register (struct frame_info *this_frame,
+				   void **this_cache,
+				   int regnum)
+{
+  throw_error (NOT_AVAILABLE_ERROR,
+              _("Registers are not available in btrace record history"));
+}
+
+/* Implement sniffer method for record_btrace_frame_unwind.  */
+
+static int
+record_btrace_frame_sniffer (const struct frame_unwind *self,
+			     struct frame_info *this_frame,
+			     void **this_cache)
+{
+  struct thread_info *tp;
+  struct btrace_thread_info *btinfo;
+  struct btrace_insn_iterator *replay;
+
+  /* THIS_FRAME does not contain a reference to its thread.  */
+  tp = find_thread_ptid (inferior_ptid);
+  gdb_assert (tp != NULL);
+
+  return btrace_is_replaying (tp);
+}
+
+/* btrace recording does not store previous memory content, neither the stack
+   frames content.  Any unwinding would return errorneous results as the stack
+   contents no longer matches the changed PC value restored from history.
+   Therefore this unwinder reports any possibly unwound registers as
+   <unavailable>.  */
+
+static const struct frame_unwind record_btrace_frame_unwind =
+{
+  NORMAL_FRAME,
+  record_btrace_frame_unwind_stop_reason,
+  record_btrace_frame_this_id,
+  record_btrace_frame_prev_register,
+  NULL,
+  record_btrace_frame_sniffer
+};
 /* Initialize the record-btrace target ops.  */
 
 static void
@@ -870,6 +933,7 @@ init_record_btrace_ops (void)
   ops->to_fetch_registers = record_btrace_fetch_registers;
   ops->to_store_registers = record_btrace_store_registers;
   ops->to_prepare_to_store = record_btrace_prepare_to_store;
+  ops->to_get_unwinder = &record_btrace_frame_unwind;
   ops->to_stratum = record_stratum;
   ops->to_magic = OPS_MAGIC;
 }
-- 
1.8.3.1

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

* [patch v8 01/24] btrace, linux: fix memory leak when reading branch trace
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
  2013-12-12  9:15 ` [patch v8 03/24] gdbarch: add instruction predicate methods Markus Metzger
@ 2013-12-12  9:15 ` Markus Metzger
  2013-12-12  9:15 ` [patch v8 14/24] record-btrace: supply register target methods Markus Metzger
                   ` (22 subsequent siblings)
  24 siblings, 0 replies; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:15 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

When it takes more than one iteration to read the BTS trace, the trace from the
previous iteration is leaked.  Fix it.

2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

	* common/linux-btrace.c (linux_read_btrace): Free trace from
	previous iteration.


---
 gdb/common/linux-btrace.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/gdb/common/linux-btrace.c b/gdb/common/linux-btrace.c
index 7e20745..2e0cd8e 100644
--- a/gdb/common/linux-btrace.c
+++ b/gdb/common/linux-btrace.c
@@ -520,6 +520,9 @@ linux_read_btrace (struct btrace_target_info *tinfo,
     {
       data_head = header->data_head;
 
+      /* Delete any leftover trace from the previous iteration.  */
+      VEC_free (btrace_block_s, btrace);
+
       /* If there's new trace, let's read it.  */
       if (data_head != tinfo->data_head)
 	{
-- 
1.8.3.1

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

* [patch v8 00/24] record-btrace: reverse
@ 2013-12-12  9:15 Markus Metzger
  2013-12-12  9:15 ` [patch v8 03/24] gdbarch: add instruction predicate methods Markus Metzger
                   ` (24 more replies)
  0 siblings, 25 replies; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:15 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

This is a small update of v7 addressing review comments from Jan and Pedro.

Jan agreed to implement "record goto" by directly navigating to the desired
instruction in the execution history.  In particular, we do not use to_wait
and to_resume.  This is similar to the implementation for s/w recording.

Markus Metzger (24):
  btrace, linux: fix memory leak when reading branch trace
  btrace: uppercase btrace_read_type
  gdbarch: add instruction predicate methods
  frame:  add frame_is_tailcall function
  frame: artificial frame id's
  btrace: change branch trace data structure
  record-btrace: fix insn range in function call history
  record-btrace: start counting at one
  btrace: increase buffer size
  record-btrace: optionally indent function call history
  record-btrace: make ranges include begin and end
  btrace: add replay position to btrace thread info
  target: add ops parameter to to_prepare_to_store method
  record-btrace: supply register target methods
  frame, backtrace: allow targets to supply a frame unwinder
  record-btrace, frame: supply target-specific unwinder
  record-btrace: provide xfer_partial target method
  record-btrace: add to_wait and to_resume target methods.
  record-btrace: provide target_find_new_threads method
  record-btrace: add record goto target methods
  record-btrace: extend unwinder
  btrace, gdbserver: read branch trace incrementally
  record-btrace: show trace from enable location
  record-btrace: add (reverse-)stepping support

 gdb/NEWS                                           |   18 +
 gdb/amd64-linux-nat.c                              |    6 +-
 gdb/amd64-tdep.c                                   |   67 +
 gdb/arch-utils.c                                   |   15 +
 gdb/arch-utils.h                                   |    4 +
 gdb/btrace.c                                       | 1615 ++++++++++++++++----
 gdb/btrace.h                                       |  269 +++-
 gdb/common/btrace-common.h                         |   29 +-
 gdb/common/linux-btrace.c                          |  125 +-
 gdb/common/linux-btrace.h                          |   15 +-
 gdb/doc/gdb.texinfo                                |   32 +-
 gdb/dwarf2-frame-tailcall.c                        |    4 +-
 gdb/dwarf2-frame.c                                 |   14 +-
 gdb/frame-unwind.c                                 |   86 +-
 gdb/frame.c                                        |   68 +-
 gdb/frame.h                                        |   18 +-
 gdb/gdbarch.c                                      |   75 +
 gdb/gdbarch.h                                      |   18 +
 gdb/gdbarch.sh                                     |    9 +
 gdb/gdbserver/linux-low.c                          |   36 +-
 gdb/gdbserver/server.c                             |   15 +-
 gdb/gdbserver/target.h                             |    9 +-
 gdb/i386-linux-nat.c                               |    6 +-
 gdb/i386-tdep.c                                    |   59 +
 gdb/inf-child.c                                    |    2 +-
 gdb/infcmd.c                                       |    6 +-
 gdb/monitor.c                                      |    2 +-
 gdb/ravenscar-thread.c                             |    7 +-
 gdb/record-btrace.c                                | 1473 +++++++++++++++---
 gdb/record-full.c                                  |    3 +-
 gdb/record.c                                       |    4 +
 gdb/record.h                                       |    7 +
 gdb/remote-m32r-sdi.c                              |    2 +-
 gdb/remote-mips.c                                  |    5 +-
 gdb/remote.c                                       |   32 +-
 gdb/stack.c                                        |    2 +-
 gdb/target.c                                       |   65 +-
 gdb/target.h                                       |   35 +-
 gdb/testsuite/gdb.btrace/Makefile.in               |    3 +-
 gdb/testsuite/gdb.btrace/delta.exp                 |   76 +
 gdb/testsuite/gdb.btrace/exception.cc              |   56 +
 gdb/testsuite/gdb.btrace/exception.exp             |   67 +
 gdb/testsuite/gdb.btrace/finish.exp                |   70 +
 gdb/testsuite/gdb.btrace/function_call_history.exp |  329 ++--
 gdb/testsuite/gdb.btrace/instruction_history.exp   |   77 +-
 gdb/testsuite/gdb.btrace/multi-thread-step.c       |   53 +
 gdb/testsuite/gdb.btrace/multi-thread-step.exp     |   99 ++
 gdb/testsuite/gdb.btrace/next.exp                  |   89 ++
 gdb/testsuite/gdb.btrace/nexti.exp                 |   89 ++
 gdb/testsuite/gdb.btrace/record_goto.c             |   51 +
 gdb/testsuite/gdb.btrace/record_goto.exp           |  177 +++
 gdb/testsuite/gdb.btrace/rn-dl-bind.c              |   37 +
 gdb/testsuite/gdb.btrace/rn-dl-bind.exp            |   48 +
 gdb/testsuite/gdb.btrace/step.exp                  |  113 ++
 gdb/testsuite/gdb.btrace/stepi.exp                 |  114 ++
 gdb/testsuite/gdb.btrace/tailcall.exp              |  103 ++
 gdb/testsuite/gdb.btrace/unknown_functions.c       |   45 +
 gdb/testsuite/gdb.btrace/unknown_functions.exp     |   60 +
 gdb/testsuite/gdb.btrace/x86-record_goto.S         |  355 +++++
 gdb/testsuite/gdb.btrace/x86-tailcall.S            |  279 ++++
 gdb/testsuite/gdb.btrace/x86-tailcall.c            |   39 +
 61 files changed, 5807 insertions(+), 879 deletions(-)
 create mode 100644 gdb/testsuite/gdb.btrace/delta.exp
 create mode 100644 gdb/testsuite/gdb.btrace/exception.cc
 create mode 100755 gdb/testsuite/gdb.btrace/exception.exp
 create mode 100644 gdb/testsuite/gdb.btrace/finish.exp
 create mode 100644 gdb/testsuite/gdb.btrace/multi-thread-step.c
 create mode 100644 gdb/testsuite/gdb.btrace/multi-thread-step.exp
 create mode 100644 gdb/testsuite/gdb.btrace/next.exp
 create mode 100644 gdb/testsuite/gdb.btrace/nexti.exp
 create mode 100644 gdb/testsuite/gdb.btrace/record_goto.c
 create mode 100644 gdb/testsuite/gdb.btrace/record_goto.exp
 create mode 100644 gdb/testsuite/gdb.btrace/rn-dl-bind.c
 create mode 100644 gdb/testsuite/gdb.btrace/rn-dl-bind.exp
 create mode 100644 gdb/testsuite/gdb.btrace/step.exp
 create mode 100644 gdb/testsuite/gdb.btrace/stepi.exp
 create mode 100644 gdb/testsuite/gdb.btrace/tailcall.exp
 create mode 100644 gdb/testsuite/gdb.btrace/unknown_functions.c
 create mode 100644 gdb/testsuite/gdb.btrace/unknown_functions.exp
 create mode 100644 gdb/testsuite/gdb.btrace/x86-record_goto.S
 create mode 100644 gdb/testsuite/gdb.btrace/x86-tailcall.S
 create mode 100644 gdb/testsuite/gdb.btrace/x86-tailcall.c

-- 
1.8.3.1

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

* [patch v8 17/24] record-btrace: provide xfer_partial target method
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (18 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 22/24] btrace, gdbserver: read branch trace incrementally Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-13 18:43   ` Pedro Alves
  2013-12-12  9:16 ` [patch v8 19/24] record-btrace: provide target_find_new_threads method Markus Metzger
                   ` (4 subsequent siblings)
  24 siblings, 1 reply; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

Provide the xfer_partial target method for the btrace record target.

Only allow memory accesses to readonly memory while we're replaying.

2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

	* record-btrace.c (record_btrace_xfer_partial): New.
	(init_record_btrace_ops): Initialize xfer_partial.


---
 gdb/record-btrace.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index cf30e17..ec99ba2 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -753,6 +753,56 @@ record_btrace_is_replaying (void)
   return 0;
 }
 
+/* The to_xfer_partial method of target record-btrace.  */
+
+static LONGEST
+record_btrace_xfer_partial (struct target_ops *ops, enum target_object object,
+			    const char *annex, gdb_byte *readbuf,
+			    const gdb_byte *writebuf, ULONGEST offset,
+			    LONGEST len)
+{
+  struct target_ops *t;
+
+  /* Filter out requests that don't make sense during replay.  */
+  if (record_btrace_is_replaying ())
+    {
+      switch (object)
+	{
+	case TARGET_OBJECT_MEMORY:
+	case TARGET_OBJECT_RAW_MEMORY:
+	case TARGET_OBJECT_STACK_MEMORY:
+	  {
+	    /* We allow reading readonly memory.  */
+	    struct target_section *section;
+
+	    section = target_section_by_addr (ops, offset);
+	    if (section != NULL)
+	      {
+		/* Check if the section we found is readonly.  */
+		if ((bfd_get_section_flags (section->the_bfd_section->owner,
+					    section->the_bfd_section)
+		     & SEC_READONLY) != 0)
+		  {
+		    /* Truncate the request to fit into this section.  */
+		    len = min (len, section->endaddr - offset);
+		    break;
+		  }
+	      }
+
+	    return -1;
+	  }
+	}
+    }
+
+  /* Forward the request.  */
+  for (t = ops->beneath; t != NULL; t = t->beneath)
+    if (t->to_xfer_partial != NULL)
+      return t->to_xfer_partial (t, object, annex, readbuf, writebuf,
+				 offset, len);
+
+  return -1;
+}
+
 /* The to_fetch_registers method of target record-btrace.  */
 
 static void
@@ -930,6 +980,7 @@ init_record_btrace_ops (void)
   ops->to_call_history_from = record_btrace_call_history_from;
   ops->to_call_history_range = record_btrace_call_history_range;
   ops->to_record_is_replaying = record_btrace_is_replaying;
+  ops->to_xfer_partial = record_btrace_xfer_partial;
   ops->to_fetch_registers = record_btrace_fetch_registers;
   ops->to_store_registers = record_btrace_store_registers;
   ops->to_prepare_to_store = record_btrace_prepare_to_store;
-- 
1.8.3.1

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

* [patch v8 05/24] frame: artificial frame id's
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (22 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 11/24] record-btrace: make ranges include begin and end Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-12 19:39   ` Pedro Alves
  2013-12-12 14:07 ` [patch v8 00/24] record-btrace: reverse Jan Kratochvil
  24 siblings, 1 reply; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches, Pedro Alves

At the moment, a frame must have a stack - except for the outer frame.

When we analyze the recorded execution for "record btrace" to detect
function calls and compute a back trace at some point in the recorded
execution history, we will end up with frames without a stack.

To prepare for this, support frame_id's without a stack component.

CC:  Pedro Alves  <palves@redhat.com>

2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

	* frame.h (frame_id_build_artificial): New.
	* frame.c (frame_id_build_artificial): New.
	(frame_id_p): An artificial frame is valid.
	(frame_id_eq): A frame is equal to itself.


---
 gdb/frame.c | 30 +++++++++++++++++++++++-------
 gdb/frame.h |  6 ++++++
 2 files changed, 29 insertions(+), 7 deletions(-)

diff --git a/gdb/frame.c b/gdb/frame.c
index ddd5e70..37d780e 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -526,6 +526,21 @@ frame_id_build_wild (CORE_ADDR stack_addr)
   return id;
 }
 
+/* See frame.h.  */
+
+struct frame_id
+frame_id_build_artificial (CORE_ADDR code_addr,
+			   CORE_ADDR special_addr)
+{
+  struct frame_id id = null_frame_id;
+
+  id.code_addr = code_addr;
+  id.code_addr_p = 1;
+  id.special_addr = special_addr;
+  id.special_addr_p = 1;
+  return id;
+}
+
 int
 frame_id_p (struct frame_id l)
 {
@@ -536,6 +551,9 @@ frame_id_p (struct frame_id l)
   /* outer_frame_id is also valid.  */
   if (!p && memcmp (&l, &outer_frame_id, sizeof (l)) == 0)
     p = 1;
+  /* An artificial frame is also valid.  */
+  if (!p && l.code_addr_p && l.special_addr_p)
+    p = 1;
   if (frame_debug)
     {
       fprintf_unfiltered (gdb_stdlog, "{ frame_id_p (l=");
@@ -559,13 +577,11 @@ frame_id_eq (struct frame_id l, struct frame_id r)
 {
   int eq;
 
-  if (!l.stack_addr_p && l.special_addr_p
-      && !r.stack_addr_p && r.special_addr_p)
-    /* The outermost frame marker is equal to itself.  This is the
-       dodgy thing about outer_frame_id, since between execution steps
-       we might step into another function - from which we can't
-       unwind either.  More thought required to get rid of
-       outer_frame_id.  */
+  if (memcmp (&l, &r, sizeof (l)) == 0)
+    /* Every frame is equal to itself.
+       This is the dodgy thing about outer_frame_id, since between execution
+       steps we might step into another function - from which we can't unwind
+       either.  More thought required to get rid of outer_frame_id.  */
     eq = 1;
   else if (!l.stack_addr_p || !r.stack_addr_p)
     /* Like a NaN, if either ID is invalid, the result is false.
diff --git a/gdb/frame.h b/gdb/frame.h
index e4e6b25..71f07dd 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -174,6 +174,12 @@ extern struct frame_id frame_id_build_special (CORE_ADDR stack_addr,
    as the special identifier address are set to indicate wild cards.  */
 extern struct frame_id frame_id_build_wild (CORE_ADDR stack_addr);
 
+/* Construct an artificial frame ID.  The first parameter is the frame's
+   constant code address (typically the function entry point), and the
+   second the frame's special identifier address.  */
+extern struct frame_id frame_id_build_artificial (CORE_ADDR code_addr,
+						  CORE_ADDR special_addr);
+
 /* Returns non-zero when L is a valid frame (a valid frame has a
    non-zero .base).  The outermost frame is valid even without an
    ID.  */
-- 
1.8.3.1

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

* [patch v8 22/24] btrace, gdbserver: read branch trace incrementally
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (17 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 23/24] record-btrace: show trace from enable location Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-13 19:46   ` Pedro Alves
  2013-12-12  9:16 ` [patch v8 17/24] record-btrace: provide xfer_partial target method Markus Metzger
                   ` (5 subsequent siblings)
  24 siblings, 1 reply; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches, Pedro Alves

Read branch trace data incrementally and extend the current trace rather than
discarding it and reading the entire trace buffer each time.

If the branch trace buffer overflowed, we can't extend the current trace so we
discard it and start anew by reading the entire branch trace buffer.

Reviewed-by: Eli Zaretskii
CC: Pedro Alves  <palves@redhat.com>

2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

	* common/linux-btrace.c (perf_event_read_bts, linux_read_btrace):
	Support delta reads.
	(linux_disable_btrace): Change return type.
	* common/linux-btrace.h (linux_read_btrace): Change parameters
	and return type to allow error reporting.  Update users.
	(linux_disable_btrace): Change return type.  Update users.
	* common/btrace-common.h (btrace_read_type) <BTRACE_READ_DELTA>:
	New.
	(btrace_error): New.
	(btrace_block) <begin>: Comment on BEGIN == 0.
	* btrace.c (btrace_compute_ftrace): Start from the end of
	the current trace.
	(btrace_stitch_trace, btrace_clear_history): New.
	(btrace_fetch): Read delta trace, return if replaying.
	(btrace_clear): Move clear history code to btrace_clear_history.
	(parse_xml_btrace): Throw an error if parsing failed.
	* target.h (struct target_ops) <to_read_btrace>: Change parameters
	and return type to allow error reporting.
	(target_read_btrace): Change parameters and return type to allow
	error reporting.
	* target.c (target_read_btrace): Update.
	* remote.c (remote_read_btrace): Support delta reads.  Pass
	errors on.

gdbserver/
	* target.h (target_ops) <read_btrace>: Change parameters and
	return type to allow error reporting.
	* server.c (handle_qxfer_btrace): Support delta reads.  Pass
	trace reading errors on.
	* linux-low.c (linux_low_read_btrace): Pass trace reading
	errors on.
	(linux_low_disable_btrace): New.


---
 gdb/NEWS                   |   4 ++
 gdb/amd64-linux-nat.c      |   6 +-
 gdb/btrace.c               | 155 ++++++++++++++++++++++++++++++++++++++++-----
 gdb/common/btrace-common.h |  27 +++++++-
 gdb/common/linux-btrace.c  |  97 +++++++++++++++++++---------
 gdb/common/linux-btrace.h  |  15 +++--
 gdb/doc/gdb.texinfo        |   8 +++
 gdb/gdbserver/linux-low.c  |  36 +++++++++--
 gdb/gdbserver/server.c     |  11 +++-
 gdb/gdbserver/target.h     |   9 ++-
 gdb/i386-linux-nat.c       |   6 +-
 gdb/remote.c               |  23 ++++---
 gdb/target.c               |   9 +--
 gdb/target.h               |  15 +++--
 14 files changed, 330 insertions(+), 91 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index e0ac447..f7927f3 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -245,6 +245,10 @@ qXfer:libraries-svr4:read's annex
   necessary for library list updating, resulting in significant
   speedup.
 
+qXfer:btrace:read's annex
+  The qXfer:btrace:read packet supports a new annex 'delta' to read
+  branch trace incrementally.
+
 * New features in the GDB remote stub, GDBserver
 
   ** GDBserver now supports target-assisted range stepping.  Currently
diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c
index b1676ac..d9acda3 100644
--- a/gdb/amd64-linux-nat.c
+++ b/gdb/amd64-linux-nat.c
@@ -1173,10 +1173,10 @@ amd64_linux_enable_btrace (ptid_t ptid)
 static void
 amd64_linux_disable_btrace (struct btrace_target_info *tinfo)
 {
-  int errcode = linux_disable_btrace (tinfo);
+  enum btrace_error errcode = linux_disable_btrace (tinfo);
 
-  if (errcode != 0)
-    error (_("Could not disable branch tracing: %s."), safe_strerror (errcode));
+  if (errcode != BTRACE_ERR_NONE)
+    error (_("Could not disable branch tracing."));
 }
 
 /* Teardown branch tracing.  */
diff --git a/gdb/btrace.c b/gdb/btrace.c
index 1f35796..b25f882 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -599,9 +599,9 @@ btrace_compute_ftrace (struct btrace_thread_info *btinfo,
   DEBUG ("compute ftrace");
 
   gdbarch = target_gdbarch ();
-  begin = NULL;
-  end = NULL;
-  level = INT_MAX;
+  begin = btinfo->begin;
+  end = btinfo->end;
+  level = begin != NULL ? -btinfo->level : INT_MAX;
   blk = VEC_length (btrace_block_s, btrace);
 
   while (blk != 0)
@@ -717,27 +717,157 @@ btrace_teardown (struct thread_info *tp)
   btrace_clear (tp);
 }
 
+/* Adjust the block trace in order to stitch old and new trace together.
+   BTRACE is the new delta trace between the last and the current stop.
+   BTINFO is the old branch trace until the last stop.
+   May modify BTRACE as well as the existing trace in BTINFO.
+   Return 0 on success, -1 otherwise.  */
+
+static int
+btrace_stitch_trace (VEC (btrace_block_s) **btrace,
+		     const struct btrace_thread_info *btinfo)
+{
+  struct btrace_function *last_bfun;
+  struct btrace_insn *last_insn;
+  btrace_block_s *first_new_block;
+
+  /* If we don't have trace, there's nothing to do.  */
+  if (VEC_empty (btrace_block_s, *btrace))
+    return 0;
+
+  last_bfun = btinfo->end;
+  gdb_assert (last_bfun != NULL);
+
+  /* Beware that block trace starts with the most recent block, so the
+     chronologically first block in the new trace is the last block in
+     the new trace's block vector.  */
+  first_new_block = VEC_last (btrace_block_s, *btrace);
+  last_insn = VEC_last (btrace_insn_s, last_bfun->insn);
+
+  /* If the current PC at the end of the block is the same as in our current
+     trace, there are two explanations:
+       1. we executed the instruction and some branch brought us back.
+       2. we have not made any progress.
+     In the first case, the delta trace vector should contain at least two
+     entries.
+     In the second case, the delta trace vector should contain exactly one
+     entry for the partial block containing the current PC.  Remove it.  */
+  if (first_new_block->end == last_insn->pc
+      && VEC_length (btrace_block_s, *btrace) == 1)
+    {
+      VEC_pop (btrace_block_s, *btrace);
+      return 0;
+    }
+
+  DEBUG ("stitching %s to %s", ftrace_print_insn_addr (last_insn),
+	 core_addr_to_string_nz (first_new_block->end));
+
+  /* Do a simple sanity check to make sure we don't accidentally end up
+     with a bad block.  This should not occur in practice.  */
+  if (first_new_block->end < last_insn->pc)
+    {
+      warning (_("Error while trying to read delta trace.  Falling back to "
+		 "a full read."));
+      return -1;
+    }
+
+  /* We adjust the last block to start at the end of our current trace.  */
+  gdb_assert (first_new_block->begin == 0);
+  first_new_block->begin = last_insn->pc;
+
+  /* We simply pop the last insn so we can insert it again as part of
+     the normal branch trace computation.
+     Since instruction iterators are based on indices in the instructions
+     vector, we don't leave any pointers dangling.  */
+  DEBUG ("pruning insn at %s for stitching",
+	 ftrace_print_insn_addr (last_insn));
+
+  VEC_pop (btrace_insn_s, last_bfun->insn);
+
+  /* The instructions vector may become empty temporarily if this has
+     been the only instruction in this function segment.
+     This violates the invariant but will be remedied shortly.  */
+  return 0;
+}
+
+/* Clear the branch trace histories in BTINFO.  */
+
+static void
+btrace_clear_history (struct btrace_thread_info *btinfo)
+{
+  xfree (btinfo->insn_history);
+  xfree (btinfo->call_history);
+  xfree (btinfo->replay);
+
+  btinfo->insn_history = NULL;
+  btinfo->call_history = NULL;
+  btinfo->replay = NULL;
+}
+
 /* See btrace.h.  */
 
 void
 btrace_fetch (struct thread_info *tp)
 {
   struct btrace_thread_info *btinfo;
+  struct btrace_target_info *tinfo;
   VEC (btrace_block_s) *btrace;
   struct cleanup *cleanup;
+  int errcode;
 
   DEBUG ("fetch thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
 
+  btrace = NULL;
   btinfo = &tp->btrace;
-  if (btinfo->target == NULL)
+  tinfo = btinfo->target;
+  if (tinfo == NULL)
+    return;
+
+  /* There's no way we could get new trace while replaying.
+     On the other hand, delta trace would return a partial record with the
+     current PC, which is the replay PC, not the last PC, as expected.  */
+  if (btinfo->replay != NULL)
     return;
 
-  btrace = target_read_btrace (btinfo->target, BTRACE_READ_NEW);
   cleanup = make_cleanup (VEC_cleanup (btrace_block_s), &btrace);
 
+  /* Let's first try to extend the trace we already have.  */
+  if (btinfo->end != NULL)
+    {
+      errcode = target_read_btrace (&btrace, tinfo, BTRACE_READ_DELTA);
+      if (errcode == 0)
+	{
+	  /* Success.  Let's try to stitch the traces together.  */
+	  errcode = btrace_stitch_trace (&btrace, btinfo);
+	}
+      else
+	{
+	  /* We failed to read delta trace.  Let's try to read new trace.  */
+	  errcode = target_read_btrace (&btrace, tinfo, BTRACE_READ_NEW);
+
+	  /* If we got any new trace, discard what we have.  */
+	  if (errcode == 0 && !VEC_empty (btrace_block_s, btrace))
+	    btrace_clear (tp);
+	}
+
+      /* If we were not able to read the trace, we start over.  */
+      if (errcode != 0)
+	{
+	  btrace_clear (tp);
+	  errcode = target_read_btrace (&btrace, tinfo, BTRACE_READ_ALL);
+	}
+    }
+  else
+    errcode = target_read_btrace (&btrace, tinfo, BTRACE_READ_ALL);
+
+  /* If we were not able to read the branch trace, signal an error.  */
+  if (errcode != 0)
+    error (_("Failed to read branch trace."));
+
+  /* Compute the trace, provided we have any.  */
   if (!VEC_empty (btrace_block_s, btrace))
     {
-      btrace_clear (tp);
+      btrace_clear_history (btinfo);
       btrace_compute_ftrace (btinfo, btrace);
     }
 
@@ -772,13 +902,7 @@ btrace_clear (struct thread_info *tp)
   btinfo->begin = NULL;
   btinfo->end = NULL;
 
-  xfree (btinfo->insn_history);
-  xfree (btinfo->call_history);
-  xfree (btinfo->replay);
-
-  btinfo->insn_history = NULL;
-  btinfo->call_history = NULL;
-  btinfo->replay = NULL;
+  btrace_clear_history (btinfo);
 }
 
 /* See btrace.h.  */
@@ -870,10 +994,7 @@ parse_xml_btrace (const char *buffer)
   errcode = gdb_xml_parse_quick (_("btrace"), "btrace.dtd", btrace_elements,
 				 buffer, &btrace);
   if (errcode != 0)
-    {
-      do_cleanups (cleanup);
-      return NULL;
-    }
+    error (_("Error parsing branch trace."));
 
   /* Keep parse results.  */
   discard_cleanups (cleanup);
diff --git a/gdb/common/btrace-common.h b/gdb/common/btrace-common.h
index 1986868..4bab92e 100644
--- a/gdb/common/btrace-common.h
+++ b/gdb/common/btrace-common.h
@@ -42,7 +42,9 @@
    asynchronous, e.g. interrupts.  */
 struct btrace_block
 {
-  /* The address of the first byte of the first instruction in the block.  */
+  /* The address of the first byte of the first instruction in the block.
+     The address may be zero if we do not know the beginning of this block,
+     such as for the first block in a delta trace.  */
   CORE_ADDR begin;
 
   /* The address of the first byte of the last instruction in the block.  */
@@ -67,7 +69,28 @@ enum btrace_read_type
   BTRACE_READ_ALL,
 
   /* Send all available trace, if it changed.  */
-  BTRACE_READ_NEW
+  BTRACE_READ_NEW,
+
+  /* Send the trace since the last request.  This will fail if the trace
+     buffer overflowed.  */
+  BTRACE_READ_DELTA
+};
+
+/* Enumeration of btrace errors.  */
+
+enum btrace_error
+{
+  /* No error.  Everything is OK.  */
+  BTRACE_ERR_NONE,
+
+  /* An unknown error.  */
+  BTRACE_ERR_UNKNOWN,
+
+  /* Branch tracing is not supported on this system.  */
+  BTRACE_ERR_NOT_SUPPORTED,
+
+  /* The branch trace buffer overflowed; no delta read possible.  */
+  BTRACE_ERR_OVERFLOW
 };
 
 #endif /* BTRACE_COMMON_H */
diff --git a/gdb/common/linux-btrace.c b/gdb/common/linux-btrace.c
index 801505b..4996a25 100644
--- a/gdb/common/linux-btrace.c
+++ b/gdb/common/linux-btrace.c
@@ -172,11 +172,11 @@ perf_event_sample_ok (const struct perf_event_sample *sample)
 
 static VEC (btrace_block_s) *
 perf_event_read_bts (struct btrace_target_info* tinfo, const uint8_t *begin,
-		     const uint8_t *end, const uint8_t *start)
+		     const uint8_t *end, const uint8_t *start, size_t size)
 {
   VEC (btrace_block_s) *btrace = NULL;
   struct perf_event_sample sample;
-  size_t read = 0, size = (end - begin);
+  size_t read = 0;
   struct btrace_block block = { 0, 0 };
   struct regcache *regcache;
 
@@ -252,6 +252,13 @@ perf_event_read_bts (struct btrace_target_info* tinfo, const uint8_t *begin,
       block.end = psample->bts.from;
     }
 
+  /* Push the last block (i.e. the first one of inferior execution), as well.
+     We don't know where it ends, but we know where it starts.  If we're
+     reading delta trace, we can fill in the start address later on.
+     Otherwise we will prune it.  */
+  block.begin = 0;
+  VEC_safe_push (btrace_block_s, btrace, &block);
+
   return btrace;
 }
 
@@ -476,7 +483,7 @@ linux_enable_btrace (ptid_t ptid)
 
 /* See linux-btrace.h.  */
 
-int
+enum btrace_error
 linux_disable_btrace (struct btrace_target_info *tinfo)
 {
   int errcode;
@@ -484,12 +491,12 @@ linux_disable_btrace (struct btrace_target_info *tinfo)
   errno = 0;
   errcode = munmap (tinfo->buffer, perf_event_mmap_size (tinfo));
   if (errcode != 0)
-    return errno;
+    return BTRACE_ERR_UNKNOWN;
 
   close (tinfo->file);
   xfree (tinfo);
 
-  return 0;
+  return BTRACE_ERR_NONE;
 }
 
 /* Check whether the branch trace has changed.  */
@@ -504,21 +511,24 @@ linux_btrace_has_changed (struct btrace_target_info *tinfo)
 
 /* See linux-btrace.h.  */
 
-VEC (btrace_block_s) *
-linux_read_btrace (struct btrace_target_info *tinfo,
+enum btrace_error
+linux_read_btrace (VEC (btrace_block_s) **btrace,
+		   struct btrace_target_info *tinfo,
 		   enum btrace_read_type type)
 {
-  VEC (btrace_block_s) *btrace = NULL;
   volatile struct perf_event_mmap_page *header;
   const uint8_t *begin, *end, *start;
-  unsigned long data_head, retries = 5;
-  size_t buffer_size;
+  unsigned long data_head, data_tail, retries = 5;
+  size_t buffer_size, size;
 
+  /* For delta reads, we return at least the partial last block containing
+     the current PC.  */
   if (type == BTRACE_READ_NEW && !linux_btrace_has_changed (tinfo))
-    return NULL;
+    return BTRACE_ERR_NONE;
 
   header = perf_event_header (tinfo);
   buffer_size = perf_event_buffer_size (tinfo);
+  data_tail = tinfo->data_head;
 
   /* We may need to retry reading the trace.  See below.  */
   while (retries--)
@@ -526,23 +536,45 @@ linux_read_btrace (struct btrace_target_info *tinfo,
       data_head = header->data_head;
 
       /* Delete any leftover trace from the previous iteration.  */
-      VEC_free (btrace_block_s, btrace);
+      VEC_free (btrace_block_s, *btrace);
 
-      /* If there's new trace, let's read it.  */
-      if (data_head != tinfo->data_head)
+      if (type == BTRACE_READ_DELTA)
 	{
-	  /* Data_head keeps growing; the buffer itself is circular.  */
-	  begin = perf_event_buffer_begin (tinfo);
-	  start = begin + data_head % buffer_size;
-
-	  if (data_head <= buffer_size)
-	    end = start;
-	  else
-	    end = perf_event_buffer_end (tinfo);
+	  /* Determine the number of bytes to read and check for buffer
+	     overflows.  */
+
+	  /* Check for data head overflows.  We might be able to recover from
+	     those but they are very unlikely and it's not really worth the
+	     effort, I think.  */
+	  if (data_head < data_tail)
+	    return BTRACE_ERR_OVERFLOW;
+
+	  /* If the buffer is smaller than the trace delta, we overflowed.  */
+	  size = data_head - data_tail;
+	  if (buffer_size < size)
+	    return BTRACE_ERR_OVERFLOW;
+	}
+      else
+	{
+	  /* Read the entire buffer.  */
+	  size = buffer_size;
 
-	  btrace = perf_event_read_bts (tinfo, begin, end, start);
+	  /* Adjust the size if the buffer has not overflowed, yet.  */
+	  if (data_head < size)
+	    size = data_head;
 	}
 
+      /* Data_head keeps growing; the buffer itself is circular.  */
+      begin = perf_event_buffer_begin (tinfo);
+      start = begin + data_head % buffer_size;
+
+      if (data_head <= buffer_size)
+	end = start;
+      else
+	end = perf_event_buffer_end (tinfo);
+
+      *btrace = perf_event_read_bts (tinfo, begin, end, start, size);
+
       /* The stopping thread notifies its ptracer before it is scheduled out.
 	 On multi-core systems, the debugger might therefore run while the
 	 kernel might be writing the last branch trace records.
@@ -554,7 +586,13 @@ linux_read_btrace (struct btrace_target_info *tinfo,
 
   tinfo->data_head = data_head;
 
-  return btrace;
+  /* Prune the incomplete last block (i.e. the first one of inferior execution)
+     if we're not doing a delta read.  There is no way of filling in its zeroed
+     BEGIN element.  */
+  if (!VEC_empty (btrace_block_s, *btrace) && type != BTRACE_READ_DELTA)
+    VEC_pop (btrace_block_s, *btrace);
+
+  return BTRACE_ERR_NONE;
 }
 
 #else /* !HAVE_LINUX_PERF_EVENT_H */
@@ -577,19 +615,20 @@ linux_enable_btrace (ptid_t ptid)
 
 /* See linux-btrace.h.  */
 
-int
+enum btrace_error
 linux_disable_btrace (struct btrace_target_info *tinfo)
 {
-  return ENOSYS;
+  return BTRACE_ERR_NOT_SUPPORTED;
 }
 
 /* See linux-btrace.h.  */
 
-VEC (btrace_block_s) *
-linux_read_btrace (struct btrace_target_info *tinfo,
+enum btrace_error
+linux_read_btrace (VEC (btrace_block_s) **btrace,
+		   struct btrace_target_info *tinfo,
 		   enum btrace_read_type type)
 {
-  return NULL;
+  return BTRACE_ERR_NOT_SUPPORTED;
 }
 
 #endif /* !HAVE_LINUX_PERF_EVENT_H */
diff --git a/gdb/common/linux-btrace.h b/gdb/common/linux-btrace.h
index d4e8402..0f0412c 100644
--- a/gdb/common/linux-btrace.h
+++ b/gdb/common/linux-btrace.h
@@ -61,17 +61,18 @@ struct btrace_target_info
   int ptr_bits;
 };
 
-/* Check whether branch tracing is supported.  */
+/* See to_supports_btrace in target.h.  */
 extern int linux_supports_btrace (void);
 
-/* Enable branch tracing for @ptid.  */
+/* See to_enable_btrace in target.h.  */
 extern struct btrace_target_info *linux_enable_btrace (ptid_t ptid);
 
-/* Disable branch tracing and deallocate @tinfo.  */
-extern int linux_disable_btrace (struct btrace_target_info *tinfo);
+/* See to_disable_btrace in target.h.  */
+extern enum btrace_error linux_disable_btrace (struct btrace_target_info *ti);
 
-/* Read branch trace data.  */
-extern VEC (btrace_block_s) *linux_read_btrace (struct btrace_target_info *,
-						enum btrace_read_type);
+/* See to_read_btrace in target.h.  */
+extern enum btrace_error linux_read_btrace (VEC (btrace_block_s) **btrace,
+					    struct btrace_target_info *btinfo,
+					    enum btrace_read_type type);
 
 #endif /* LINUX_BTRACE_H */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 684053a..2161715 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -39906,6 +39906,14 @@ Returns all available branch trace.
 @item new
 Returns all available branch trace if the branch trace changed since
 the last read request.
+
+@item delta
+Returns the new branch trace since the last read request.  Adds a new
+block to the end of the trace that begins at zero and ends at the source
+location of the first branch in the trace buffer.  This extra block is
+used to stitch traces together.
+
+If the trace buffer overflowed, returns an error indicating the overflow.
 @end table
 
 This packet is not probed by default; the remote stub must request it
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 770ee16..679b352 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -5705,7 +5705,7 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
 
 #ifdef HAVE_LINUX_BTRACE
 
-/* Enable branch tracing.  */
+/* See to_enable_btrace target method.  */
 
 static struct btrace_target_info *
 linux_low_enable_btrace (ptid_t ptid)
@@ -5725,17 +5725,39 @@ linux_low_enable_btrace (ptid_t ptid)
   return tinfo;
 }
 
-/* Read branch trace data as btrace xml document.  */
+/* See to_disable_btrace target method.  */
 
-static void
+static int
+linux_low_disable_btrace (struct btrace_target_info *tinfo)
+{
+  enum btrace_error err;
+
+  err = linux_disable_btrace (tinfo);
+  return (err == BTRACE_ERR_NONE ? 0 : -1);
+}
+
+/* See to_read_btrace target method.  */
+
+static int
 linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
 		       int type)
 {
   VEC (btrace_block_s) *btrace;
   struct btrace_block *block;
+  enum btrace_error err;
   int i;
 
-  btrace = linux_read_btrace (tinfo, type);
+  btrace = NULL;
+  err = linux_read_btrace (&btrace, tinfo, type);
+  if (err != BTRACE_ERR_NONE)
+    {
+      if (err == BTRACE_ERR_OVERFLOW)
+	buffer_grow_str0 (buffer, "E.Overflow.");
+      else
+	buffer_grow_str0 (buffer, "E.Generic Error.");
+
+      return -1;
+    }
 
   buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
   buffer_grow_str (buffer, "<btrace version=\"1.0\">\n");
@@ -5744,9 +5766,11 @@ linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
     buffer_xml_printf (buffer, "<block begin=\"0x%s\" end=\"0x%s\"/>\n",
 		       paddress (block->begin), paddress (block->end));
 
-  buffer_grow_str (buffer, "</btrace>\n");
+  buffer_grow_str0 (buffer, "</btrace>\n");
 
   VEC_free (btrace_block_s, btrace);
+
+  return 0;
 }
 #endif /* HAVE_LINUX_BTRACE */
 
@@ -5819,7 +5843,7 @@ static struct target_ops linux_target_ops = {
 #ifdef HAVE_LINUX_BTRACE
   linux_supports_btrace,
   linux_low_enable_btrace,
-  linux_disable_btrace,
+  linux_low_disable_btrace,
   linux_low_read_btrace,
 #else
   NULL,
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 1a110e0..5903edf 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1348,7 +1348,7 @@ handle_qxfer_btrace (const char *annex,
 {
   static struct buffer cache;
   struct thread_info *thread;
-  int type;
+  int type, result;
 
   if (the_target->read_btrace == NULL || writebuf != NULL)
     return -2;
@@ -1380,6 +1380,8 @@ handle_qxfer_btrace (const char *annex,
     type = BTRACE_READ_ALL;
   else if (strcmp (annex, "new") == 0)
     type = BTRACE_READ_NEW;
+  else if (strcmp (annex, "delta") == 0)
+    type = BTRACE_READ_DELTA;
   else
     {
       strcpy (own_buf, "E.Bad annex.");
@@ -1390,7 +1392,12 @@ handle_qxfer_btrace (const char *annex,
     {
       buffer_free (&cache);
 
-      target_read_btrace (thread->btrace, &cache, type);
+      result = target_read_btrace (thread->btrace, &cache, type);
+      if (result != 0)
+	{
+	  memcpy (own_buf, cache.buffer, cache.used_size);
+	  return -3;
+	}
     }
   else if (offset > cache.used_size)
     {
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index c5e6fee..6969b5c 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -356,12 +356,15 @@ struct target_ops
      information struct for reading and for disabling branch trace.  */
   struct btrace_target_info *(*enable_btrace) (ptid_t ptid);
 
-  /* Disable branch tracing.  */
+  /* Disable branch tracing.
+     Returns zero on success, non-zero otherwise.  */
   int (*disable_btrace) (struct btrace_target_info *tinfo);
 
   /* Read branch trace data into buffer.  We use an int to specify the type
-     to break a cyclic dependency.  */
-  void (*read_btrace) (struct btrace_target_info *, struct buffer *, int type);
+     to break a cyclic dependency.
+     Return 0 on success; print an error message into BUFFER and return -1,
+     otherwise.  */
+  int (*read_btrace) (struct btrace_target_info *, struct buffer *, int type);
 
   /* Return true if target supports range stepping.  */
   int (*supports_range_stepping) (void);
diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c
index c2f4fcc..f90bfaa 100644
--- a/gdb/i386-linux-nat.c
+++ b/gdb/i386-linux-nat.c
@@ -1084,10 +1084,10 @@ i386_linux_enable_btrace (ptid_t ptid)
 static void
 i386_linux_disable_btrace (struct btrace_target_info *tinfo)
 {
-  int errcode = linux_disable_btrace (tinfo);
+  enum btrace_error errcode = linux_disable_btrace (tinfo);
 
-  if (errcode != 0)
-    error (_("Could not disable branch tracing: %s."), safe_strerror (errcode));
+  if (errcode != BTRACE_ERR_NONE)
+    error (_("Could not disable branch tracing."));
 }
 
 /* Teardown branch tracing.  */
diff --git a/gdb/remote.c b/gdb/remote.c
index 5fe1c53..318966b 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -11428,13 +11428,14 @@ remote_teardown_btrace (struct btrace_target_info *tinfo)
 
 /* Read the branch trace.  */
 
-static VEC (btrace_block_s) *
-remote_read_btrace (struct btrace_target_info *tinfo,
+static enum btrace_error
+remote_read_btrace (VEC (btrace_block_s) **btrace,
+		    struct btrace_target_info *tinfo,
 		    enum btrace_read_type type)
 {
   struct packet_config *packet = &remote_protocol_packets[PACKET_qXfer_btrace];
   struct remote_state *rs = get_remote_state ();
-  VEC (btrace_block_s) *btrace = NULL;
+  struct cleanup *cleanup;
   const char *annex;
   char *xml;
 
@@ -11453,6 +11454,9 @@ remote_read_btrace (struct btrace_target_info *tinfo,
     case BTRACE_READ_NEW:
       annex = "new";
       break;
+    case BTRACE_READ_DELTA:
+      annex = "delta";
+      break;
     default:
       internal_error (__FILE__, __LINE__,
 		      _("Bad branch tracing read type: %u."),
@@ -11461,15 +11465,14 @@ remote_read_btrace (struct btrace_target_info *tinfo,
 
   xml = target_read_stralloc (&current_target,
                               TARGET_OBJECT_BTRACE, annex);
-  if (xml != NULL)
-    {
-      struct cleanup *cleanup = make_cleanup (xfree, xml);
+  if (xml == NULL)
+    return BTRACE_ERR_UNKNOWN;
 
-      btrace = parse_xml_btrace (xml);
-      do_cleanups (cleanup);
-    }
+  cleanup = make_cleanup (xfree, xml);
+  *btrace = parse_xml_btrace (xml);
+  do_cleanups (cleanup);
 
-  return btrace;
+  return BTRACE_ERR_NONE;
 }
 
 static int
diff --git a/gdb/target.c b/gdb/target.c
index 7b41795c..f6d8a0f 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4214,18 +4214,19 @@ target_teardown_btrace (struct btrace_target_info *btinfo)
 
 /* See target.h.  */
 
-VEC (btrace_block_s) *
-target_read_btrace (struct btrace_target_info *btinfo,
+enum btrace_error
+target_read_btrace (VEC (btrace_block_s) **btrace,
+		    struct btrace_target_info *btinfo,
 		    enum btrace_read_type type)
 {
   struct target_ops *t;
 
   for (t = current_target.beneath; t != NULL; t = t->beneath)
     if (t->to_read_btrace != NULL)
-      return t->to_read_btrace (btinfo, type);
+      return t->to_read_btrace (btrace, btinfo, type);
 
   tcomplain ();
-  return NULL;
+  return BTRACE_ERR_NOT_SUPPORTED;
 }
 
 /* See target.h.  */
diff --git a/gdb/target.h b/gdb/target.h
index 97ba1bc..72f5910 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -828,9 +828,13 @@ struct target_ops
        be attempting to talk to a remote target.  */
     void (*to_teardown_btrace) (struct btrace_target_info *tinfo);
 
-    /* Read branch trace data.  */
-    VEC (btrace_block_s) *(*to_read_btrace) (struct btrace_target_info *,
-					     enum btrace_read_type);
+    /* Read branch trace data for the thread indicated by BTINFO into DATA.
+       DATA is cleared before new trace is added.
+       The branch trace will start with the most recent block and continue
+       towards older blocks.  */
+    enum btrace_error (*to_read_btrace) (VEC (btrace_block_s) **data,
+					 struct btrace_target_info *btinfo,
+					 enum btrace_read_type type);
 
     /* Stop trace recording.  */
     void (*to_stop_recording) (void);
@@ -1962,8 +1966,9 @@ extern void target_disable_btrace (struct btrace_target_info *btinfo);
 extern void target_teardown_btrace (struct btrace_target_info *btinfo);
 
 /* See to_read_btrace in struct target_ops.  */
-extern VEC (btrace_block_s) *target_read_btrace (struct btrace_target_info *,
-						 enum btrace_read_type);
+extern enum btrace_error target_read_btrace (VEC (btrace_block_s) **,
+					     struct btrace_target_info *,
+					     enum btrace_read_type);
 
 /* See to_stop_recording in struct target_ops.  */
 extern void target_stop_recording (void);
-- 
1.8.3.1

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

* [patch v8 15/24] frame, backtrace: allow targets to supply a frame unwinder
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (14 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 13/24] target: add ops parameter to to_prepare_to_store method Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-13 18:27   ` Pedro Alves
  2013-12-12  9:16 ` [patch v8 07/24] record-btrace: fix insn range in function call history Markus Metzger
                   ` (8 subsequent siblings)
  24 siblings, 1 reply; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

Allow targets to supply their own target-specific frame unwinders; one for
normal frames and one for tailcall frames.  If a target-specific unwinder
is supplied, it will be chosen before any other unwinder.

gdb/
2013-02-11  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* dwarf2-frame.c (dwarf2_frame_cfa): Move UNWIND_UNAVAILABLE check
	earlier.
	* frame-unwind.c: Include target.h.
	(frame_unwind_try_unwinder): New function with code from ...
	(frame_unwind_find_by_frame): ... here.  New variable
	unwinder_from_target, call also target_get_unwinder,
	target_get_tailcall_unwinder, and frame_unwind_try_unwinder for it.
	* frame.c (get_frame_unwind_stop_reason): Unconditionally call
	get_prev_frame_1.
	* target.c (target_get_unwinder, target_get_tailcall_unwinder): New.
	* target.h (struct target_ops): New fields to_get_unwinder and
	to_get_tailcall_unwinder.
	(target_get_unwinder, target_get_tailcall_unwinder): New declarations.


---
 gdb/dwarf2-frame.c |  8 ++---
 gdb/frame-unwind.c | 86 ++++++++++++++++++++++++++++++++++++------------------
 gdb/frame.c        |  9 ++----
 gdb/target.c       | 28 ++++++++++++++++++
 gdb/target.h       | 11 +++++++
 5 files changed, 104 insertions(+), 38 deletions(-)

diff --git a/gdb/dwarf2-frame.c b/gdb/dwarf2-frame.c
index c4f8771..4efa7d8 100644
--- a/gdb/dwarf2-frame.c
+++ b/gdb/dwarf2-frame.c
@@ -1513,16 +1513,16 @@ dwarf2_frame_cfa (struct frame_info *this_frame)
 {
   while (get_frame_type (this_frame) == INLINE_FRAME)
     this_frame = get_prev_frame (this_frame);
+  if (get_frame_unwind_stop_reason (this_frame) == UNWIND_UNAVAILABLE)
+    throw_error (NOT_AVAILABLE_ERROR,
+                _("can't compute CFA for this frame: "
+                  "required registers or memory are unavailable"));
   /* This restriction could be lifted if other unwinders are known to
      compute the frame base in a way compatible with the DWARF
      unwinder.  */
   if (!frame_unwinder_is (this_frame, &dwarf2_frame_unwind)
       && !frame_unwinder_is (this_frame, &dwarf2_tailcall_frame_unwind))
     error (_("can't compute CFA for this frame"));
-  if (get_frame_unwind_stop_reason (this_frame) == UNWIND_UNAVAILABLE)
-    throw_error (NOT_AVAILABLE_ERROR,
-		 _("can't compute CFA for this frame: "
-		   "required registers or memory are unavailable"));
   return get_frame_base (this_frame);
 }
 \f
diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
index 8fabf52..e32f4b8 100644
--- a/gdb/frame-unwind.c
+++ b/gdb/frame-unwind.c
@@ -27,6 +27,7 @@
 #include "exceptions.h"
 #include "gdb_assert.h"
 #include "gdb_obstack.h"
+#include "target.h"
 
 static struct gdbarch_data *frame_unwind_data;
 
@@ -88,6 +89,48 @@ frame_unwind_append_unwinder (struct gdbarch *gdbarch,
   (*ip)->unwinder = unwinder;
 }
 
+/* Call SNIFFER from UNWINDER.  If it succeeded set UNWINDER for
+   THIS_FRAME and return 1.  Otherwise the function keeps THIS_FRAME
+   unchanged and returns 0.  */
+
+static int
+frame_unwind_try_unwinder (struct frame_info *this_frame, void **this_cache,
+                          const struct frame_unwind *unwinder)
+{
+  struct cleanup *old_cleanup;
+  volatile struct gdb_exception ex;
+  int res = 0;
+
+  old_cleanup = frame_prepare_for_sniffer (this_frame, unwinder);
+
+  TRY_CATCH (ex, RETURN_MASK_ERROR)
+    {
+      res = unwinder->sniffer (unwinder, this_frame, this_cache);
+    }
+  if (ex.reason < 0 && ex.error == NOT_AVAILABLE_ERROR)
+    {
+      /* This usually means that not even the PC is available,
+        thus most unwinders aren't able to determine if they're
+        the best fit.  Keep trying.  Fallback prologue unwinders
+        should always accept the frame.  */
+      do_cleanups (old_cleanup);
+      return 0;
+    }
+  else if (ex.reason < 0)
+    throw_exception (ex);
+  else if (res)
+    {
+      discard_cleanups (old_cleanup);
+      return 1;
+    }
+  else
+    {
+      do_cleanups (old_cleanup);
+      return 0;
+    }
+  gdb_assert_not_reached ("frame_unwind_try_unwinder");
+}
+
 /* Iterate through sniffers for THIS_FRAME frame until one returns with an
    unwinder implementation.  THIS_FRAME->UNWIND must be NULL, it will get set
    by this function.  Possibly initialize THIS_CACHE.  */
@@ -98,37 +141,24 @@ frame_unwind_find_by_frame (struct frame_info *this_frame, void **this_cache)
   struct gdbarch *gdbarch = get_frame_arch (this_frame);
   struct frame_unwind_table *table = gdbarch_data (gdbarch, frame_unwind_data);
   struct frame_unwind_table_entry *entry;
+  const struct frame_unwind *unwinder_from_target;
+
+  unwinder_from_target = target_get_unwinder ();
+  if (unwinder_from_target != NULL
+      && frame_unwind_try_unwinder (this_frame, this_cache,
+                                   unwinder_from_target))
+    return;
+
+  unwinder_from_target = target_get_tailcall_unwinder ();
+  if (unwinder_from_target != NULL
+      && frame_unwind_try_unwinder (this_frame, this_cache,
+                                   unwinder_from_target))
+    return;
 
   for (entry = table->list; entry != NULL; entry = entry->next)
-    {
-      struct cleanup *old_cleanup;
-      volatile struct gdb_exception ex;
-      int res = 0;
-
-      old_cleanup = frame_prepare_for_sniffer (this_frame, entry->unwinder);
-
-      TRY_CATCH (ex, RETURN_MASK_ERROR)
-	{
-	  res = entry->unwinder->sniffer (entry->unwinder, this_frame,
-					  this_cache);
-	}
-      if (ex.reason < 0 && ex.error == NOT_AVAILABLE_ERROR)
-	{
-	  /* This usually means that not even the PC is available,
-	     thus most unwinders aren't able to determine if they're
-	     the best fit.  Keep trying.  Fallback prologue unwinders
-	     should always accept the frame.  */
-	}
-      else if (ex.reason < 0)
-	throw_exception (ex);
-      else if (res)
-        {
-          discard_cleanups (old_cleanup);
-          return;
-        }
+    if (frame_unwind_try_unwinder (this_frame, this_cache, entry->unwinder))
+      return;
 
-      do_cleanups (old_cleanup);
-    }
   internal_error (__FILE__, __LINE__, _("frame_unwind_find_by_frame failed"));
 }
 
diff --git a/gdb/frame.c b/gdb/frame.c
index 37d780e..3f2aabb 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -2507,13 +2507,10 @@ get_frame_sp (struct frame_info *this_frame)
 enum unwind_stop_reason
 get_frame_unwind_stop_reason (struct frame_info *frame)
 {
-  /* If we haven't tried to unwind past this point yet, then assume
-     that unwinding would succeed.  */
-  if (frame->prev_p == 0)
-    return UNWIND_NO_REASON;
+  /* Fill-in STOP_REASON.  */
+  get_prev_frame_1 (frame);
+  gdb_assert (frame->prev_p);
 
-  /* Otherwise, we set a reason when we succeeded (or failed) to
-     unwind.  */
   return frame->stop_reason;
 }
 
diff --git a/gdb/target.c b/gdb/target.c
index 342850f..7b41795c 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4477,6 +4477,34 @@ target_call_history_range (ULONGEST begin, ULONGEST end, int flags)
   tcomplain ();
 }
 
+/* See target.h.  */
+
+const struct frame_unwind *
+target_get_unwinder (void)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    if (t->to_get_unwinder != NULL)
+      return t->to_get_unwinder;
+
+  return NULL;
+}
+
+/* See target.h.  */
+
+const struct frame_unwind *
+target_get_tailcall_unwinder (void)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    if (t->to_get_tailcall_unwinder != NULL)
+      return t->to_get_tailcall_unwinder;
+
+  return NULL;
+}
+
 static int
 deprecated_debug_xfer_memory (CORE_ADDR memaddr, bfd_byte *myaddr, int len,
 			      int write, struct mem_attrib *attrib,
diff --git a/gdb/target.h b/gdb/target.h
index db4b5a3..97ba1bc 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -891,6 +891,11 @@ struct target_ops
        non-empty annex.  */
     int (*to_augmented_libraries_svr4_read) (void);
 
+    /* Those unwinders are tried before any other arch unwinders.  Use NULL if
+       it is not used.  */
+    const struct frame_unwind *to_get_unwinder;
+    const struct frame_unwind *to_get_tailcall_unwinder;
+
     int to_magic;
     /* Need sub-structure for target machine related rather than comm related?
      */
@@ -1774,6 +1779,12 @@ extern char *target_fileio_read_stralloc (const char *filename);
 
 extern int target_core_of_thread (ptid_t ptid);
 
+/* See to_get_unwinder in struct target_ops.  */
+extern const struct frame_unwind *target_get_unwinder (void);
+
+/* See to_get_tailcall_unwinder in struct target_ops.  */
+extern const struct frame_unwind *target_get_tailcall_unwinder (void);
+
 /* Verify that the memory in the [MEMADDR, MEMADDR+SIZE) range matches
    the contents of [DATA,DATA+SIZE).  Returns 1 if there's a match, 0
    if there's a mismatch, and -1 if an error is encountered while
-- 
1.8.3.1

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

* [patch v8 13/24] target: add ops parameter to to_prepare_to_store method
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (13 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 24/24] record-btrace: add (reverse-)stepping support Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-12  9:16 ` [patch v8 15/24] frame, backtrace: allow targets to supply a frame unwinder Markus Metzger
                   ` (9 subsequent siblings)
  24 siblings, 0 replies; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

To allow forwarding the prepare_to_store request to the target beneath,
add a target_ops * parameter.

2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

	* target.h (target_ops) <to_prepare_to_store>: Add parameter.
	(target_prepare_to_store): Remove macro.  New function.
	* target.c (update_current_target): Do not inherit/default
	prepare_to_store.
	(target_prepare_to_store): New.
	(debug_to_prepare_to_store): Remove.
	* remote.c (remote_prepare_to_store): Add parameter.
	* remote-mips.c (mips_prepare_to_store): Add parameter.
	* remote-m32r-sdi.c (m32r_prepare_to_store): Add parameter.
	* ravenscar-thread.c (ravenscar_prepare_to_store): Add
	parameter.
	* monitor.c (monitor_prepare_to_store): Add parameter.
	* inf-child.c (inf_child_prepare_to_store): Add parameter.


---
 gdb/inf-child.c        |  2 +-
 gdb/monitor.c          |  2 +-
 gdb/ravenscar-thread.c |  7 ++++---
 gdb/record-full.c      |  3 ++-
 gdb/remote-m32r-sdi.c  |  2 +-
 gdb/remote-mips.c      |  5 +++--
 gdb/remote.c           |  5 +++--
 gdb/target.c           | 36 +++++++++++++++++++++---------------
 gdb/target.h           |  5 ++---
 9 files changed, 38 insertions(+), 29 deletions(-)

diff --git a/gdb/inf-child.c b/gdb/inf-child.c
index 3db09c9..123ff14 100644
--- a/gdb/inf-child.c
+++ b/gdb/inf-child.c
@@ -100,7 +100,7 @@ inf_child_post_attach (int pid)
    program being debugged.  */
 
 static void
-inf_child_prepare_to_store (struct regcache *regcache)
+inf_child_prepare_to_store (struct target_ops *ops, struct regcache *regcache)
 {
 }
 
diff --git a/gdb/monitor.c b/gdb/monitor.c
index 2d94d42..5d2c3f3 100644
--- a/gdb/monitor.c
+++ b/gdb/monitor.c
@@ -1427,7 +1427,7 @@ monitor_store_registers (struct target_ops *ops,
    debugged.  */
 
 static void
-monitor_prepare_to_store (struct regcache *regcache)
+monitor_prepare_to_store (struct target_ops *ops, struct regcache *regcache)
 {
   /* Do nothing, since we can store individual regs.  */
 }
diff --git a/gdb/ravenscar-thread.c b/gdb/ravenscar-thread.c
index 9ef265d..53e0446 100644
--- a/gdb/ravenscar-thread.c
+++ b/gdb/ravenscar-thread.c
@@ -62,7 +62,8 @@ static void ravenscar_fetch_registers (struct target_ops *ops,
                                        struct regcache *regcache, int regnum);
 static void ravenscar_store_registers (struct target_ops *ops,
                                        struct regcache *regcache, int regnum);
-static void ravenscar_prepare_to_store (struct regcache *regcache);
+static void ravenscar_prepare_to_store (struct target_ops *ops,
+					struct regcache *regcache);
 static void ravenscar_resume (struct target_ops *ops, ptid_t ptid, int step,
 			      enum gdb_signal siggnal);
 static void ravenscar_mourn_inferior (struct target_ops *ops);
@@ -303,14 +304,14 @@ ravenscar_store_registers (struct target_ops *ops,
 }
 
 static void
-ravenscar_prepare_to_store (struct regcache *regcache)
+ravenscar_prepare_to_store (struct target_ops *ops, struct regcache *regcache)
 {
   struct target_ops *beneath = find_target_beneath (&ravenscar_ops);
 
   if (!ravenscar_runtime_initialized ()
       || ptid_equal (inferior_ptid, base_magic_null_ptid)
       || ptid_equal (inferior_ptid, ravenscar_running_thread ()))
-    beneath->to_prepare_to_store (regcache);
+    beneath->to_prepare_to_store (beneath, regcache);
   else
     {
       struct gdbarch *gdbarch = get_regcache_arch (regcache);
diff --git a/gdb/record-full.c b/gdb/record-full.c
index f9af408..19ab774 100644
--- a/gdb/record-full.c
+++ b/gdb/record-full.c
@@ -2148,7 +2148,8 @@ record_full_core_fetch_registers (struct target_ops *ops,
 /* "to_prepare_to_store" method for prec over corefile.  */
 
 static void
-record_full_core_prepare_to_store (struct regcache *regcache)
+record_full_core_prepare_to_store (struct target_ops *ops,
+				   struct regcache *regcache)
 {
 }
 
diff --git a/gdb/remote-m32r-sdi.c b/gdb/remote-m32r-sdi.c
index e81b2de..f560388 100644
--- a/gdb/remote-m32r-sdi.c
+++ b/gdb/remote-m32r-sdi.c
@@ -1013,7 +1013,7 @@ m32r_store_register (struct target_ops *ops,
    debugged.  */
 
 static void
-m32r_prepare_to_store (struct regcache *regcache)
+m32r_prepare_to_store (struct target_ops *target, struct regcache *regcache)
 {
   /* Do nothing, since we can store individual regs.  */
   if (remote_debug)
diff --git a/gdb/remote-mips.c b/gdb/remote-mips.c
index e4e5083..5b3254d 100644
--- a/gdb/remote-mips.c
+++ b/gdb/remote-mips.c
@@ -90,7 +90,8 @@ static int mips_map_regno (struct gdbarch *, int);
 
 static void mips_set_register (int regno, ULONGEST value);
 
-static void mips_prepare_to_store (struct regcache *regcache);
+static void mips_prepare_to_store (struct target_ops *ops,
+				   struct regcache *regcache);
 
 static int mips_fetch_word (CORE_ADDR addr, unsigned int *valp);
 
@@ -2064,7 +2065,7 @@ mips_fetch_registers (struct target_ops *ops,
    registers, so this function doesn't have to do anything.  */
 
 static void
-mips_prepare_to_store (struct regcache *regcache)
+mips_prepare_to_store (struct target_ops *ops, struct regcache *regcache)
 {
 }
 
diff --git a/gdb/remote.c b/gdb/remote.c
index 8472e2a..5fe1c53 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -98,7 +98,8 @@ static void async_handle_remote_sigint_twice (int);
 
 static void remote_files_info (struct target_ops *ignore);
 
-static void remote_prepare_to_store (struct regcache *regcache);
+static void remote_prepare_to_store (struct target_ops *ops,
+				     struct regcache *regcache);
 
 static void remote_open (char *name, int from_tty);
 
@@ -6413,7 +6414,7 @@ remote_fetch_registers (struct target_ops *ops,
    first.  */
 
 static void
-remote_prepare_to_store (struct regcache *regcache)
+remote_prepare_to_store (struct target_ops *ops, struct regcache *regcache)
 {
   struct remote_arch_state *rsa = get_remote_arch_state ();
   int i;
diff --git a/gdb/target.c b/gdb/target.c
index 85b5037..342850f 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -91,8 +91,6 @@ static struct target_ops debug_target;
 
 static void debug_to_open (char *, int);
 
-static void debug_to_prepare_to_store (struct regcache *);
-
 static void debug_to_files_info (struct target_ops *);
 
 static int debug_to_insert_breakpoint (struct gdbarch *,
@@ -587,7 +585,7 @@ update_current_target (void)
       /* Do not inherit to_wait.  */
       /* Do not inherit to_fetch_registers.  */
       /* Do not inherit to_store_registers.  */
-      INHERIT (to_prepare_to_store, t);
+      /* Do not inherit to_prepare_to_store.  */
       INHERIT (deprecated_xfer_memory, t);
       INHERIT (to_files_info, t);
       INHERIT (to_insert_breakpoint, t);
@@ -721,9 +719,6 @@ update_current_target (void)
   de_fault (to_post_attach,
 	    (void (*) (int))
 	    target_ignore);
-  de_fault (to_prepare_to_store,
-	    (void (*) (struct regcache *))
-	    noprocess);
   de_fault (deprecated_xfer_memory,
 	    (int (*) (CORE_ADDR, gdb_byte *, int, int,
 		      struct mem_attrib *, struct target_ops *))
@@ -4004,6 +3999,26 @@ target_store_registers (struct regcache *regcache, int regno)
   noprocess ();
 }
 
+/* See target.h.  */
+
+void
+target_prepare_to_store (struct regcache *regcache)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    {
+      if (t->to_prepare_to_store != NULL)
+	{
+	  t->to_prepare_to_store (t, regcache);
+	  if (targetdebug)
+	    fprintf_unfiltered (gdb_stdlog, "target_prepare_to_store ()\n");
+
+	  return;
+	}
+    }
+}
+
 int
 target_core_of_thread (ptid_t ptid)
 {
@@ -4462,14 +4477,6 @@ target_call_history_range (ULONGEST begin, ULONGEST end, int flags)
   tcomplain ();
 }
 
-static void
-debug_to_prepare_to_store (struct regcache *regcache)
-{
-  debug_target.to_prepare_to_store (regcache);
-
-  fprintf_unfiltered (gdb_stdlog, "target_prepare_to_store ()\n");
-}
-
 static int
 deprecated_debug_xfer_memory (CORE_ADDR memaddr, bfd_byte *myaddr, int len,
 			      int write, struct mem_attrib *attrib,
@@ -4921,7 +4928,6 @@ setup_target_debug (void)
 
   current_target.to_open = debug_to_open;
   current_target.to_post_attach = debug_to_post_attach;
-  current_target.to_prepare_to_store = debug_to_prepare_to_store;
   current_target.deprecated_xfer_memory = deprecated_debug_xfer_memory;
   current_target.to_files_info = debug_to_files_info;
   current_target.to_insert_breakpoint = debug_to_insert_breakpoint;
diff --git a/gdb/target.h b/gdb/target.h
index 1e1467e..db4b5a3 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -370,7 +370,7 @@ struct target_ops
 		       ptid_t, struct target_waitstatus *, int);
     void (*to_fetch_registers) (struct target_ops *, struct regcache *, int);
     void (*to_store_registers) (struct target_ops *, struct regcache *, int);
-    void (*to_prepare_to_store) (struct regcache *);
+    void (*to_prepare_to_store) (struct target_ops *, struct regcache *);
 
     /* Transfer LEN bytes of memory between GDB address MYADDR and
        target address MEMADDR.  If WRITE, transfer them to the target, else
@@ -1001,8 +1001,7 @@ extern void target_store_registers (struct regcache *regcache, int regs);
    that REGISTERS contains all the registers from the program being
    debugged.  */
 
-#define	target_prepare_to_store(regcache)	\
-     (*current_target.to_prepare_to_store) (regcache)
+extern void target_prepare_to_store (struct regcache *);
 
 /* Determine current address space of thread PTID.  */
 
-- 
1.8.3.1

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

* [patch v8 06/24] btrace: change branch trace data structure
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (3 preceding siblings ...)
  2013-12-12  9:15 ` [patch v8 16/24] record-btrace, frame: supply target-specific unwinder Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-12  9:16 ` [patch v8 18/24] record-btrace: add to_wait and to_resume target methods Markus Metzger
                   ` (19 subsequent siblings)
  24 siblings, 0 replies; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches, Christian Himpel

The branch trace is represented as 3 vectors:
  - a block vector
  - a instruction vector
  - a function vector

Each vector (except for the first) is computed from the one above.

Change this into a graph where a node represents a sequence of instructions
belonging to the same function and where we have three types of edges to connect
the function segments:
  - control flow
  - same function (instance)
  - call stack

This allows us to navigate in the branch trace.  We will need this for "record
goto" and reverse execution.

This patch introduces the data structure and computes the control flow edges.
It also introduces iterator structs to simplify iterating over the branch trace
in control-flow order.

It also fixes PR gdb/15240 since now recursive calls are handled correctly.
Fix the test that got the number of expected fib instances and also the
function numbers wrong.

The current instruction had been part of the branch trace.  This will look odd
once we start support for reverse execution.  Remove it.  We still keep it in
the trace itself to allow extending the branch trace more easily in the future.

CC: Christian Himpel <christian.himpel@intel.com>
2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

	* btrace.h (struct btrace_func_link): New.
	(enum btrace_function_flag): New.
	(struct btrace_inst): Rename to ...
	(struct btrace_insn): ...this. Update all users.
	(struct btrace_func) <ibegin, iend>: Remove.
	(struct btrace_func_link): New.
	(struct btrace_func): Rename to ...
	(struct btrace_function): ...this. Update all users.
	(struct btrace_function) <segment, flow, up, insn, insn_offset,
	number, level, flags>: New.
	(struct btrace_insn_iterator): Rename to ...
	(struct btrace_insn_history): ...this.
	Update all users.
	(struct btrace_insn_iterator, btrace_call_iterator): New.
	(struct btrace_target_info) <btrace, itrace, ftrace>: Remove.
	(struct btrace_target_info) <begin, end, level,
	insn_history, call_history>: New.
	(btrace_insn_get, btrace_insn_number, btrace_insn_begin,
	btrace_insn_end, btrace_insn_prev, btrace_insn_next,
	btrace_insn_cmp, btrace_find_insn_by_number, btrace_call_get,
	btrace_call_number, btrace_call_begin, btrace_call_end,
	btrace_call_prev, btrace_call_next, btrace_call_cmp,
	btrace_find_function_by_number, btrace_set_insn_history,
	btrace_set_call_history): New.
	* btrace.c (btrace_init_insn_iterator,
	btrace_init_func_iterator, compute_itrace): Remove.
	(ftrace_print_function_name, ftrace_print_filename,
	ftrace_skip_file): Change
	parameter to const.
	(ftrace_init_func): Remove.
	(ftrace_debug): Use new btrace_function fields.
	(ftrace_function_switched): Also consider gaining and
	losing symbol information).
	(ftrace_print_insn_addr, ftrace_new_call, ftrace_new_return,
	ftrace_new_switch, ftrace_find_caller, ftrace_new_function,
	ftrace_update_caller, ftrace_fixup_caller, ftrace_new_tailcall):
	New.
	(ftrace_new_function): Move. Remove debug print.
	(ftrace_update_lines, ftrace_update_insns): New.
	(ftrace_update_function): Check for call, ret, and jump.
	(compute_ftrace): Renamed to ...
	(btrace_compute_ftrace): ...this. Rewritten to compute call
	stack.
	(btrace_fetch, btrace_clear): Updated.
	(btrace_insn_get, btrace_insn_number, btrace_insn_begin,
	btrace_insn_end, btrace_insn_prev, btrace_insn_next,
	btrace_insn_cmp, btrace_find_insn_by_number, btrace_call_get,
	btrace_call_number, btrace_call_begin, btrace_call_end,
	btrace_call_prev, btrace_call_next, btrace_call_cmp,
	btrace_find_function_by_number, btrace_set_insn_history,
	btrace_set_call_history): New.
	* record-btrace.c (require_btrace): Use new btrace thread
	info fields.
	(record_btrace_info, btrace_insn_history,
	record_btrace_insn_history, record_btrace_insn_history_range):
	Use new btrace thread info fields and new iterator.
	(btrace_func_history_src_line): Rename to ...
	(btrace_call_history_src_line): ...this. Use new btrace
	thread info fields.
	(btrace_func_history): Rename to ...
	(btrace_call_history): ...this. Use new btrace thread info
	fields and new iterator.
	(record_btrace_call_history, record_btrace_call_history_range):
	Use new btrace thread info fields and new iterator.

testsuite/
	* gdb.btrace/function_call_history.exp: Fix expected function
	trace.
	* gdb.btrace/instruction_history.exp: Initialize traced.
	Remove traced_functions.


---
 gdb/btrace.c                                       | 1151 ++++++++++++++++----
 gdb/btrace.h                                       |  237 +++-
 gdb/record-btrace.c                                |  342 +++---
 gdb/testsuite/gdb.btrace/function_call_history.exp |   28 +-
 gdb/testsuite/gdb.btrace/instruction_history.exp   |   14 +-
 5 files changed, 1374 insertions(+), 398 deletions(-)

diff --git a/gdb/btrace.c b/gdb/btrace.c
index 46e73db..975b60c 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -45,92 +45,11 @@
 
 #define DEBUG_FTRACE(msg, args...) DEBUG ("[ftrace] " msg, ##args)
 
-/* Initialize the instruction iterator.  */
-
-static void
-btrace_init_insn_iterator (struct btrace_thread_info *btinfo)
-{
-  DEBUG ("init insn iterator");
-
-  btinfo->insn_iterator.begin = 1;
-  btinfo->insn_iterator.end = 0;
-}
-
-/* Initialize the function iterator.  */
-
-static void
-btrace_init_func_iterator (struct btrace_thread_info *btinfo)
-{
-  DEBUG ("init func iterator");
-
-  btinfo->func_iterator.begin = 1;
-  btinfo->func_iterator.end = 0;
-}
-
-/* Compute the instruction trace from the block trace.  */
-
-static VEC (btrace_inst_s) *
-compute_itrace (VEC (btrace_block_s) *btrace)
-{
-  VEC (btrace_inst_s) *itrace;
-  struct gdbarch *gdbarch;
-  unsigned int b;
-
-  DEBUG ("compute itrace");
-
-  itrace = NULL;
-  gdbarch = target_gdbarch ();
-  b = VEC_length (btrace_block_s, btrace);
-
-  while (b-- != 0)
-    {
-      btrace_block_s *block;
-      CORE_ADDR pc;
-
-      block = VEC_index (btrace_block_s, btrace, b);
-      pc = block->begin;
-
-      /* Add instructions for this block.  */
-      for (;;)
-	{
-	  btrace_inst_s *inst;
-	  int size;
-
-	  /* We should hit the end of the block.  Warn if we went too far.  */
-	  if (block->end < pc)
-	    {
-	      warning (_("Recorded trace may be corrupted."));
-	      break;
-	    }
-
-	  inst = VEC_safe_push (btrace_inst_s, itrace, NULL);
-	  inst->pc = pc;
-
-	  /* We're done once we pushed the instruction at the end.  */
-	  if (block->end == pc)
-	    break;
-
-	  size = gdb_insn_length (gdbarch, pc);
-
-	  /* Make sure we terminate if we fail to compute the size.  */
-	  if (size <= 0)
-	    {
-	      warning (_("Recorded trace may be incomplete."));
-	      break;
-	    }
-
-	  pc += size;
-	}
-    }
-
-  return itrace;
-}
-
 /* Return the function name of a recorded function segment for printing.
    This function never returns NULL.  */
 
 static const char *
-ftrace_print_function_name (struct btrace_func *bfun)
+ftrace_print_function_name (const struct btrace_function *bfun)
 {
   struct minimal_symbol *msym;
   struct symbol *sym;
@@ -151,7 +70,7 @@ ftrace_print_function_name (struct btrace_func *bfun)
    This function never returns NULL.  */
 
 static const char *
-ftrace_print_filename (struct btrace_func *bfun)
+ftrace_print_filename (const struct btrace_function *bfun)
 {
   struct symbol *sym;
   const char *filename;
@@ -166,44 +85,53 @@ ftrace_print_filename (struct btrace_func *bfun)
   return filename;
 }
 
-/* Print an ftrace debug status message.  */
+/* Return a string representation of the address of an instruction.
+   This function never returns NULL.  */
 
-static void
-ftrace_debug (struct btrace_func *bfun, const char *prefix)
+static const char *
+ftrace_print_insn_addr (const struct btrace_insn *insn)
 {
-  DEBUG_FTRACE ("%s: fun = %s, file = %s, lines = [%d; %d], insn = [%u; %u]",
-		prefix, ftrace_print_function_name (bfun),
-		ftrace_print_filename (bfun), bfun->lbegin, bfun->lend,
-		bfun->ibegin, bfun->iend);
+  if (insn == NULL)
+    return "<nil>";
+
+  return core_addr_to_string_nz (insn->pc);
 }
 
-/* Initialize a recorded function segment.  */
+/* Print an ftrace debug status message.  */
 
 static void
-ftrace_init_func (struct btrace_func *bfun, struct minimal_symbol *mfun,
-		  struct symbol *fun, unsigned int idx)
+ftrace_debug (const struct btrace_function *bfun, const char *prefix)
 {
-  bfun->msym = mfun;
-  bfun->sym = fun;
-  bfun->lbegin = INT_MAX;
-  bfun->lend = 0;
-  bfun->ibegin = idx;
-  bfun->iend = idx;
+  const char *fun, *file;
+  unsigned int ibegin, iend;
+  int lbegin, lend, level;
+
+  fun = ftrace_print_function_name (bfun);
+  file = ftrace_print_filename (bfun);
+  level = bfun->level;
+
+  lbegin = bfun->lbegin;
+  lend = bfun->lend;
+
+  ibegin = bfun->insn_offset;
+  iend = ibegin + VEC_length (btrace_insn_s, bfun->insn);
+
+  DEBUG_FTRACE ("%s: fun = %s, file = %s, level = %d, lines = [%d; %d], "
+		"insn = [%u; %u)", prefix, fun, file, level, lbegin, lend,
+		ibegin, iend);
 }
 
-/* Check whether the function has changed.  */
+/* Return non-zero if BFUN does not match MFUN and FUN,
+   return zero otherwise.  */
 
 static int
-ftrace_function_switched (struct btrace_func *bfun,
-			  struct minimal_symbol *mfun, struct symbol *fun)
+ftrace_function_switched (const struct btrace_function *bfun,
+			  const struct minimal_symbol *mfun,
+			  const struct symbol *fun)
 {
   struct minimal_symbol *msym;
   struct symbol *sym;
 
-  /* The function changed if we did not have one before.  */
-  if (bfun == NULL)
-    return 1;
-
   msym = bfun->msym;
   sym = bfun->sym;
 
@@ -228,109 +156,505 @@ ftrace_function_switched (struct btrace_func *bfun,
 	return 1;
     }
 
+  /* If we lost symbol information, we switched functions.  */
+  if (!(msym == NULL && sym == NULL) && mfun == NULL && fun == NULL)
+    return 1;
+
+  /* If we gained symbol information, we switched functions.  */
+  if (msym == NULL && sym == NULL && !(mfun == NULL && fun == NULL))
+    return 1;
+
   return 0;
 }
 
-/* Check if we should skip this file when generating the function call
-   history.  We would want to do that if, say, a macro that is defined
-   in another file is expanded in this function.  */
+/* Return non-zero if we should skip this file when generating the function
+   call history, zero otherwise.
+   We would want to do that if, say, a macro that is defined in another file
+   is expanded in this function.  */
 
 static int
-ftrace_skip_file (struct btrace_func *bfun, const char *filename)
+ftrace_skip_file (const struct btrace_function *bfun, const char *fullname)
 {
   struct symbol *sym;
   const char *bfile;
 
   sym = bfun->sym;
+  if (sym == NULL)
+    return 1;
 
-  if (sym != NULL)
-    bfile = symtab_to_fullname (sym->symtab);
-  else
-    bfile = "";
+  bfile = symtab_to_fullname (sym->symtab);
+
+  return (filename_cmp (bfile, fullname) != 0);
+}
+
+/* Allocate and initialize a new branch trace function segment.
+   PREV is the chronologically preceding function segment.
+   MFUN and FUN are the symbol information we have for this function.  */
+
+static struct btrace_function *
+ftrace_new_function (struct btrace_function *prev,
+		     struct minimal_symbol *mfun,
+		     struct symbol *fun)
+{
+  struct btrace_function *bfun;
+
+  bfun = xzalloc (sizeof (*bfun));
+
+  bfun->msym = mfun;
+  bfun->sym = fun;
+  bfun->flow.prev = prev;
+
+  /* We start with the identities of min and max, respectively.  */
+  bfun->lbegin = INT_MAX;
+  bfun->lend = INT_MIN;
 
-  if (filename == NULL)
-    filename = "";
+  if (prev != NULL)
+    {
+      gdb_assert (prev->flow.next == NULL);
+      prev->flow.next = bfun;
 
-  return (filename_cmp (bfile, filename) != 0);
+      bfun->number = prev->number + 1;
+      bfun->insn_offset = (prev->insn_offset
+			   + VEC_length (btrace_insn_s, prev->insn));
+    }
+
+  return bfun;
 }
 
-/* Compute the function trace from the instruction trace.  */
+/* Update the UP field of a function segment.  */
 
-static VEC (btrace_func_s) *
-compute_ftrace (VEC (btrace_inst_s) *itrace)
+static void
+ftrace_update_caller (struct btrace_function *bfun,
+		      struct btrace_function *caller,
+		      enum btrace_function_flag flags)
 {
-  VEC (btrace_func_s) *ftrace;
-  struct btrace_inst *binst;
-  struct btrace_func *bfun;
-  unsigned int idx;
+  if (bfun->up != NULL)
+    ftrace_debug (bfun, "updating caller");
 
-  DEBUG ("compute ftrace");
+  bfun->up = caller;
+  bfun->flags = flags;
+
+  ftrace_debug (bfun, "set caller");
+}
+
+/* Fix up the caller for all segments of a function.  */
+
+static void
+ftrace_fixup_caller (struct btrace_function *bfun,
+		     struct btrace_function *caller,
+		     enum btrace_function_flag flags)
+{
+  struct btrace_function *prev, *next;
+
+  ftrace_update_caller (bfun, caller, flags);
+
+  /* Update all function segments belonging to the same function.  */
+  for (prev = bfun->segment.prev; prev != NULL; prev = prev->segment.prev)
+    ftrace_update_caller (prev, caller, flags);
+
+  for (next = bfun->segment.next; next != NULL; next = next->segment.next)
+    ftrace_update_caller (next, caller, flags);
+}
+
+/* Add a new function segment for a call.
+   CALLER is the chronologically preceding function segment.
+   MFUN and FUN are the symbol information we have for this function.  */
+
+static struct btrace_function *
+ftrace_new_call (struct btrace_function *caller,
+		 struct minimal_symbol *mfun,
+		 struct symbol *fun)
+{
+  struct btrace_function *bfun;
+
+  bfun = ftrace_new_function (caller, mfun, fun);
+  bfun->up = caller;
+  bfun->level = caller->level + 1;
+
+  ftrace_debug (bfun, "new call");
+
+  return bfun;
+}
+
+/* Add a new function segment for a tail call.
+   CALLER is the chronologically preceding function segment.
+   MFUN and FUN are the symbol information we have for this function.  */
+
+static struct btrace_function *
+ftrace_new_tailcall (struct btrace_function *caller,
+		     struct minimal_symbol *mfun,
+		     struct symbol *fun)
+{
+  struct btrace_function *bfun;
 
-  ftrace = NULL;
-  bfun = NULL;
+  bfun = ftrace_new_function (caller, mfun, fun);
+  bfun->up = caller;
+  bfun->level = caller->level + 1;
+  bfun->flags |= BFUN_UP_LINKS_TO_TAILCALL;
 
-  for (idx = 0; VEC_iterate (btrace_inst_s, itrace, idx, binst); ++idx)
+  ftrace_debug (bfun, "new tail call");
+
+  return bfun;
+}
+
+/* Find the innermost caller in the back trace of BFUN with MFUN/FUN
+   symbol information.  */
+
+static struct btrace_function *
+ftrace_find_caller (struct btrace_function *bfun,
+		    struct minimal_symbol *mfun,
+		    struct symbol *fun)
+{
+  for (; bfun != NULL; bfun = bfun->up)
+    {
+      /* Skip functions with incompatible symbol information.  */
+      if (ftrace_function_switched (bfun, mfun, fun))
+	continue;
+
+      /* This is the function segment we're looking for.  */
+      break;
+    }
+
+  return bfun;
+}
+
+/* Find the innermost caller in the back trace of BFUN, skipping all
+   function segments that do not end with a call instruction (e.g.
+   tail calls ending with a jump).  */
+
+static struct btrace_function *
+ftrace_find_call (struct gdbarch *gdbarch, struct btrace_function *bfun)
+{
+  for (; bfun != NULL; bfun = bfun->up)
     {
-      struct symtab_and_line sal;
-      struct bound_minimal_symbol mfun;
-      struct symbol *fun;
-      const char *filename;
+      struct btrace_insn *last;
       CORE_ADDR pc;
 
-      pc = binst->pc;
+      /* We do not allow empty function segments.  */
+      gdb_assert (!VEC_empty (btrace_insn_s, bfun->insn));
+
+      last = VEC_last (btrace_insn_s, bfun->insn);
+      pc = last->pc;
 
-      /* Try to determine the function we're in.  We use both types of symbols
-	 to avoid surprises when we sometimes get a full symbol and sometimes
-	 only a minimal symbol.  */
-      fun = find_pc_function (pc);
-      mfun = lookup_minimal_symbol_by_pc (pc);
+      if (gdbarch_insn_is_call (gdbarch, pc))
+	break;
+    }
+
+  return bfun;
+}
+
+/* Add a continuation segment for a function into which we return.
+   PREV is the chronologically preceding function segment.
+   MFUN and FUN are the symbol information we have for this function.  */
+
+static struct btrace_function *
+ftrace_new_return (struct gdbarch *gdbarch,
+		   struct btrace_function *prev,
+		   struct minimal_symbol *mfun,
+		   struct symbol *fun)
+{
+  struct btrace_function *bfun, *caller;
+
+  bfun = ftrace_new_function (prev, mfun, fun);
+
+  /* It is important to start at PREV's caller.  Otherwise, we might find
+     PREV itself, if PREV is a recursive function.  */
+  caller = ftrace_find_caller (prev->up, mfun, fun);
+  if (caller != NULL)
+    {
+      /* The caller of PREV is the preceding btrace function segment in this
+	 function instance.  */
+      gdb_assert (caller->segment.next == NULL);
+
+      caller->segment.next = bfun;
+      bfun->segment.prev = caller;
+
+      /* Maintain the function level.  */
+      bfun->level = caller->level;
+
+      /* Maintain the call stack.  */
+      bfun->up = caller->up;
+      bfun->flags = caller->flags;
+
+      ftrace_debug (bfun, "new return");
+    }
+  else
+    {
+      /* We did not find a caller.  This could mean that something went
+	 wrong or that the call is simply not included in the trace.  */
 
-      if (fun == NULL && mfun.minsym == NULL)
+      /* Let's search for some actual call.  */
+      caller = ftrace_find_call (gdbarch, prev->up);
+      if (caller == NULL)
 	{
-	  DEBUG_FTRACE ("no symbol at %u, pc=%s", idx,
-			core_addr_to_string_nz (pc));
-	  continue;
-	}
+	  /* There is no call in PREV's back trace.  We assume that the
+	     branch trace did not include it.  */
+
+	  /* Let's find the topmost call function - this skips tail calls.  */
+	  while (prev->up != NULL)
+	    prev = prev->up;
 
-      /* If we're switching functions, we start over.  */
-      if (ftrace_function_switched (bfun, mfun.minsym, fun))
+	  /* We maintain levels for a series of returns for which we have
+	     not seen the calls.
+	     We start at the preceding function's level in case this has
+	     already been a return for which we have not seen the call.
+	     We start at level 0 otherwise, to handle tail calls correctly.  */
+	  bfun->level = min (0, prev->level) - 1;
+
+	  /* Fix up the call stack for PREV.  */
+	  ftrace_fixup_caller (prev, bfun, BFUN_UP_LINKS_TO_RET);
+
+	  ftrace_debug (bfun, "new return - no caller");
+	}
+      else
 	{
-	  bfun = VEC_safe_push (btrace_func_s, ftrace, NULL);
+	  /* There is a call in PREV's back trace to which we should have
+	     returned.  Let's remain at this level.  */
+	  bfun->level = prev->level;
 
-	  ftrace_init_func (bfun, mfun.minsym, fun, idx);
-	  ftrace_debug (bfun, "init");
+	  ftrace_debug (bfun, "new return - unknown caller");
 	}
+    }
+
+  return bfun;
+}
+
+/* Add a new function segment for a function switch.
+   PREV is the chronologically preceding function segment.
+   MFUN and FUN are the symbol information we have for this function.  */
+
+static struct btrace_function *
+ftrace_new_switch (struct btrace_function *prev,
+		   struct minimal_symbol *mfun,
+		   struct symbol *fun)
+{
+  struct btrace_function *bfun;
+
+  /* This is an unexplained function switch.  The call stack will likely
+     be wrong at this point.  */
+  bfun = ftrace_new_function (prev, mfun, fun);
 
-      /* Update the instruction range.  */
-      bfun->iend = idx;
-      ftrace_debug (bfun, "update insns");
+  /* We keep the function level.  */
+  bfun->level = prev->level;
 
-      /* Let's see if we have source correlation, as well.  */
-      sal = find_pc_line (pc, 0);
-      if (sal.symtab == NULL || sal.line == 0)
+  ftrace_debug (bfun, "new switch");
+
+  return bfun;
+}
+
+/* Update BFUN with respect to the instruction at PC.  This may create new
+   function segments.
+   Return the chronologically latest function segment, never NULL.  */
+
+static struct btrace_function *
+ftrace_update_function (struct gdbarch *gdbarch,
+			struct btrace_function *bfun, CORE_ADDR pc)
+{
+  struct bound_minimal_symbol bmfun;
+  struct minimal_symbol *mfun;
+  struct symbol *fun;
+  struct btrace_insn *last;
+
+  /* Try to determine the function we're in.  We use both types of symbols
+     to avoid surprises when we sometimes get a full symbol and sometimes
+     only a minimal symbol.  */
+  fun = find_pc_function (pc);
+  bmfun = lookup_minimal_symbol_by_pc (pc);
+  mfun = bmfun.minsym;
+
+  if (fun == NULL && mfun == NULL)
+    DEBUG_FTRACE ("no symbol at %s", core_addr_to_string_nz (pc));
+
+  /* If we didn't have a function before, we create one.  */
+  if (bfun == NULL)
+    return ftrace_new_function (bfun, mfun, fun);
+
+  /* Check the last instruction, if we have one.
+     We do this check first, since it allows us to fill in the call stack
+     links in addition to the normal flow links.  */
+  last = NULL;
+  if (!VEC_empty (btrace_insn_s, bfun->insn))
+    last = VEC_last (btrace_insn_s, bfun->insn);
+
+  if (last != NULL)
+    {
+      CORE_ADDR lpc;
+
+      lpc = last->pc;
+
+      /* Check for returns.  */
+      if (gdbarch_insn_is_ret (gdbarch, lpc))
+	return ftrace_new_return (gdbarch, bfun, mfun, fun);
+
+      /* Check for calls.  */
+      if (gdbarch_insn_is_call (gdbarch, lpc))
 	{
-	  DEBUG_FTRACE ("no lines at %u, pc=%s", idx,
-			core_addr_to_string_nz (pc));
-	  continue;
+	  int size;
+
+	  size = gdb_insn_length (gdbarch, lpc);
+
+	  /* Ignore calls to the next instruction.  They are used for PIC.  */
+	  if (lpc + size != pc)
+	    return ftrace_new_call (bfun, mfun, fun);
 	}
+    }
+
+  /* Check if we're switching functions for some other reason.  */
+  if (ftrace_function_switched (bfun, mfun, fun))
+    {
+      DEBUG_FTRACE ("switching from %s in %s at %s",
+		    ftrace_print_insn_addr (last),
+		    ftrace_print_function_name (bfun),
+		    ftrace_print_filename (bfun));
 
-      /* Check if we switched files.  This could happen if, say, a macro that
-	 is defined in another file is expanded here.  */
-      filename = symtab_to_fullname (sal.symtab);
-      if (ftrace_skip_file (bfun, filename))
+      if (last != NULL)
 	{
-	  DEBUG_FTRACE ("ignoring file at %u, pc=%s, file=%s", idx,
-			core_addr_to_string_nz (pc), filename);
-	  continue;
+	  CORE_ADDR start, lpc;
+
+	  start = get_pc_function_start (pc);
+
+	  /* If we can't determine the function for PC, we treat a jump at
+	     the end of the block as tail call.  */
+	  if (start == 0)
+	    start = pc;
+
+	  lpc = last->pc;
+
+	  /* Jumps indicate optimized tail calls.  */
+	  if (start == pc && gdbarch_insn_is_jump (gdbarch, lpc))
+	    return ftrace_new_tailcall (bfun, mfun, fun);
 	}
 
-      /* Update the line range.  */
-      bfun->lbegin = min (bfun->lbegin, sal.line);
-      bfun->lend = max (bfun->lend, sal.line);
-      ftrace_debug (bfun, "update lines");
+      return ftrace_new_switch (bfun, mfun, fun);
+    }
+
+  return bfun;
+}
+
+/* Update BFUN's source range with respect to the instruction at PC.  */
+
+static void
+ftrace_update_lines (struct btrace_function *bfun, CORE_ADDR pc)
+{
+  struct symtab_and_line sal;
+  const char *fullname;
+
+  sal = find_pc_line (pc, 0);
+  if (sal.symtab == NULL || sal.line == 0)
+    {
+      DEBUG_FTRACE ("no lines at %s", core_addr_to_string_nz (pc));
+      return;
+    }
+
+  /* Check if we switched files.  This could happen if, say, a macro that
+     is defined in another file is expanded here.  */
+  fullname = symtab_to_fullname (sal.symtab);
+  if (ftrace_skip_file (bfun, fullname))
+    {
+      DEBUG_FTRACE ("ignoring file at %s, file=%s",
+		    core_addr_to_string_nz (pc), fullname);
+      return;
+    }
+
+  /* Update the line range.  */
+  bfun->lbegin = min (bfun->lbegin, sal.line);
+  bfun->lend = max (bfun->lend, sal.line);
+
+  if (record_debug > 1)
+    ftrace_debug (bfun, "update lines");
+}
+
+/* Add the instruction at PC to BFUN's instructions.  */
+
+static void
+ftrace_update_insns (struct btrace_function *bfun, CORE_ADDR pc)
+{
+  struct btrace_insn *insn;
+
+  insn = VEC_safe_push (btrace_insn_s, bfun->insn, NULL);
+  insn->pc = pc;
+
+  if (record_debug > 1)
+    ftrace_debug (bfun, "update insn");
+}
+
+/* Compute the function branch trace from a block branch trace BTRACE for
+   a thread given by BTINFO.  */
+
+static void
+btrace_compute_ftrace (struct btrace_thread_info *btinfo,
+		       VEC (btrace_block_s) *btrace)
+{
+  struct btrace_function *begin, *end;
+  struct gdbarch *gdbarch;
+  unsigned int blk;
+  int level;
+
+  DEBUG ("compute ftrace");
+
+  gdbarch = target_gdbarch ();
+  begin = NULL;
+  end = NULL;
+  level = INT_MAX;
+  blk = VEC_length (btrace_block_s, btrace);
+
+  while (blk != 0)
+    {
+      btrace_block_s *block;
+      CORE_ADDR pc;
+
+      blk -= 1;
+
+      block = VEC_index (btrace_block_s, btrace, blk);
+      pc = block->begin;
+
+      for (;;)
+	{
+	  int size;
+
+	  /* We should hit the end of the block.  Warn if we went too far.  */
+	  if (block->end < pc)
+	    {
+	      warning (_("Recorded trace may be corrupted around %s."),
+		       core_addr_to_string_nz (pc));
+	      break;
+	    }
+
+	  end = ftrace_update_function (gdbarch, end, pc);
+	  if (begin == NULL)
+	    begin = end;
+
+	  /* Maintain the function level offset.  */
+	  level = min (level, end->level);
+
+	  ftrace_update_insns (end, pc);
+	  ftrace_update_lines (end, pc);
+
+	  /* We're done once we pushed the instruction at the end.  */
+	  if (block->end == pc)
+	    break;
+
+	  size = gdb_insn_length (gdbarch, pc);
+
+	  /* Make sure we terminate if we fail to compute the size.  */
+	  if (size <= 0)
+	    {
+	      warning (_("Recorded trace may be incomplete around %s."),
+		       core_addr_to_string_nz (pc));
+	      break;
+	    }
+
+	  pc += size;
+	}
     }
 
-  return ftrace;
+  btinfo->begin = begin;
+  btinfo->end = end;
+
+  /* LEVEL is the minimal function level of all btrace function segments.
+     Define the global level offset to -LEVEL so all function levels are
+     normalized to start at zero.  */
+  btinfo->level = -level;
 }
 
 /* See btrace.h.  */
@@ -394,6 +718,7 @@ btrace_fetch (struct thread_info *tp)
 {
   struct btrace_thread_info *btinfo;
   VEC (btrace_block_s) *btrace;
+  struct cleanup *cleanup;
 
   DEBUG ("fetch thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
 
@@ -402,18 +727,15 @@ btrace_fetch (struct thread_info *tp)
     return;
 
   btrace = target_read_btrace (btinfo->target, BTRACE_READ_NEW);
-  if (VEC_empty (btrace_block_s, btrace))
-    return;
-
-  btrace_clear (tp);
+  cleanup = make_cleanup (VEC_cleanup (btrace_block_s), &btrace);
 
-  btinfo->btrace = btrace;
-  btinfo->itrace = compute_itrace (btinfo->btrace);
-  btinfo->ftrace = compute_ftrace (btinfo->itrace);
+  if (!VEC_empty (btrace_block_s, btrace))
+    {
+      btrace_clear (tp);
+      btrace_compute_ftrace (btinfo, btrace);
+    }
 
-  /* Initialize branch trace iterators.  */
-  btrace_init_insn_iterator (btinfo);
-  btrace_init_func_iterator (btinfo);
+  do_cleanups (cleanup);
 }
 
 /* See btrace.h.  */
@@ -422,18 +744,29 @@ void
 btrace_clear (struct thread_info *tp)
 {
   struct btrace_thread_info *btinfo;
+  struct btrace_function *it, *trash;
 
   DEBUG ("clear thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
 
   btinfo = &tp->btrace;
 
-  VEC_free (btrace_block_s, btinfo->btrace);
-  VEC_free (btrace_inst_s, btinfo->itrace);
-  VEC_free (btrace_func_s, btinfo->ftrace);
+  it = btinfo->begin;
+  while (it != NULL)
+    {
+      trash = it;
+      it = it->flow.next;
 
-  btinfo->btrace = NULL;
-  btinfo->itrace = NULL;
-  btinfo->ftrace = NULL;
+      xfree (trash);
+    }
+
+  btinfo->begin = NULL;
+  btinfo->end = NULL;
+
+  xfree (btinfo->insn_history);
+  xfree (btinfo->call_history);
+
+  btinfo->insn_history = NULL;
+  btinfo->call_history = NULL;
 }
 
 /* See btrace.h.  */
@@ -541,3 +874,451 @@ parse_xml_btrace (const char *buffer)
 
   return btrace;
 }
+
+/* See btrace.h.  */
+
+const struct btrace_insn *
+btrace_insn_get (const struct btrace_insn_iterator *it)
+{
+  const struct btrace_function *bfun;
+  unsigned int index, end;
+
+  index = it->index;
+  bfun = it->function;
+
+  /* The index is within the bounds of this function's instruction vector.  */
+  end = VEC_length (btrace_insn_s, bfun->insn);
+  gdb_assert (0 < end);
+  gdb_assert (index < end);
+
+  return VEC_index (btrace_insn_s, bfun->insn, index);
+}
+
+/* See btrace.h.  */
+
+unsigned int
+btrace_insn_number (const struct btrace_insn_iterator *it)
+{
+  const struct btrace_function *bfun;
+
+  bfun = it->function;
+  return bfun->insn_offset + it->index;
+}
+
+/* See btrace.h.  */
+
+void
+btrace_insn_begin (struct btrace_insn_iterator *it,
+		   const struct btrace_thread_info *btinfo)
+{
+  const struct btrace_function *bfun;
+
+  bfun = btinfo->begin;
+  if (bfun == NULL)
+    error (_("No trace."));
+
+  it->function = bfun;
+  it->index = 0;
+}
+
+/* See btrace.h.  */
+
+void
+btrace_insn_end (struct btrace_insn_iterator *it,
+		 const struct btrace_thread_info *btinfo)
+{
+  const struct btrace_function *bfun;
+  unsigned int length;
+
+  bfun = btinfo->end;
+  if (bfun == NULL)
+    error (_("No trace."));
+
+  /* The last instruction in the last function is the current instruction.
+     We point to it - it is one past the end of the execution trace.  */
+  length = VEC_length (btrace_insn_s, bfun->insn);
+
+  it->function = bfun;
+  it->index = length - 1;
+}
+
+/* See btrace.h.  */
+
+unsigned int
+btrace_insn_next (struct btrace_insn_iterator *it, unsigned int stride)
+{
+  const struct btrace_function *bfun;
+  unsigned int index, steps;
+
+  bfun = it->function;
+  steps = 0;
+  index = it->index;
+
+  while (stride != 0)
+    {
+      unsigned int end, space, adv;
+
+      end = VEC_length (btrace_insn_s, bfun->insn);
+
+      gdb_assert (0 < end);
+      gdb_assert (index < end);
+
+      /* Compute the number of instructions remaining in this segment.  */
+      space = end - index;
+
+      /* Advance the iterator as far as possible within this segment.  */
+      adv = min (space, stride);
+      stride -= adv;
+      index += adv;
+      steps += adv;
+
+      /* Move to the next function if we're at the end of this one.  */
+      if (index == end)
+	{
+	  const struct btrace_function *next;
+
+	  next = bfun->flow.next;
+	  if (next == NULL)
+	    {
+	      /* We stepped past the last function.
+
+		 Let's adjust the index to point to the last instruction in
+		 the previous function.  */
+	      index -= 1;
+	      steps -= 1;
+	      break;
+	    }
+
+	  /* We now point to the first instruction in the new function.  */
+	  bfun = next;
+	  index = 0;
+	}
+
+      /* We did make progress.  */
+      gdb_assert (adv > 0);
+    }
+
+  /* Update the iterator.  */
+  it->function = bfun;
+  it->index = index;
+
+  return steps;
+}
+
+/* See btrace.h.  */
+
+unsigned int
+btrace_insn_prev (struct btrace_insn_iterator *it, unsigned int stride)
+{
+  const struct btrace_function *bfun;
+  unsigned int index, steps;
+
+  bfun = it->function;
+  steps = 0;
+  index = it->index;
+
+  while (stride != 0)
+    {
+      unsigned int adv;
+
+      /* Move to the previous function if we're at the start of this one.  */
+      if (index == 0)
+	{
+	  const struct btrace_function *prev;
+
+	  prev = bfun->flow.prev;
+	  if (prev == NULL)
+	    break;
+
+	  /* We point to one after the last instruction in the new function.  */
+	  bfun = prev;
+	  index = VEC_length (btrace_insn_s, bfun->insn);
+
+	  /* There is at least one instruction in this function segment.  */
+	  gdb_assert (index > 0);
+	}
+
+      /* Advance the iterator as far as possible within this segment.  */
+      adv = min (index, stride);
+      stride -= adv;
+      index -= adv;
+      steps += adv;
+
+      /* We did make progress.  */
+      gdb_assert (adv > 0);
+    }
+
+  /* Update the iterator.  */
+  it->function = bfun;
+  it->index = index;
+
+  return steps;
+}
+
+/* See btrace.h.  */
+
+int
+btrace_insn_cmp (const struct btrace_insn_iterator *lhs,
+		 const struct btrace_insn_iterator *rhs)
+{
+  unsigned int lnum, rnum;
+
+  lnum = btrace_insn_number (lhs);
+  rnum = btrace_insn_number (rhs);
+
+  return (int) (lnum - rnum);
+}
+
+/* See btrace.h.  */
+
+int
+btrace_find_insn_by_number (struct btrace_insn_iterator *it,
+			    const struct btrace_thread_info *btinfo,
+			    unsigned int number)
+{
+  const struct btrace_function *bfun;
+  unsigned int end;
+
+  for (bfun = btinfo->end; bfun != NULL; bfun = bfun->flow.prev)
+    if (bfun->insn_offset <= number)
+      break;
+
+  if (bfun == NULL)
+    return 0;
+
+  end = bfun->insn_offset + VEC_length (btrace_insn_s, bfun->insn);
+  if (end <= number)
+    return 0;
+
+  it->function = bfun;
+  it->index = number - bfun->insn_offset;
+
+  return 1;
+}
+
+/* See btrace.h.  */
+
+const struct btrace_function *
+btrace_call_get (const struct btrace_call_iterator *it)
+{
+  return it->function;
+}
+
+/* See btrace.h.  */
+
+unsigned int
+btrace_call_number (const struct btrace_call_iterator *it)
+{
+  const struct btrace_thread_info *btinfo;
+  const struct btrace_function *bfun;
+  unsigned int insns;
+
+  btinfo = it->btinfo;
+  bfun = it->function;
+  if (bfun != NULL)
+    return bfun->number;
+
+  /* For the end iterator, i.e. bfun == NULL, we return one more than the
+     number of the last function.  */
+  bfun = btinfo->end;
+  insns = VEC_length (btrace_insn_s, bfun->insn);
+
+  /* If the function contains only a single instruction (i.e. the current
+     instruction), it will be skipped and its number is already the number
+     we seek.  */
+  if (insns == 1)
+    return bfun->number;
+
+  /* Otherwise, return one more than the number of the last function.  */
+  return bfun->number + 1;
+}
+
+/* See btrace.h.  */
+
+void
+btrace_call_begin (struct btrace_call_iterator *it,
+		   const struct btrace_thread_info *btinfo)
+{
+  const struct btrace_function *bfun;
+
+  bfun = btinfo->begin;
+  if (bfun == NULL)
+    error (_("No trace."));
+
+  it->btinfo = btinfo;
+  it->function = bfun;
+}
+
+/* See btrace.h.  */
+
+void
+btrace_call_end (struct btrace_call_iterator *it,
+		 const struct btrace_thread_info *btinfo)
+{
+  const struct btrace_function *bfun;
+
+  bfun = btinfo->end;
+  if (bfun == NULL)
+    error (_("No trace."));
+
+  it->btinfo = btinfo;
+  it->function = NULL;
+}
+
+/* See btrace.h.  */
+
+unsigned int
+btrace_call_next (struct btrace_call_iterator *it, unsigned int stride)
+{
+  const struct btrace_function *bfun;
+  unsigned int steps;
+
+  bfun = it->function;
+  steps = 0;
+  while (bfun != NULL)
+    {
+      const struct btrace_function *next;
+      unsigned int insns;
+
+      next = bfun->flow.next;
+      if (next == NULL)
+	{
+	  /* Ignore the last function if it only contains a single
+	     (i.e. the current) instruction.  */
+	  insns = VEC_length (btrace_insn_s, bfun->insn);
+	  if (insns == 1)
+	    steps -= 1;
+	}
+
+      if (stride == steps)
+	break;
+
+      bfun = next;
+      steps += 1;
+    }
+
+  it->function = bfun;
+  return steps;
+}
+
+/* See btrace.h.  */
+
+unsigned int
+btrace_call_prev (struct btrace_call_iterator *it, unsigned int stride)
+{
+  const struct btrace_thread_info *btinfo;
+  const struct btrace_function *bfun;
+  unsigned int steps;
+
+  bfun = it->function;
+  steps = 0;
+
+  if (bfun == NULL)
+    {
+      unsigned int insns;
+
+      btinfo = it->btinfo;
+      bfun = btinfo->end;
+      if (bfun == NULL)
+	return 0;
+
+      /* Ignore the last function if it only contains a single
+	 (i.e. the current) instruction.  */
+      insns = VEC_length (btrace_insn_s, bfun->insn);
+      if (insns == 1)
+	bfun = bfun->flow.prev;
+
+      if (bfun == NULL)
+	return 0;
+
+      steps += 1;
+    }
+
+  while (steps < stride)
+    {
+      const struct btrace_function *prev;
+
+      prev = bfun->flow.prev;
+      if (prev == NULL)
+	break;
+
+      bfun = prev;
+      steps += 1;
+    }
+
+  it->function = bfun;
+  return steps;
+}
+
+/* See btrace.h.  */
+
+int
+btrace_call_cmp (const struct btrace_call_iterator *lhs,
+		 const struct btrace_call_iterator *rhs)
+{
+  unsigned int lnum, rnum;
+
+  lnum = btrace_call_number (lhs);
+  rnum = btrace_call_number (rhs);
+
+  return (int) (lnum - rnum);
+}
+
+/* See btrace.h.  */
+
+int
+btrace_find_call_by_number (struct btrace_call_iterator *it,
+			    const struct btrace_thread_info *btinfo,
+			    unsigned int number)
+{
+  const struct btrace_function *bfun;
+
+  for (bfun = btinfo->end; bfun != NULL; bfun = bfun->flow.prev)
+    {
+      unsigned int bnum;
+
+      bnum = bfun->number;
+      if (number == bnum)
+	{
+	  it->btinfo = btinfo;
+	  it->function = bfun;
+	  return 1;
+	}
+
+      /* Functions are ordered and numbered consecutively.  We could bail out
+	 earlier.  On the other hand, it is very unlikely that we search for
+	 a nonexistent function.  */
+  }
+
+  return 0;
+}
+
+/* See btrace.h.  */
+
+void
+btrace_set_insn_history (struct btrace_thread_info *btinfo,
+			 const struct btrace_insn_iterator *begin,
+			 const struct btrace_insn_iterator *end)
+{
+  if (btinfo->insn_history == NULL)
+    btinfo->insn_history = xzalloc (sizeof (*btinfo->insn_history));
+
+  btinfo->insn_history->begin = *begin;
+  btinfo->insn_history->end = *end;
+}
+
+/* See btrace.h.  */
+
+void
+btrace_set_call_history (struct btrace_thread_info *btinfo,
+			 const struct btrace_call_iterator *begin,
+			 const struct btrace_call_iterator *end)
+{
+  gdb_assert (begin->btinfo == end->btinfo);
+
+  if (btinfo->call_history == NULL)
+    btinfo->call_history = xzalloc (sizeof (*btinfo->call_history));
+
+  btinfo->call_history->begin = *begin;
+  btinfo->call_history->end = *end;
+}
diff --git a/gdb/btrace.h b/gdb/btrace.h
index bd8425d..f4b2cc1 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -29,63 +29,128 @@
 #include "btrace-common.h"
 
 struct thread_info;
+struct btrace_function;
 
 /* A branch trace instruction.
 
    This represents a single instruction in a branch trace.  */
-struct btrace_inst
+struct btrace_insn
 {
   /* The address of this instruction.  */
   CORE_ADDR pc;
 };
 
-/* A branch trace function.
+/* A vector of branch trace instructions.  */
+typedef struct btrace_insn btrace_insn_s;
+DEF_VEC_O (btrace_insn_s);
+
+/* A doubly-linked list of branch trace function segments.  */
+struct btrace_func_link
+{
+  struct btrace_function *prev;
+  struct btrace_function *next;
+};
+
+/* Flags for btrace function segments.  */
+enum btrace_function_flag
+{
+  /* The 'up' link interpretation.
+     If set, it points to the function segment we returned to.
+     If clear, it points to the function segment we called from.  */
+  BFUN_UP_LINKS_TO_RET = (1 << 0),
+
+  /* The 'up' link points to a tail call.  This obviously only makes sense
+     if bfun_up_links_to_ret is clear.  */
+  BFUN_UP_LINKS_TO_TAILCALL = (1 << 1)
+};
+
+/* A branch trace function segment.
 
    This represents a function segment in a branch trace, i.e. a consecutive
-   number of instructions belonging to the same function.  */
-struct btrace_func
+   number of instructions belonging to the same function.
+
+   We do not allow function segments without any instructions.  */
+struct btrace_function
 {
-  /* The full and minimal symbol for the function.  One of them may be NULL.  */
+  /* The full and minimal symbol for the function.  Both may be NULL.  */
   struct minimal_symbol *msym;
   struct symbol *sym;
 
+  /* The previous and next segment belonging to the same function.
+     If a function calls another function, the former will have at least
+     two segments: one before the call and another after the return.  */
+  struct btrace_func_link segment;
+
+  /* The previous and next function in control flow order.  */
+  struct btrace_func_link flow;
+
+  /* The directly preceding function segment in a (fake) call stack.  */
+  struct btrace_function *up;
+
+  /* The instructions in this function segment.
+     The instruction vector will never be empty.  */
+  VEC (btrace_insn_s) *insn;
+
+  /* The instruction number offset for the first instruction in this
+     function segment.  */
+  unsigned int insn_offset;
+
+  /* The function number in control-flow order.  */
+  unsigned int number;
+
+  /* The function level in a back trace across the entire branch trace.
+     A caller's level is one lower than the level of its callee.
+
+     Levels can be negative if we see returns for which we have not seen
+     the corresponding calls.  The branch trace thread information provides
+     a fixup to normalize function levels so the smallest level is zero.  */
+  int level;
+
   /* The source line range of this function segment (both inclusive).  */
   int lbegin, lend;
 
-  /* The instruction number range in the instruction trace corresponding
-     to this function segment (both inclusive).  */
-  unsigned int ibegin, iend;
+  /* A bit-vector of btrace_function_flag.  */
+  enum btrace_function_flag flags;
 };
 
-/* Branch trace may also be represented as a vector of:
+/* A branch trace instruction iterator.  */
+struct btrace_insn_iterator
+{
+  /* The branch trace function segment containing the instruction.
+     Will never be NULL.  */
+  const struct btrace_function *function;
 
-   - branch trace instructions starting with the oldest instruction.
-   - branch trace functions starting with the oldest function.  */
-typedef struct btrace_inst btrace_inst_s;
-typedef struct btrace_func btrace_func_s;
+  /* The index into the function segment's instruction vector.  */
+  unsigned int index;
+};
 
-/* Define functions operating on branch trace vectors.  */
-DEF_VEC_O (btrace_inst_s);
-DEF_VEC_O (btrace_func_s);
+/* A branch trace function call iterator.  */
+struct btrace_call_iterator
+{
+  /* The branch trace information for this thread.  Will never be NULL.  */
+  const struct btrace_thread_info *btinfo;
+
+  /* The branch trace function segment.
+     This will be NULL for the iterator pointing to the end of the trace.  */
+  const struct btrace_function *function;
+};
 
 /* Branch trace iteration state for "record instruction-history".  */
-struct btrace_insn_iterator
+struct btrace_insn_history
 {
-  /* The instruction index range from begin (inclusive) to end (exclusive)
-     that has been covered last time.
-     If end < begin, the branch trace has just been updated.  */
-  unsigned int begin;
-  unsigned int end;
+  /* The branch trace instruction range from BEGIN (inclusive) to
+     END (exclusive) that has been covered last time.  */
+  struct btrace_insn_iterator begin;
+  struct btrace_insn_iterator end;
 };
 
 /* Branch trace iteration state for "record function-call-history".  */
-struct btrace_func_iterator
+struct btrace_call_history
 {
-  /* The function index range from begin (inclusive) to end (exclusive)
-     that has been covered last time.
-     If end < begin, the branch trace has just been updated.  */
-  unsigned int begin;
-  unsigned int end;
+  /* The branch trace function range from BEGIN (inclusive) to END (exclusive)
+     that has been covered last time.  */
+  struct btrace_call_iterator begin;
+  struct btrace_call_iterator end;
 };
 
 /* Branch trace information per thread.
@@ -103,16 +168,25 @@ struct btrace_thread_info
      the underlying architecture.  */
   struct btrace_target_info *target;
 
-  /* The current branch trace for this thread.  */
-  VEC (btrace_block_s) *btrace;
-  VEC (btrace_inst_s) *itrace;
-  VEC (btrace_func_s) *ftrace;
+  /* The current branch trace for this thread (both inclusive).
+
+     The last instruction of END is the current instruction, which is not
+     part of the execution history.
+     Both will be NULL if there is no branch trace available.  If there is
+     branch trace available, both will be non-NULL.  */
+  struct btrace_function *begin;
+  struct btrace_function *end;
+
+  /* The function level offset.  When added to each function's LEVEL,
+     this normalizes the function levels such that the smallest level
+     becomes zero.  */
+  int level;
 
   /* The instruction history iterator.  */
-  struct btrace_insn_iterator insn_iterator;
+  struct btrace_insn_history *insn_history;
 
   /* The function call history iterator.  */
-  struct btrace_func_iterator func_iterator;
+  struct btrace_call_history *call_history;
 };
 
 /* Enable branch tracing for a thread.  */
@@ -139,4 +213,99 @@ extern void btrace_free_objfile (struct objfile *);
 /* Parse a branch trace xml document into a block vector.  */
 extern VEC (btrace_block_s) *parse_xml_btrace (const char*);
 
+/* Dereference a branch trace instruction iterator.  Return a pointer to the
+   instruction the iterator points to.  */
+extern const struct btrace_insn *
+  btrace_insn_get (const struct btrace_insn_iterator *);
+
+/* Return the instruction number for a branch trace iterator.
+   Returns one past the maximum instruction number for the end iterator.
+   Returns zero if the iterator does not point to a valid instruction.  */
+extern unsigned int btrace_insn_number (const struct btrace_insn_iterator *);
+
+/* Initialize a branch trace instruction iterator to point to the begin/end of
+   the branch trace.  Throws an error if there is no branch trace.  */
+extern void btrace_insn_begin (struct btrace_insn_iterator *,
+			       const struct btrace_thread_info *);
+extern void btrace_insn_end (struct btrace_insn_iterator *,
+			     const struct btrace_thread_info *);
+
+/* Increment/decrement a branch trace instruction iterator by at most STRIDE
+   instructions.  Return the number of instructions by which the instruction
+   iterator has been advanced.
+   Returns zero, if the operation failed or STRIDE had been zero.  */
+extern unsigned int btrace_insn_next (struct btrace_insn_iterator *,
+				      unsigned int stride);
+extern unsigned int btrace_insn_prev (struct btrace_insn_iterator *,
+				      unsigned int stride);
+
+/* Compare two branch trace instruction iterators.
+   Return a negative number if LHS < RHS.
+   Return zero if LHS == RHS.
+   Return a positive number if LHS > RHS.  */
+extern int btrace_insn_cmp (const struct btrace_insn_iterator *lhs,
+			    const struct btrace_insn_iterator *rhs);
+
+/* Find an instruction in the function branch trace by its number.
+   If the instruction is found, initialize the branch trace instruction
+   iterator to point to this instruction and return non-zero.
+   Return zero otherwise.  */
+extern int btrace_find_insn_by_number (struct btrace_insn_iterator *,
+				       const struct btrace_thread_info *,
+				       unsigned int number);
+
+/* Dereference a branch trace call iterator.  Return a pointer to the
+   function the iterator points to or NULL if the interator points past
+   the end of the branch trace.  */
+extern const struct btrace_function *
+  btrace_call_get (const struct btrace_call_iterator *);
+
+/* Return the function number for a branch trace call iterator.
+   Returns one past the maximum function number for the end iterator.
+   Returns zero if the iterator does not point to a valid function.  */
+extern unsigned int btrace_call_number (const struct btrace_call_iterator *);
+
+/* Initialize a branch trace call iterator to point to the begin/end of
+   the branch trace.  Throws an error if there is no branch trace.  */
+extern void btrace_call_begin (struct btrace_call_iterator *,
+			       const struct btrace_thread_info *);
+extern void btrace_call_end (struct btrace_call_iterator *,
+			     const struct btrace_thread_info *);
+
+/* Increment/decrement a branch trace call iterator by at most STRIDE function
+   segments.  Return the number of function segments by which the call
+   iterator has been advanced.
+   Returns zero, if the operation failed or STRIDE had been zero.  */
+extern unsigned int btrace_call_next (struct btrace_call_iterator *,
+				      unsigned int stride);
+extern unsigned int btrace_call_prev (struct btrace_call_iterator *,
+				      unsigned int stride);
+
+/* Compare two branch trace call iterators.
+   Return a negative number if LHS < RHS.
+   Return zero if LHS == RHS.
+   Return a positive number if LHS > RHS.  */
+extern int btrace_call_cmp (const struct btrace_call_iterator *lhs,
+			    const struct btrace_call_iterator *rhs);
+
+/* Find a function in the function branch trace by its NUMBER.
+   If the function is found, initialize the branch trace call
+   iterator to point to this function and return non-zero.
+   Return zero otherwise.  */
+extern int btrace_find_call_by_number (struct btrace_call_iterator *,
+				       const struct btrace_thread_info *,
+				       unsigned int number);
+
+/* Set the branch trace instruction history from BEGIN (inclusive) to
+   END (exclusive).  */
+extern void btrace_set_insn_history (struct btrace_thread_info *,
+				     const struct btrace_insn_iterator *begin,
+				     const struct btrace_insn_iterator *end);
+
+/* Set the branch trace function call history from BEGIN (inclusive) to
+   END (exclusive).  */
+extern void btrace_set_call_history (struct btrace_thread_info *,
+				     const struct btrace_call_iterator *begin,
+				     const struct btrace_call_iterator *end);
+
 #endif /* BTRACE_H */
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 68f40c8..248dc2e 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -74,7 +74,7 @@ require_btrace (void)
 
   btinfo = &tp->btrace;
 
-  if (VEC_empty (btrace_inst_s, btinfo->itrace))
+  if (btinfo->begin == NULL)
     error (_("No trace."));
 
   return btinfo;
@@ -206,7 +206,7 @@ record_btrace_info (void)
 {
   struct btrace_thread_info *btinfo;
   struct thread_info *tp;
-  unsigned int insts, funcs;
+  unsigned int insns, calls;
 
   DEBUG ("info");
 
@@ -216,12 +216,26 @@ record_btrace_info (void)
 
   btrace_fetch (tp);
 
+  insns = 0;
+  calls = 0;
+
   btinfo = &tp->btrace;
-  insts = VEC_length (btrace_inst_s, btinfo->itrace);
-  funcs = VEC_length (btrace_func_s, btinfo->ftrace);
+  if (btinfo->begin != NULL)
+    {
+      struct btrace_call_iterator call;
+      struct btrace_insn_iterator insn;
+
+      btrace_call_end (&call, btinfo);
+      btrace_call_prev (&call, 1);
+      calls = btrace_call_number (&call) + 1;
+
+      btrace_insn_end (&insn, btinfo);
+      btrace_insn_prev (&insn, 1);
+      insns = btrace_insn_number (&insn) + 1;
+    }
 
   printf_unfiltered (_("Recorded %u instructions in %u functions for thread "
-		       "%d (%s).\n"), insts, funcs, tp->num,
+		       "%d (%s).\n"), insns, calls, tp->num,
 		     target_pid_to_str (tp->ptid));
 }
 
@@ -236,27 +250,31 @@ ui_out_field_uint (struct ui_out *uiout, const char *fld, unsigned int val)
 /* Disassemble a section of the recorded instruction trace.  */
 
 static void
-btrace_insn_history (struct btrace_thread_info *btinfo, struct ui_out *uiout,
-		     unsigned int begin, unsigned int end, int flags)
+btrace_insn_history (struct ui_out *uiout,
+		     const struct btrace_insn_iterator *begin,
+		     const struct btrace_insn_iterator *end, int flags)
 {
   struct gdbarch *gdbarch;
-  struct btrace_inst *inst;
-  unsigned int idx;
+  struct btrace_insn_iterator it;
 
-  DEBUG ("itrace (0x%x): [%u; %u[", flags, begin, end);
+  DEBUG ("itrace (0x%x): [%u; %u)", flags, btrace_insn_number (begin),
+	 btrace_insn_number (end));
 
   gdbarch = target_gdbarch ();
 
-  for (idx = begin; VEC_iterate (btrace_inst_s, btinfo->itrace, idx, inst)
-	 && idx < end; ++idx)
+  for (it = *begin; btrace_insn_cmp (&it, end) != 0; btrace_insn_next (&it, 1))
     {
+      const struct btrace_insn *insn;
+
+      insn = btrace_insn_get (&it);
+
       /* Print the instruction index.  */
-      ui_out_field_uint (uiout, "index", idx);
+      ui_out_field_uint (uiout, "index", btrace_insn_number (&it));
       ui_out_text (uiout, "\t");
 
       /* Disassembly with '/m' flag may not produce the expected result.
 	 See PR gdb/11833.  */
-      gdb_disassembly (gdbarch, uiout, NULL, flags, 1, inst->pc, inst->pc + 1);
+      gdb_disassembly (gdbarch, uiout, NULL, flags, 1, insn->pc, insn->pc + 1);
     }
 }
 
@@ -266,72 +284,62 @@ static void
 record_btrace_insn_history (int size, int flags)
 {
   struct btrace_thread_info *btinfo;
+  struct btrace_insn_history *history;
+  struct btrace_insn_iterator begin, end;
   struct cleanup *uiout_cleanup;
   struct ui_out *uiout;
-  unsigned int context, last, begin, end;
+  unsigned int context, covered;
 
   uiout = current_uiout;
   uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout,
 						       "insn history");
-  btinfo = require_btrace ();
-  last = VEC_length (btrace_inst_s, btinfo->itrace);
-
   context = abs (size);
-  begin = btinfo->insn_iterator.begin;
-  end = btinfo->insn_iterator.end;
-
-  DEBUG ("insn-history (0x%x): %d, prev: [%u; %u[", flags, size, begin, end);
-
   if (context == 0)
     error (_("Bad record instruction-history-size."));
 
-  /* We start at the end.  */
-  if (end < begin)
-    {
-      /* Truncate the context, if necessary.  */
-      context = min (context, last);
-
-      end = last;
-      begin = end - context;
-    }
-  else if (size < 0)
+  btinfo = require_btrace ();
+  history = btinfo->insn_history;
+  if (history == NULL)
     {
-      if (begin == 0)
-	{
-	  printf_unfiltered (_("At the start of the branch trace record.\n"));
-
-	  btinfo->insn_iterator.end = 0;
-	  return;
-	}
+      /* No matter the direction, we start with the tail of the trace.  */
+      btrace_insn_end (&begin, btinfo);
+      end = begin;
 
-      /* Truncate the context, if necessary.  */
-      context = min (context, begin);
+      DEBUG ("insn-history (0x%x): %d", flags, size);
 
-      end = begin;
-      begin -= context;
+      covered = btrace_insn_prev (&begin, context);
     }
   else
     {
-      if (end == last)
-	{
-	  printf_unfiltered (_("At the end of the branch trace record.\n"));
+      begin = history->begin;
+      end = history->end;
 
-	  btinfo->insn_iterator.begin = last;
-	  return;
-	}
+      DEBUG ("insn-history (0x%x): %d, prev: [%u; %u)", flags, size,
+	     btrace_insn_number (&begin), btrace_insn_number (&end));
 
-      /* Truncate the context, if necessary.  */
-      context = min (context, last - end);
-
-      begin = end;
-      end += context;
+      if (size < 0)
+	{
+	  end = begin;
+	  covered = btrace_insn_prev (&begin, context);
+	}
+      else
+	{
+	  begin = end;
+	  covered = btrace_insn_next (&end, context);
+	}
     }
 
-  btrace_insn_history (btinfo, uiout, begin, end, flags);
-
-  btinfo->insn_iterator.begin = begin;
-  btinfo->insn_iterator.end = end;
+  if (covered > 0)
+    btrace_insn_history (uiout, &begin, &end, flags);
+  else
+    {
+      if (size < 0)
+	printf_unfiltered (_("At the start of the branch trace record.\n"));
+      else
+	printf_unfiltered (_("At the end of the branch trace record.\n"));
+    }
 
+  btrace_set_insn_history (btinfo, &begin, &end);
   do_cleanups (uiout_cleanup);
 }
 
@@ -341,39 +349,41 @@ static void
 record_btrace_insn_history_range (ULONGEST from, ULONGEST to, int flags)
 {
   struct btrace_thread_info *btinfo;
+  struct btrace_insn_history *history;
+  struct btrace_insn_iterator begin, end;
   struct cleanup *uiout_cleanup;
   struct ui_out *uiout;
-  unsigned int last, begin, end;
+  unsigned int low, high;
+  int found;
 
   uiout = current_uiout;
   uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout,
 						       "insn history");
-  btinfo = require_btrace ();
-  last = VEC_length (btrace_inst_s, btinfo->itrace);
+  low = from;
+  high = to;
 
-  begin = (unsigned int) from;
-  end = (unsigned int) to;
-
-  DEBUG ("insn-history (0x%x): [%u; %u[", flags, begin, end);
+  DEBUG ("insn-history (0x%x): [%u; %u)", flags, low, high);
 
   /* Check for wrap-arounds.  */
-  if (begin != from || end != to)
+  if (low != from || high != to)
     error (_("Bad range."));
 
-  if (end <= begin)
+  if (high <= low)
     error (_("Bad range."));
 
-  if (last <= begin)
-    error (_("Range out of bounds."));
+  btinfo = require_btrace ();
 
-  /* Truncate the range, if necessary.  */
-  if (last < end)
-    end = last;
+  found = btrace_find_insn_by_number (&begin, btinfo, low);
+  if (found == 0)
+    error (_("Range out of bounds."));
 
-  btrace_insn_history (btinfo, uiout, begin, end, flags);
+  /* Silently truncate the range, if necessary.  */
+  found = btrace_find_insn_by_number (&end, btinfo, high);
+  if (found == 0)
+    btrace_insn_end (&end, btinfo);
 
-  btinfo->insn_iterator.begin = begin;
-  btinfo->insn_iterator.end = end;
+  btrace_insn_history (uiout, &begin, &end, flags);
+  btrace_set_insn_history (btinfo, &begin, &end);
 
   do_cleanups (uiout_cleanup);
 }
@@ -412,23 +422,27 @@ record_btrace_insn_history_from (ULONGEST from, int size, int flags)
 /* Print the instruction number range for a function call history line.  */
 
 static void
-btrace_func_history_insn_range (struct ui_out *uiout, struct btrace_func *bfun)
+btrace_call_history_insn_range (struct ui_out *uiout,
+				const struct btrace_function *bfun)
 {
-  ui_out_field_uint (uiout, "insn begin", bfun->ibegin);
+  unsigned int begin, end;
 
-  if (bfun->ibegin == bfun->iend)
-    return;
+  begin = bfun->insn_offset;
+  end = begin + VEC_length (btrace_insn_s, bfun->insn);
 
+  ui_out_field_uint (uiout, "insn begin", begin);
   ui_out_text (uiout, "-");
-  ui_out_field_uint (uiout, "insn end", bfun->iend);
+  ui_out_field_uint (uiout, "insn end", end);
 }
 
 /* Print the source line information for a function call history line.  */
 
 static void
-btrace_func_history_src_line (struct ui_out *uiout, struct btrace_func *bfun)
+btrace_call_history_src_line (struct ui_out *uiout,
+			      const struct btrace_function *bfun)
 {
   struct symbol *sym;
+  int begin, end;
 
   sym = bfun->sym;
   if (sym == NULL)
@@ -437,54 +451,66 @@ btrace_func_history_src_line (struct ui_out *uiout, struct btrace_func *bfun)
   ui_out_field_string (uiout, "file",
 		       symtab_to_filename_for_display (sym->symtab));
 
-  if (bfun->lend == 0)
+  begin = bfun->lbegin;
+  end = bfun->lend;
+
+  if (end < begin)
     return;
 
   ui_out_text (uiout, ":");
-  ui_out_field_int (uiout, "min line", bfun->lbegin);
+  ui_out_field_int (uiout, "min line", begin);
 
-  if (bfun->lend == bfun->lbegin)
+  if (end == begin)
     return;
 
   ui_out_text (uiout, "-");
-  ui_out_field_int (uiout, "max line", bfun->lend);
+  ui_out_field_int (uiout, "max line", end);
 }
 
 /* Disassemble a section of the recorded function trace.  */
 
 static void
-btrace_func_history (struct btrace_thread_info *btinfo, struct ui_out *uiout,
-		     unsigned int begin, unsigned int end,
+btrace_call_history (struct ui_out *uiout,
+		     const struct btrace_call_iterator *begin,
+		     const struct btrace_call_iterator *end,
 		     enum record_print_flag flags)
 {
-  struct btrace_func *bfun;
-  unsigned int idx;
+  struct btrace_call_iterator it;
 
-  DEBUG ("ftrace (0x%x): [%u; %u[", flags, begin, end);
+  DEBUG ("ftrace (0x%x): [%u; %u)", flags, btrace_call_number (begin),
+	 btrace_call_number (end));
 
-  for (idx = begin; VEC_iterate (btrace_func_s, btinfo->ftrace, idx, bfun)
-	 && idx < end; ++idx)
+  for (it = *begin; btrace_call_cmp (&it, end) < 0; btrace_call_next (&it, 1))
     {
+      const struct btrace_function *bfun;
+      struct minimal_symbol *msym;
+      struct symbol *sym;
+
+      bfun = btrace_call_get (&it);
+      msym = bfun->msym;
+      sym = bfun->sym;
+
       /* Print the function index.  */
-      ui_out_field_uint (uiout, "index", idx);
+      ui_out_field_uint (uiout, "index", bfun->number);
       ui_out_text (uiout, "\t");
 
       if ((flags & RECORD_PRINT_INSN_RANGE) != 0)
 	{
-	  btrace_func_history_insn_range (uiout, bfun);
+	  btrace_call_history_insn_range (uiout, bfun);
 	  ui_out_text (uiout, "\t");
 	}
 
       if ((flags & RECORD_PRINT_SRC_LINE) != 0)
 	{
-	  btrace_func_history_src_line (uiout, bfun);
+	  btrace_call_history_src_line (uiout, bfun);
 	  ui_out_text (uiout, "\t");
 	}
 
-      if (bfun->sym != NULL)
-	ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (bfun->sym));
-      else if (bfun->msym != NULL)
-	ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (bfun->msym));
+      if (sym != NULL)
+	ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (sym));
+      else if (msym != NULL)
+	ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (msym));
+
       ui_out_text (uiout, "\n");
     }
 }
@@ -495,72 +521,62 @@ static void
 record_btrace_call_history (int size, int flags)
 {
   struct btrace_thread_info *btinfo;
+  struct btrace_call_history *history;
+  struct btrace_call_iterator begin, end;
   struct cleanup *uiout_cleanup;
   struct ui_out *uiout;
-  unsigned int context, last, begin, end;
+  unsigned int context, covered;
 
   uiout = current_uiout;
   uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout,
 						       "insn history");
-  btinfo = require_btrace ();
-  last = VEC_length (btrace_func_s, btinfo->ftrace);
-
   context = abs (size);
-  begin = btinfo->func_iterator.begin;
-  end = btinfo->func_iterator.end;
-
-  DEBUG ("func-history (0x%x): %d, prev: [%u; %u[", flags, size, begin, end);
-
   if (context == 0)
     error (_("Bad record function-call-history-size."));
 
-  /* We start at the end.  */
-  if (end < begin)
-    {
-      /* Truncate the context, if necessary.  */
-      context = min (context, last);
-
-      end = last;
-      begin = end - context;
-    }
-  else if (size < 0)
+  btinfo = require_btrace ();
+  history = btinfo->call_history;
+  if (history == NULL)
     {
-      if (begin == 0)
-	{
-	  printf_unfiltered (_("At the start of the branch trace record.\n"));
-
-	  btinfo->func_iterator.end = 0;
-	  return;
-	}
+      /* No matter the direction, we start with the tail of the trace.  */
+      btrace_call_end (&begin, btinfo);
+      end = begin;
 
-      /* Truncate the context, if necessary.  */
-      context = min (context, begin);
+      DEBUG ("call-history (0x%x): %d", flags, size);
 
-      end = begin;
-      begin -= context;
+      covered = btrace_call_prev (&begin, context);
     }
   else
     {
-      if (end == last)
-	{
-	  printf_unfiltered (_("At the end of the branch trace record.\n"));
+      begin = history->begin;
+      end = history->end;
 
-	  btinfo->func_iterator.begin = last;
-	  return;
-	}
+      DEBUG ("call-history (0x%x): %d, prev: [%u; %u)", flags, size,
+	     btrace_call_number (&begin), btrace_call_number (&end));
 
-      /* Truncate the context, if necessary.  */
-      context = min (context, last - end);
-
-      begin = end;
-      end += context;
+      if (size < 0)
+	{
+	  end = begin;
+	  covered = btrace_call_prev (&begin, context);
+	}
+      else
+	{
+	  begin = end;
+	  covered = btrace_call_next (&end, context);
+	}
     }
 
-  btrace_func_history (btinfo, uiout, begin, end, flags);
-
-  btinfo->func_iterator.begin = begin;
-  btinfo->func_iterator.end = end;
+  if (covered > 0)
+    btrace_call_history (uiout, &begin, &end, flags);
+  else
+    {
+      if (size < 0)
+	printf_unfiltered (_("At the start of the branch trace record.\n"));
+      else
+	printf_unfiltered (_("At the end of the branch trace record.\n"));
+    }
 
+  btrace_set_call_history (btinfo, &begin, &end);
   do_cleanups (uiout_cleanup);
 }
 
@@ -570,39 +586,41 @@ static void
 record_btrace_call_history_range (ULONGEST from, ULONGEST to, int flags)
 {
   struct btrace_thread_info *btinfo;
+  struct btrace_call_history *history;
+  struct btrace_call_iterator begin, end;
   struct cleanup *uiout_cleanup;
   struct ui_out *uiout;
-  unsigned int last, begin, end;
+  unsigned int low, high;
+  int found;
 
   uiout = current_uiout;
   uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout,
 						       "func history");
-  btinfo = require_btrace ();
-  last = VEC_length (btrace_func_s, btinfo->ftrace);
+  low = from;
+  high = to;
 
-  begin = (unsigned int) from;
-  end = (unsigned int) to;
-
-  DEBUG ("func-history (0x%x): [%u; %u[", flags, begin, end);
+  DEBUG ("call-history (0x%x): [%u; %u)", flags, low, high);
 
   /* Check for wrap-arounds.  */
-  if (begin != from || end != to)
+  if (low != from || high != to)
     error (_("Bad range."));
 
-  if (end <= begin)
+  if (high <= low)
     error (_("Bad range."));
 
-  if (last <= begin)
-    error (_("Range out of bounds."));
+  btinfo = require_btrace ();
 
-  /* Truncate the range, if necessary.  */
-  if (last < end)
-    end = last;
+  found = btrace_find_call_by_number (&begin, btinfo, low);
+  if (found == 0)
+    error (_("Range out of bounds."));
 
-  btrace_func_history (btinfo, uiout, begin, end, flags);
+  /* Silently truncate the range, if necessary.  */
+  found = btrace_find_call_by_number (&end, btinfo, high);
+  if (found == 0)
+    btrace_call_end (&end, btinfo);
 
-  btinfo->func_iterator.begin = begin;
-  btinfo->func_iterator.end = end;
+  btrace_call_history (uiout, &begin, &end, flags);
+  btrace_set_call_history (btinfo, &begin, &end);
 
   do_cleanups (uiout_cleanup);
 }
diff --git a/gdb/testsuite/gdb.btrace/function_call_history.exp b/gdb/testsuite/gdb.btrace/function_call_history.exp
index 97447e1..7658637 100644
--- a/gdb/testsuite/gdb.btrace/function_call_history.exp
+++ b/gdb/testsuite/gdb.btrace/function_call_history.exp
@@ -204,16 +204,18 @@ set bp_location [gdb_get_line_number "bp.2" $testfile.c]
 gdb_breakpoint $bp_location
 gdb_continue_to_breakpoint "cont to $bp_location" ".*$testfile.c:$bp_location.*"
 
-# at this point we expect to have main, fib, ..., fib, main, where fib occurs 8 times,
-# so we limit the output to only show the latest 10 function calls
-gdb_test_no_output "set record function-call-history-size 10"
-set message "show recursive function call history"
-gdb_test_multiple "record function-call-history" $message {
-    -re "13\tmain\r\n14\tfib\r\n15\tfib\r\n16\tfib\r\n17\tfib\r\n18\tfib\r\n19\tfib\r\n20\tfib\r\n21\tfib\r\n22	 main\r\n$gdb_prompt $" {
-        pass $message
-    }
-    -re "13\tinc\r\n14\tmain\r\n15\tinc\r\n16\tmain\r\n17\tinc\r\n18\tmain\r\n19\tinc\r\n20\tmain\r\n21\tfib\r\n22\tmain\r\n$gdb_prompt $" {
-        # recursive function calls appear only as 1 call
-        kfail "gdb/15240" $message
-    }
-}
+# at this point we expect to have main, fib, ..., fib, main, where fib occurs 9 times,
+# so we limit the output to only show the latest 11 function calls
+gdb_test_no_output "set record function-call-history-size 11"
+gdb_test "record function-call-history" "
+20\tmain\r
+21\tfib\r
+22\tfib\r
+23\tfib\r
+24\tfib\r
+25\tfib\r
+26\tfib\r
+27\tfib\r
+28\tfib\r
+29\tfib\r
+30\tmain" "show recursive function call history"
diff --git a/gdb/testsuite/gdb.btrace/instruction_history.exp b/gdb/testsuite/gdb.btrace/instruction_history.exp
index c1a61b7..377dca1 100644
--- a/gdb/testsuite/gdb.btrace/instruction_history.exp
+++ b/gdb/testsuite/gdb.btrace/instruction_history.exp
@@ -47,18 +47,18 @@ gdb_continue_to_breakpoint "cont to $bp_location" ".*$srcfile2:$bp_location.*"
 # it is necessary to count the number of lines that are
 # shown by the "record instruction-history" command.
 
+set traced {}
 set testname "determine number of recorded instructions"
 gdb_test_multiple "info record" $testname {
     -re "Active record target: record-btrace\r\nRecorded \(\[0-9\]*\) instructions in \(\[0-9\]*\) functions for thread 1 .*\\.\r\n$gdb_prompt $" {
         set traced $expect_out(1,string)
-        set traced_functions $expect_out(2,string)
         pass $testname
     }
 }
 
-# we have exactly 7 instructions here
-set message "exactly 7 instructions"
-if { $traced != 7 } {
+# we have exactly 6 instructions here
+set message "exactly 6 instructions"
+if { $traced != 6 } {
     fail $message
 } else {
     pass $message
@@ -144,6 +144,8 @@ if { $lines != $history_size } {
     pass $message
 }
 
+set history_size 2
+gdb_test_no_output "set record instruction-history-size $history_size"
 set message "browse history forward middle"
 set lines [test_lines_length "record instruction-history +" $message]
 if { $lines != $history_size } {
@@ -165,6 +167,8 @@ gdb_test "record instruction-history" "At the end of the branch trace record\\."
 # make sure we cannot move further
 gdb_test "record instruction-history" "At the end of the branch trace record\\." "browse history forward beyond 2"
 
+set history_size 3
+gdb_test_no_output "set record instruction-history-size $history_size"
 set message "browse history backward last"
 set lines [test_lines_length "record instruction-history -" $message]
 if { $lines != $history_size } {
@@ -173,6 +177,8 @@ if { $lines != $history_size } {
     pass $message
 }
 
+set history_size 2
+gdb_test_no_output "set record instruction-history-size $history_size"
 set message "browse history backward middle"
 set lines [test_lines_length "record instruction-history -" $message]
 if { $lines != $history_size } {
-- 
1.8.3.1

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

* [patch v8 20/24] record-btrace: add record goto target methods
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (10 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 21/24] record-btrace: extend unwinder Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-12  9:16 ` [patch v8 12/24] btrace: add replay position to btrace thread info Markus Metzger
                   ` (12 subsequent siblings)
  24 siblings, 0 replies; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

Reviewed-by: Eli Zaretskii

2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

	* record-btrace.c (record_btrace_set_replay,
	record_btrace_goto_begin, record_btrace_goto_end,
	record_btrace_goto): New.
	(init_record_btrace_ops): Initialize them.
	* NEWS: Announce it.

testsuite/
	* gdb.btrace/Makefile.in (EXECUTABLES): Add record_goto.
	* gdb.btrace/record_goto.c: New.
	* gdb.btrace/record_goto.exp: New.
	* gdb.btrace/x86-record_goto.S: New.


---
 gdb/NEWS                                   |   2 +
 gdb/record-btrace.c                        | 124 +++++++++-
 gdb/testsuite/gdb.btrace/Makefile.in       |   2 +-
 gdb/testsuite/gdb.btrace/record_goto.c     |  51 +++++
 gdb/testsuite/gdb.btrace/record_goto.exp   | 163 +++++++++++++
 gdb/testsuite/gdb.btrace/x86-record_goto.S | 355 +++++++++++++++++++++++++++++
 6 files changed, 692 insertions(+), 5 deletions(-)
 create mode 100644 gdb/testsuite/gdb.btrace/record_goto.c
 create mode 100644 gdb/testsuite/gdb.btrace/record_goto.exp
 create mode 100644 gdb/testsuite/gdb.btrace/x86-record_goto.S

diff --git a/gdb/NEWS b/gdb/NEWS
index 39404cc..b932c06 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -91,6 +91,8 @@ info exceptions REGEXP
   debugged.  If provided, only the exceptions whose names match REGEXP
   are listed.
 
+* The btrace record target now supports the 'record goto' command.
+
 * New options
 
 set debug symfile off|on
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index e8cd33a..d78c7a0 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -55,13 +55,13 @@ static struct observer *record_btrace_thread_observer;
 
 
 /* Update the branch trace for the current thread and return a pointer to its
-   branch trace information struct.
+   thread_info.
 
    Throws an error if there is no thread or no trace.  This function never
    returns NULL.  */
 
-static struct btrace_thread_info *
-require_btrace (void)
+static struct thread_info *
+require_btrace_thread (void)
 {
   struct thread_info *tp;
   struct btrace_thread_info *btinfo;
@@ -79,7 +79,23 @@ require_btrace (void)
   if (btinfo->begin == NULL)
     error (_("No trace."));
 
-  return btinfo;
+  return tp;
+}
+
+/* Update the branch trace for the current thread and return a pointer to its
+   branch trace information struct.
+
+   Throws an error if there is no thread or no trace.  This function never
+   returns NULL.  */
+
+static struct btrace_thread_info *
+require_btrace (void)
+{
+  struct thread_info *tp;
+
+  tp = require_btrace_thread ();
+
+  return &tp->btrace;
 }
 
 /* Enable branch tracing for one thread.  Warn on errors.  */
@@ -1010,6 +1026,103 @@ record_btrace_find_new_threads (struct target_ops *ops)
       }
 }
 
+/* Set the replay branch trace instruction iterator.  If IT is NULL, replay
+   is stopped.  */
+
+static void
+record_btrace_set_replay (struct thread_info *tp,
+			  const struct btrace_insn_iterator *it)
+{
+  struct btrace_thread_info *btinfo;
+
+  btinfo = &tp->btrace;
+
+  if (it == NULL || it->function == NULL)
+    {
+      if (btinfo->replay == NULL)
+	return;
+
+      xfree (btinfo->replay);
+      btinfo->replay = NULL;
+    }
+  else
+    {
+      if (btinfo->replay == NULL)
+	btinfo->replay = xmalloc (sizeof (*btinfo->replay));
+      else if (btrace_insn_cmp (btinfo->replay, it) == 0)
+	return;
+
+      *btinfo->replay = *it;
+    }
+
+  /* Clear the function call and instruction histories so we start anew
+     from the new replay position.  */
+  xfree (btinfo->insn_history);
+  xfree (btinfo->call_history);
+
+  btinfo->insn_history = NULL;
+  btinfo->call_history = NULL;
+
+  registers_changed_ptid (tp->ptid);
+}
+
+/* The to_goto_record_begin method of target record-btrace.  */
+
+static void
+record_btrace_goto_begin (void)
+{
+  struct thread_info *tp;
+  struct btrace_insn_iterator begin;
+
+  tp = require_btrace_thread ();
+
+  btrace_insn_begin (&begin, &tp->btrace);
+  record_btrace_set_replay (tp, &begin);
+
+  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
+}
+
+/* The to_goto_record_end method of target record-btrace.  */
+
+static void
+record_btrace_goto_end (void)
+{
+  struct thread_info *tp;
+
+  tp = require_btrace_thread ();
+
+  record_btrace_set_replay (tp, NULL);
+
+  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
+}
+
+/* The to_goto_record method of target record-btrace.  */
+
+static void
+record_btrace_goto (ULONGEST insn)
+{
+  struct thread_info *tp;
+  struct btrace_insn_iterator it;
+  unsigned int number;
+  int found;
+
+  number = insn;
+
+  /* Check for wrap-arounds.  */
+  if (number != insn)
+    error (_("Instruction number out of range."));
+
+  tp = require_btrace_thread ();
+
+  found = btrace_find_insn_by_number (&it, &tp->btrace, number);
+  if (found == 0)
+    error (_("No such instruction."));
+
+  record_btrace_set_replay (tp, &it);
+
+  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
+}
+
 /* Initialize the record-btrace target ops.  */
 
 static void
@@ -1045,6 +1158,9 @@ init_record_btrace_ops (void)
   ops->to_resume = record_btrace_resume;
   ops->to_wait = record_btrace_wait;
   ops->to_find_new_threads = record_btrace_find_new_threads;
+  ops->to_goto_record_begin = record_btrace_goto_begin;
+  ops->to_goto_record_end = record_btrace_goto_end;
+  ops->to_goto_record = record_btrace_goto;
   ops->to_stratum = record_stratum;
   ops->to_magic = OPS_MAGIC;
 }
diff --git a/gdb/testsuite/gdb.btrace/Makefile.in b/gdb/testsuite/gdb.btrace/Makefile.in
index 5c70700..aa2820a 100644
--- a/gdb/testsuite/gdb.btrace/Makefile.in
+++ b/gdb/testsuite/gdb.btrace/Makefile.in
@@ -2,7 +2,7 @@ VPATH = @srcdir@
 srcdir = @srcdir@
 
 EXECUTABLES   = enable function_call_history instruction_history tailcall \
-  exception
+  exception record_goto
 
 MISCELLANEOUS =
 
diff --git a/gdb/testsuite/gdb.btrace/record_goto.c b/gdb/testsuite/gdb.btrace/record_goto.c
new file mode 100644
index 0000000..1250708
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/record_goto.c
@@ -0,0 +1,51 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2013 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <markus.t.metzger@intel.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+void
+fun1 (void)
+{
+}
+
+void
+fun2 (void)
+{
+  fun1 ();
+}
+
+void
+fun3 (void)
+{
+  fun1 ();
+  fun2 ();
+}
+
+void
+fun4 (void)
+{
+  fun1 ();
+  fun2 ();
+  fun3 ();
+}
+
+int
+main (void)
+{
+  fun4 ();
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.btrace/record_goto.exp b/gdb/testsuite/gdb.btrace/record_goto.exp
new file mode 100644
index 0000000..ce27392
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/record_goto.exp
@@ -0,0 +1,163 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile x86-record_goto.S
+
+set opts {}
+if [info exists COMPILE] {
+    # make check RUNTESTFLAGS="gdb.btrace/record_goto.exp COMPILE=1"
+    standard_testfile record_goto.c
+    lappend opts debug
+} elseif { ![istarget x86_64-*-* ] || ![is_lp64_target] } {
+    verbose "Skipping ${testfile}."
+    return
+}
+
+if [prepare_for_testing record_goto.exp $testfile $srcfile $opts] {
+    return -1
+}
+if ![runto_main] {
+    return -1
+}
+
+# we want a small context sizes to simplify the test
+gdb_test_no_output "set record instruction-history-size 3"
+gdb_test_no_output "set record function-call-history-size 3"
+
+# trace the call to the test function
+gdb_test_no_output "record btrace"
+gdb_test "next"
+
+# start by listing all functions
+gdb_test "record function-call-history /ci 1, +20" "
+1\t  fun4\tinst 1,3\r
+2\t    fun1\tinst 4,7\r
+3\t  fun4\tinst 8,8\r
+4\t    fun2\tinst 9,11\r
+5\t      fun1\tinst 12,15\r
+6\t    fun2\tinst 16,17\r
+7\t  fun4\tinst 18,18\r
+8\t    fun3\tinst 19,21\r
+9\t      fun1\tinst 22,25\r
+10\t    fun3\tinst 26,26\r
+11\t      fun2\tinst 27,29\r
+12\t        fun1\tinst 30,33\r
+13\t      fun2\tinst 34,35\r
+14\t    fun3\tinst 36,37\r
+15\t  fun4\tinst 38,39\r" "record_goto - list all functions"
+
+# let's see if we can go back in history
+gdb_test "record goto 18" "
+.*fun4 \\(\\) at record_goto.c:43.*" "record_goto - goto 18"
+
+# the function call history should start at the new location
+gdb_test "record function-call-history /ci" "
+7\t  fun4\tinst 18,18\r
+8\t    fun3\tinst 19,21\r
+9\t      fun1\tinst 22,25\r" "record_goto - function-call-history from 18 forwards"
+
+# the instruciton history should start at the new location
+gdb_test "record instruction-history" "
+18.*\r
+19.*\r
+20.*\r" "record_goto - instruciton-history from 18 forwards"
+
+# let's go to another place in the history
+gdb_test "record goto 26" "
+.*fun3 \\(\\) at record_goto.c:35.*" "record_goto - goto 26"
+
+# the function call history should start at the new location
+gdb_test "record function-call-history /ci -" "
+8\t    fun3\tinst 19,21\r
+9\t      fun1\tinst 22,25\r
+10\t    fun3\tinst 26,26\r" "record_goto - function-call-history from 26 backwards"
+
+# the instruciton history should start at the new location
+gdb_test "record instruction-history -" "
+24.*\r
+25.*\r
+26.*\r" "record_goto - instruciton-history from 26 backwards"
+
+# test that we can go to the begin of the trace
+gdb_test "record goto begin" "
+.*fun4 \\(\\) at record_goto.c:40.*" "record_goto - goto begin"
+
+# check that we're filling up the context correctly
+gdb_test "record function-call-history /ci -" "
+1\t  fun4\tinst 1,3\r
+2\t    fun1\tinst 4,7\r
+3\t  fun4\tinst 8,8\r" "record_goto - function-call-history from begin backwards"
+
+# check that we're filling up the context correctly
+gdb_test "record instruction-history -" "
+1.*\r
+2.*\r
+3.*\r" "record_goto - instruciton-history from begin backwards"
+
+# we should get the exact same history from the first instruction
+gdb_test "record goto 2" "
+.*fun4 \\(\\) at record_goto.c:40.*" "record_goto - goto 2"
+
+# check that we're filling up the context correctly
+gdb_test "record function-call-history /ci -" "
+1\t  fun4\tinst 1,3\r
+2\t    fun1\tinst 4,7\r
+3\t  fun4\tinst 8,8\r" "record_goto - function-call-history from 2 backwards"
+
+# check that we're filling up the context correctly
+gdb_test "record instruction-history -" "
+1.*\r
+2.*\r
+3.*\r" "record_goto - instruciton-history from 2 backwards"
+
+# check that we can go to the end of the trace
+gdb_test "record goto end" "
+.*main \\(\\) at record_goto.c:50.*" "record_goto - goto end"
+
+# check that we're filling up the context correctly
+gdb_test "record function-call-history /ci" "
+13\t      fun2\tinst 34,35\r
+14\t    fun3\tinst 36,37\r
+15\t  fun4\tinst 38,39\r" "record_goto - function-call-history from end forwards"
+
+# check that we're filling up the context correctly
+gdb_test "record instruction-history" "
+37.*\r
+38.*\r
+39.*\r" "record_goto - instruciton-history from end forwards"
+
+# we should get the exact same history from the second to last instruction
+gdb_test "record goto 38" "
+.*fun4 \\(\\) at record_goto.c:44.*" "record_goto - goto 38"
+
+# check that we're filling up the context correctly
+gdb_test "record function-call-history /ci" "
+13\t      fun2\tinst 34,35\r
+14\t    fun3\tinst 36,37\r
+15\t  fun4\tinst 38,39\r" "record_goto - function-call-history from 38 forwards"
+
+# check that we're filling up the context correctly
+gdb_test "record instruction-history" "
+37.*\r
+38.*\r
+39.*\r" "record_goto - instruciton-history from 38 forwards"
diff --git a/gdb/testsuite/gdb.btrace/x86-record_goto.S b/gdb/testsuite/gdb.btrace/x86-record_goto.S
new file mode 100644
index 0000000..08d1ce7
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/x86-record_goto.S
@@ -0,0 +1,355 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2013 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <markus.t.metzger@intel.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+   This file has been generated using:
+   gcc -S -dA -g record_goto.c -o x86-record_goto.S  */
+
+	.file	"record_goto.c"
+	.section	.debug_abbrev,"",@progbits
+.Ldebug_abbrev0:
+	.section	.debug_info,"",@progbits
+.Ldebug_info0:
+	.section	.debug_line,"",@progbits
+.Ldebug_line0:
+	.text
+.Ltext0:
+.globl fun1
+	.type	fun1, @function
+fun1:
+.LFB0:
+	.file 1 "record_goto.c"
+	# record_goto.c:22
+	.loc 1 22 0
+	.cfi_startproc
+	# basic block 2
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	movq	%rsp, %rbp
+	.cfi_offset 6, -16
+	.cfi_def_cfa_register 6
+	# record_goto.c:23
+	.loc 1 23 0
+	leave
+	.cfi_def_cfa 7, 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	fun1, .-fun1
+.globl fun2
+	.type	fun2, @function
+fun2:
+.LFB1:
+	# record_goto.c:27
+	.loc 1 27 0
+	.cfi_startproc
+	# basic block 2
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	movq	%rsp, %rbp
+	.cfi_offset 6, -16
+	.cfi_def_cfa_register 6
+	# record_goto.c:28
+	.loc 1 28 0
+	call	fun1
+	# record_goto.c:29
+	.loc 1 29 0
+	leave
+	.cfi_def_cfa 7, 8
+	ret
+	.cfi_endproc
+.LFE1:
+	.size	fun2, .-fun2
+.globl fun3
+	.type	fun3, @function
+fun3:
+.LFB2:
+	# record_goto.c:33
+	.loc 1 33 0
+	.cfi_startproc
+	# basic block 2
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	movq	%rsp, %rbp
+	.cfi_offset 6, -16
+	.cfi_def_cfa_register 6
+	# record_goto.c:34
+	.loc 1 34 0
+	call	fun1
+	# record_goto.c:35
+	.loc 1 35 0
+	call	fun2
+	# record_goto.c:36
+	.loc 1 36 0
+	leave
+	.cfi_def_cfa 7, 8
+	ret
+	.cfi_endproc
+.LFE2:
+	.size	fun3, .-fun3
+.globl fun4
+	.type	fun4, @function
+fun4:
+.LFB3:
+	# record_goto.c:40
+	.loc 1 40 0
+	.cfi_startproc
+	# basic block 2
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	movq	%rsp, %rbp
+	.cfi_offset 6, -16
+	.cfi_def_cfa_register 6
+	# record_goto.c:41
+	.loc 1 41 0
+	call	fun1
+	# record_goto.c:42
+	.loc 1 42 0
+	call	fun2
+	# record_goto.c:43
+	.loc 1 43 0
+	call	fun3
+	# record_goto.c:44
+	.loc 1 44 0
+	leave
+	.cfi_def_cfa 7, 8
+	ret
+	.cfi_endproc
+.LFE3:
+	.size	fun4, .-fun4
+.globl main
+	.type	main, @function
+main:
+.LFB4:
+	# record_goto.c:48
+	.loc 1 48 0
+	.cfi_startproc
+	# basic block 2
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	movq	%rsp, %rbp
+	.cfi_offset 6, -16
+	.cfi_def_cfa_register 6
+	# record_goto.c:49
+	.loc 1 49 0
+	call	fun4
+	# record_goto.c:50
+	.loc 1 50 0
+	movl	$0, %eax
+	# record_goto.c:51
+	.loc 1 51 0
+	leave
+	.cfi_def_cfa 7, 8
+	ret
+	.cfi_endproc
+.LFE4:
+	.size	main, .-main
+.Letext0:
+	.section	.debug_info
+	.long	0xbc	# Length of Compilation Unit Info
+	.value	0x3	# DWARF version number
+	.long	.Ldebug_abbrev0	# Offset Into Abbrev. Section
+	.byte	0x8	# Pointer Size (in bytes)
+	.uleb128 0x1	# (DIE (0xb) DW_TAG_compile_unit)
+	.long	.LASF4	# DW_AT_producer: "GNU C 4.4.4 20100726 (Red Hat 4.4.4-13)"
+	.byte	0x1	# DW_AT_language
+	.long	.LASF5	# DW_AT_name: "record_goto.c"
+	.long	.LASF6	# DW_AT_comp_dir: ""
+	.quad	.Ltext0	# DW_AT_low_pc
+	.quad	.Letext0	# DW_AT_high_pc
+	.long	.Ldebug_line0	# DW_AT_stmt_list
+	.uleb128 0x2	# (DIE (0x2d) DW_TAG_subprogram)
+	.byte	0x1	# DW_AT_external
+	.long	.LASF0	# DW_AT_name: "fun1"
+	.byte	0x1	# DW_AT_decl_file (record_goto.c)
+	.byte	0x15	# DW_AT_decl_line
+	.byte	0x1	# DW_AT_prototyped
+	.quad	.LFB0	# DW_AT_low_pc
+	.quad	.LFE0	# DW_AT_high_pc
+	.byte	0x1	# DW_AT_frame_base
+	.byte	0x9c	# DW_OP_call_frame_cfa
+	.uleb128 0x2	# (DIE (0x48) DW_TAG_subprogram)
+	.byte	0x1	# DW_AT_external
+	.long	.LASF1	# DW_AT_name: "fun2"
+	.byte	0x1	# DW_AT_decl_file (record_goto.c)
+	.byte	0x1a	# DW_AT_decl_line
+	.byte	0x1	# DW_AT_prototyped
+	.quad	.LFB1	# DW_AT_low_pc
+	.quad	.LFE1	# DW_AT_high_pc
+	.byte	0x1	# DW_AT_frame_base
+	.byte	0x9c	# DW_OP_call_frame_cfa
+	.uleb128 0x2	# (DIE (0x63) DW_TAG_subprogram)
+	.byte	0x1	# DW_AT_external
+	.long	.LASF2	# DW_AT_name: "fun3"
+	.byte	0x1	# DW_AT_decl_file (record_goto.c)
+	.byte	0x20	# DW_AT_decl_line
+	.byte	0x1	# DW_AT_prototyped
+	.quad	.LFB2	# DW_AT_low_pc
+	.quad	.LFE2	# DW_AT_high_pc
+	.byte	0x1	# DW_AT_frame_base
+	.byte	0x9c	# DW_OP_call_frame_cfa
+	.uleb128 0x2	# (DIE (0x7e) DW_TAG_subprogram)
+	.byte	0x1	# DW_AT_external
+	.long	.LASF3	# DW_AT_name: "fun4"
+	.byte	0x1	# DW_AT_decl_file (record_goto.c)
+	.byte	0x27	# DW_AT_decl_line
+	.byte	0x1	# DW_AT_prototyped
+	.quad	.LFB3	# DW_AT_low_pc
+	.quad	.LFE3	# DW_AT_high_pc
+	.byte	0x1	# DW_AT_frame_base
+	.byte	0x9c	# DW_OP_call_frame_cfa
+	.uleb128 0x3	# (DIE (0x99) DW_TAG_subprogram)
+	.byte	0x1	# DW_AT_external
+	.long	.LASF7	# DW_AT_name: "main"
+	.byte	0x1	# DW_AT_decl_file (record_goto.c)
+	.byte	0x2f	# DW_AT_decl_line
+	.byte	0x1	# DW_AT_prototyped
+	.long	0xb8	# DW_AT_type
+	.quad	.LFB4	# DW_AT_low_pc
+	.quad	.LFE4	# DW_AT_high_pc
+	.byte	0x1	# DW_AT_frame_base
+	.byte	0x9c	# DW_OP_call_frame_cfa
+	.uleb128 0x4	# (DIE (0xb8) DW_TAG_base_type)
+	.byte	0x4	# DW_AT_byte_size
+	.byte	0x5	# DW_AT_encoding
+	.ascii "int\0"	# DW_AT_name
+	.byte	0x0	# end of children of DIE 0xb
+	.section	.debug_abbrev
+	.uleb128 0x1	# (abbrev code)
+	.uleb128 0x11	# (TAG: DW_TAG_compile_unit)
+	.byte	0x1	# DW_children_yes
+	.uleb128 0x25	# (DW_AT_producer)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x13	# (DW_AT_language)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x1b	# (DW_AT_comp_dir)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x11	# (DW_AT_low_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x12	# (DW_AT_high_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x10	# (DW_AT_stmt_list)
+	.uleb128 0x6	# (DW_FORM_data4)
+	.byte	0x0
+	.byte	0x0
+	.uleb128 0x2	# (abbrev code)
+	.uleb128 0x2e	# (TAG: DW_TAG_subprogram)
+	.byte	0x0	# DW_children_no
+	.uleb128 0x3f	# (DW_AT_external)
+	.uleb128 0xc	# (DW_FORM_flag)
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x3a	# (DW_AT_decl_file)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3b	# (DW_AT_decl_line)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x27	# (DW_AT_prototyped)
+	.uleb128 0xc	# (DW_FORM_flag)
+	.uleb128 0x11	# (DW_AT_low_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x12	# (DW_AT_high_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x40	# (DW_AT_frame_base)
+	.uleb128 0xa	# (DW_FORM_block1)
+	.byte	0x0
+	.byte	0x0
+	.uleb128 0x3	# (abbrev code)
+	.uleb128 0x2e	# (TAG: DW_TAG_subprogram)
+	.byte	0x0	# DW_children_no
+	.uleb128 0x3f	# (DW_AT_external)
+	.uleb128 0xc	# (DW_FORM_flag)
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x3a	# (DW_AT_decl_file)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3b	# (DW_AT_decl_line)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x27	# (DW_AT_prototyped)
+	.uleb128 0xc	# (DW_FORM_flag)
+	.uleb128 0x49	# (DW_AT_type)
+	.uleb128 0x13	# (DW_FORM_ref4)
+	.uleb128 0x11	# (DW_AT_low_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x12	# (DW_AT_high_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x40	# (DW_AT_frame_base)
+	.uleb128 0xa	# (DW_FORM_block1)
+	.byte	0x0
+	.byte	0x0
+	.uleb128 0x4	# (abbrev code)
+	.uleb128 0x24	# (TAG: DW_TAG_base_type)
+	.byte	0x0	# DW_children_no
+	.uleb128 0xb	# (DW_AT_byte_size)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3e	# (DW_AT_encoding)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0x8	# (DW_FORM_string)
+	.byte	0x0
+	.byte	0x0
+	.byte	0x0
+	.section	.debug_pubnames,"",@progbits
+	.long	0x3b	# Length of Public Names Info
+	.value	0x2	# DWARF Version
+	.long	.Ldebug_info0	# Offset of Compilation Unit Info
+	.long	0xc0	# Compilation Unit Length
+	.long	0x2d	# DIE offset
+	.ascii "fun1\0"	# external name
+	.long	0x48	# DIE offset
+	.ascii "fun2\0"	# external name
+	.long	0x63	# DIE offset
+	.ascii "fun3\0"	# external name
+	.long	0x7e	# DIE offset
+	.ascii "fun4\0"	# external name
+	.long	0x99	# DIE offset
+	.ascii "main\0"	# external name
+	.long	0x0
+	.section	.debug_aranges,"",@progbits
+	.long	0x2c	# Length of Address Ranges Info
+	.value	0x2	# DWARF Version
+	.long	.Ldebug_info0	# Offset of Compilation Unit Info
+	.byte	0x8	# Size of Address
+	.byte	0x0	# Size of Segment Descriptor
+	.value	0x0	# Pad to 16 byte boundary
+	.value	0x0
+	.quad	.Ltext0	# Address
+	.quad	.Letext0-.Ltext0	# Length
+	.quad	0x0
+	.quad	0x0
+	.section	.debug_str,"MS",@progbits,1
+.LASF3:
+	.string	"fun4"
+.LASF5:
+	.string	"record_goto.c"
+.LASF4:
+	.string	"GNU C 4.4.4 20100726 (Red Hat 4.4.4-13)"
+.LASF7:
+	.string	"main"
+.LASF1:
+	.string	"fun2"
+.LASF0:
+	.string	"fun1"
+.LASF6:
+	.string	""
+.LASF2:
+	.string	"fun3"
+	.ident	"GCC: (GNU) 4.4.4 20100726 (Red Hat 4.4.4-13)"
+	.section	.note.GNU-stack,"",@progbits
-- 
1.8.3.1

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

* [patch v8 04/24] frame:  add frame_is_tailcall function
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (20 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 19/24] record-btrace: provide target_find_new_threads method Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-12  9:16 ` [patch v8 11/24] record-btrace: make ranges include begin and end Markus Metzger
                   ` (2 subsequent siblings)
  24 siblings, 0 replies; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

Currently, gdb is looking at the frame type when trying to identify
tailcall frames.  This requires touching several places when adding
a new tailcall frame type.

Add a predicate for tailcall frames.

2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

	* frame.h (frame_is_tailcall): New.
	* frame.c (frame_is_tailcall): New.
	(skip_artificial_frames, frame_pop, get_frame_address_in_block):
	Call frame_is_tailcall.
	* infcmd.c (construct_inferior_arguments): Call frame_is_tailcall.
	* stack.h (frame_info): Call frame_is_tailcall.
	* dwarf2-frame-tailcall.c (frame_is_tailcall): Rename to ..
	(frame_is_dwarf2_tailcall): ... this.
	(cache_find): Update.


---
 gdb/dwarf2-frame-tailcall.c |  4 ++--
 gdb/frame.c                 | 23 +++++++++++++++++------
 gdb/frame.h                 |  7 ++++++-
 gdb/infcmd.c                |  6 +++---
 gdb/stack.c                 |  2 +-
 5 files changed, 29 insertions(+), 13 deletions(-)

diff --git a/gdb/dwarf2-frame-tailcall.c b/gdb/dwarf2-frame-tailcall.c
index b82a051..35de19d 100644
--- a/gdb/dwarf2-frame-tailcall.c
+++ b/gdb/dwarf2-frame-tailcall.c
@@ -140,7 +140,7 @@ cache_unref (struct tailcall_cache *cache)
    return 0.  */
 
 static int
-frame_is_tailcall (struct frame_info *fi)
+frame_is_dwarf2_tailcall (struct frame_info *fi)
 {
   return frame_unwinder_is (fi, &dwarf2_tailcall_frame_unwind);
 }
@@ -154,7 +154,7 @@ cache_find (struct frame_info *fi)
   struct tailcall_cache *cache;
   void **slot;
 
-  while (frame_is_tailcall (fi))
+  while (frame_is_dwarf2_tailcall (fi))
     {
       fi = get_next_frame (fi);
       gdb_assert (fi != NULL);
diff --git a/gdb/frame.c b/gdb/frame.c
index 6a8b5ae..ddd5e70 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -415,7 +415,7 @@ static struct frame_info *
 skip_artificial_frames (struct frame_info *frame)
 {
   while (get_frame_type (frame) == INLINE_FRAME
-	 || get_frame_type (frame) == TAILCALL_FRAME)
+	 || frame_is_tailcall (frame))
     frame = get_prev_frame (frame);
 
   return frame;
@@ -919,9 +919,9 @@ frame_pop (struct frame_info *this_frame)
   if (!prev_frame)
     error (_("Cannot pop the initial frame."));
 
-  /* Ignore TAILCALL_FRAME type frames, they were executed already before
-     entering THISFRAME.  */
-  while (get_frame_type (prev_frame) == TAILCALL_FRAME)
+  /* Ignore tailcall frames, they were executed already before entering
+     THISFRAME.  */
+  while (frame_is_tailcall (prev_frame))
     prev_frame = get_prev_frame (prev_frame);
 
   /* Make a copy of all the register values unwound from this frame.
@@ -2180,9 +2180,9 @@ get_frame_address_in_block (struct frame_info *this_frame)
     next_frame = next_frame->next;
 
   if ((get_frame_type (next_frame) == NORMAL_FRAME
-       || get_frame_type (next_frame) == TAILCALL_FRAME)
+       || frame_is_tailcall (next_frame))
       && (get_frame_type (this_frame) == NORMAL_FRAME
-	  || get_frame_type (this_frame) == TAILCALL_FRAME
+	  || frame_is_tailcall (this_frame)
 	  || get_frame_type (this_frame) == INLINE_FRAME))
     return pc - 1;
 
@@ -2330,6 +2330,17 @@ frame_unwinder_is (struct frame_info *fi, const struct frame_unwind *unwinder)
   return fi->unwind == unwinder;
 }
 
+/* See frame.h.  */
+
+int
+frame_is_tailcall (struct frame_info *fi)
+{
+  enum frame_type type;
+
+  type = get_frame_type (fi);
+  return type == TAILCALL_FRAME;
+}
+
 /* Level of the selected frame: 0 for innermost, 1 for its caller, ...
    or -1 for a NULL frame.  */
 
diff --git a/gdb/frame.h b/gdb/frame.h
index f8d5bc1..e4e6b25 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -181,7 +181,7 @@ extern int frame_id_p (struct frame_id l);
 
 /* Returns non-zero when L is a valid frame representing a frame made up by GDB
    without stack data representation in inferior, such as INLINE_FRAME or
-   TAILCALL_FRAME.  */
+   tailcall frames.  */
 extern int frame_id_artificial_p (struct frame_id l);
 
 /* Returns non-zero when L and R identify the same frame, or, if
@@ -765,4 +765,9 @@ extern struct frame_info *create_new_frame (CORE_ADDR base, CORE_ADDR pc);
 extern int frame_unwinder_is (struct frame_info *fi,
 			      const struct frame_unwind *unwinder);
 
+/* Return true if frame FI is a tailcall frame.  */
+
+extern int frame_is_tailcall (struct frame_info *fi);
+
+
 #endif /* !defined (FRAME_H)  */
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 19f720b..dd8d06a 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -1776,9 +1776,9 @@ finish_command (char *arg, int from_tty)
       return;
     }
 
-  /* Ignore TAILCALL_FRAME type frames, they were executed already before
-     entering THISFRAME.  */
-  while (get_frame_type (frame) == TAILCALL_FRAME)
+  /* Ignore tailcall frames, they were executed already before entering
+     THISFRAME.  */
+  while (frame_is_tailcall (frame))
     frame = get_prev_frame (frame);
 
   /* Find the function we will return from.  */
diff --git a/gdb/stack.c b/gdb/stack.c
index f45bb80..8007819 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -1529,7 +1529,7 @@ frame_info (char *addr_exp, int from_tty)
 	printf_filtered (_(" Outermost frame: %s\n"),
 			 frame_stop_reason_string (reason));
     }
-  else if (get_frame_type (fi) == TAILCALL_FRAME)
+  else if (frame_is_tailcall (fi))
     puts_filtered (" tail call frame");
   else if (get_frame_type (fi) == INLINE_FRAME)
     printf_filtered (" inlined into frame %d",
-- 
1.8.3.1

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

* [patch v8 18/24] record-btrace: add to_wait and to_resume target methods.
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (4 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 06/24] btrace: change branch trace data structure Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-12  9:16 ` [patch v8 10/24] record-btrace: optionally indent function call history Markus Metzger
                   ` (18 subsequent siblings)
  24 siblings, 0 replies; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

Add simple to_wait and to_resume target methods that prevent stepping when the
current replay position is not at the end of the execution log.

2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

	* record-btrace.c (record_btrace_resume): New.
	(record_btrace_wait): New.
	(init_record_btrace_ops): Initialize to_wait and to_resume.


---
 gdb/record-btrace.c | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index ec99ba2..f2aa4fa 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -953,6 +953,45 @@ static const struct frame_unwind record_btrace_frame_unwind =
   NULL,
   record_btrace_frame_sniffer
 };
+
+/* The to_resume method of target record-btrace.  */
+
+static void
+record_btrace_resume (struct target_ops *ops, ptid_t ptid, int step,
+		      enum gdb_signal signal)
+{
+  /* As long as we're not replaying, just forward the request.  */
+  if (!record_btrace_is_replaying ())
+    {
+      for (ops = ops->beneath; ops != NULL; ops = ops->beneath)
+	if (ops->to_resume != NULL)
+	  return ops->to_resume (ops, ptid, step, signal);
+
+      error (_("Cannot find target for stepping."));
+    }
+
+  error (_("You can't do this from here.  Do 'record goto end', first."));
+}
+
+/* The to_wait method of target record-btrace.  */
+
+static ptid_t
+record_btrace_wait (struct target_ops *ops, ptid_t ptid,
+		    struct target_waitstatus *status, int options)
+{
+  /* As long as we're not replaying, just forward the request.  */
+  if (!record_btrace_is_replaying ())
+    {
+      for (ops = ops->beneath; ops != NULL; ops = ops->beneath)
+	if (ops->to_wait != NULL)
+	  return ops->to_wait (ops, ptid, status, options);
+
+      error (_("Cannot find target for waiting."));
+    }
+
+  error (_("You can't do this from here.  Do 'record goto end', first."));
+}
+
 /* Initialize the record-btrace target ops.  */
 
 static void
@@ -985,6 +1024,8 @@ init_record_btrace_ops (void)
   ops->to_store_registers = record_btrace_store_registers;
   ops->to_prepare_to_store = record_btrace_prepare_to_store;
   ops->to_get_unwinder = &record_btrace_frame_unwind;
+  ops->to_resume = record_btrace_resume;
+  ops->to_wait = record_btrace_wait;
   ops->to_stratum = record_stratum;
   ops->to_magic = OPS_MAGIC;
 }
-- 
1.8.3.1

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

* [patch v8 24/24] record-btrace: add (reverse-)stepping support
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (12 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 12/24] btrace: add replay position to btrace thread info Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-12 16:36   ` Eli Zaretskii
  2013-12-13 19:22   ` Pedro Alves
  2013-12-12  9:16 ` [patch v8 13/24] target: add ops parameter to to_prepare_to_store method Markus Metzger
                   ` (10 subsequent siblings)
  24 siblings, 2 replies; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches, Eli Zaretskii

Non-stop mode is not working.  Do not allow record-btrace in non-stop mode.

CC: Eli Zaretskii  <eliz@gnu.org>

2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

	* btrace.h (btrace_thread_flag): New.
	(struct btrace_thread_info) <flags>: New.
	* record-btrace.c (record_btrace_resume_thread,
	record_btrace_find_thread_to_move, btrace_step_no_history,
	btrace_step_stopped, record_btrace_start_replaying,
	record_btrace_step_thread,
	record_btrace_find_resume_thread): New.
	(record_btrace_resume, record_btrace_wait): Extend.
	(record_btrace_can_execute_reverse): New.
	(record_btrace_open): Fail in non-stop mode.
	(record_btrace_set_replay): Split into this, ...
	(record_btrace_stop_replaying): ... this, ...
	(record_btrace_clear_histories): ... and this.
	(init_record_btrace_ops): Init to_can_execute_reverse.
	* NEWS: Announce it.

testsuite/
	* gdb.btrace/delta.exp: Check reverse stepi.
	* gdb.btrace/tailcall.exp: Update.  Add stepping tests.
	* gdb.btrace/finish.exp: New.
	* gdb.btrace/next.exp: New.
	* gdb.btrace/nexti.exp: New.
	* gdb.btrace/record_goto.c: Add comments.
	* gdb.btrace/step.exp: New.
	* gdb.btrace/stepi.exp: New.
	* gdb.btrace/multi-thread-step.c: New.
	* gdb.btrace/multi-thread-step.exp: New.
	* gdb.btrace/rn-dl-bind.c: New.
	* gdb.btrace/rn-dl-bind.exp: New.

doc/
	* gdb.texinfo: Document limited reverse/replay support
	for target record-btrace.


---
 gdb/NEWS                                       |   4 +
 gdb/btrace.h                                   |  22 ++
 gdb/doc/gdb.texinfo                            |   6 +-
 gdb/record-btrace.c                            | 407 +++++++++++++++++++++++--
 gdb/testsuite/gdb.btrace/delta.exp             |  13 +
 gdb/testsuite/gdb.btrace/finish.exp            |  70 +++++
 gdb/testsuite/gdb.btrace/multi-thread-step.c   |  53 ++++
 gdb/testsuite/gdb.btrace/multi-thread-step.exp |  99 ++++++
 gdb/testsuite/gdb.btrace/next.exp              |  89 ++++++
 gdb/testsuite/gdb.btrace/nexti.exp             |  89 ++++++
 gdb/testsuite/gdb.btrace/record_goto.c         |  36 +--
 gdb/testsuite/gdb.btrace/rn-dl-bind.c          |  37 +++
 gdb/testsuite/gdb.btrace/rn-dl-bind.exp        |  48 +++
 gdb/testsuite/gdb.btrace/step.exp              | 113 +++++++
 gdb/testsuite/gdb.btrace/stepi.exp             | 114 +++++++
 gdb/testsuite/gdb.btrace/tailcall.exp          |  24 ++
 16 files changed, 1183 insertions(+), 41 deletions(-)
 create mode 100644 gdb/testsuite/gdb.btrace/finish.exp
 create mode 100644 gdb/testsuite/gdb.btrace/multi-thread-step.c
 create mode 100644 gdb/testsuite/gdb.btrace/multi-thread-step.exp
 create mode 100644 gdb/testsuite/gdb.btrace/next.exp
 create mode 100644 gdb/testsuite/gdb.btrace/nexti.exp
 create mode 100644 gdb/testsuite/gdb.btrace/rn-dl-bind.c
 create mode 100644 gdb/testsuite/gdb.btrace/rn-dl-bind.exp
 create mode 100644 gdb/testsuite/gdb.btrace/step.exp
 create mode 100644 gdb/testsuite/gdb.btrace/stepi.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index f7927f3..fb379a6 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -95,6 +95,10 @@ info exceptions REGEXP
   For locations inside the execution trace, the back trace is computed
   based on the information stored in the execution trace.
 
+* The btrace record target supports limited reverse execution and replay.
+  The target does not record data and does therefore not allow reading
+  memory or registers.
+
 * New options
 
 set debug symfile off|on
diff --git a/gdb/btrace.h b/gdb/btrace.h
index 548ebba..5b52ec6 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -153,6 +153,25 @@ struct btrace_call_history
   struct btrace_call_iterator end;
 };
 
+/* Branch trace thread flags.  */
+enum btrace_thread_flag
+{
+  /* The thread is to be stepped forwards.  */
+  BTHR_STEP = (1 << 0),
+
+  /* The thread is to be stepped backwards.  */
+  BTHR_RSTEP = (1 << 1),
+
+  /* The thread is to be continued forwards.  */
+  BTHR_CONT = (1 << 2),
+
+  /* The thread is to be continued backwards.  */
+  BTHR_RCONT = (1 << 3),
+
+  /* The thread is to be moved.  */
+  BTHR_MOVE = (BTHR_STEP | BTHR_RSTEP | BTHR_CONT | BTHR_RCONT)
+};
+
 /* Branch trace information per thread.
 
    This represents the branch trace configuration as well as the entry point
@@ -182,6 +201,9 @@ struct btrace_thread_info
      becomes zero.  */
   int level;
 
+  /* A bit-vector of btrace_thread_flag.  */
+  enum btrace_thread_flag flags;
+
   /* The instruction history iterator.  */
   struct btrace_insn_history *insn_history;
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 2161715..d599471 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6255,8 +6255,10 @@ replay implementation.  This method allows replaying and reverse
 execution.
 
 @item btrace
-Hardware-supported instruction recording.  This method does not allow
-replaying and reverse execution.
+Hardware-supported instruction recording.  This method does not record
+data.  Further, the data is collected in a ring buffer so old data will
+be overwritten when the buffer is full.  It allows limited replay and
+reverse execution.
 
 This recording method may not be available on all processors.
 @end table
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 1e8cfc1..bc23f2d 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -167,6 +167,10 @@ record_btrace_open (char *args, int from_tty)
   if (!target_supports_btrace ())
     error (_("Target does not support branch tracing."));
 
+  if (non_stop)
+    error (_("Record btrace can't debug inferior in non-stop mode "
+	     "(non-stop)."));
+
   gdb_assert (record_btrace_thread_observer == NULL);
 
   disable_chain = make_cleanup (null_cleanup, NULL);
@@ -1233,14 +1237,147 @@ const struct frame_unwind record_btrace_tailcall_frame_unwind =
   record_btrace_frame_dealloc_cache
 };
 
+/* Indicate that TP should be resumed according to FLAG.  */
+
+static void
+record_btrace_resume_thread (struct thread_info *tp,
+			     enum btrace_thread_flag flag)
+{
+  struct btrace_thread_info *btinfo;
+
+  DEBUG ("resuming %d (%s): %u", tp->num, target_pid_to_str (tp->ptid), flag);
+
+  btinfo = &tp->btrace;
+
+  if ((btinfo->flags & BTHR_MOVE) != 0)
+    error (_("Thread already moving."));
+
+  /* Fetch the latest branch trace.  */
+  btrace_fetch (tp);
+
+  btinfo->flags |= flag;
+}
+
+/* Find the thread to resume given a PTID.  */
+
+static struct thread_info *
+record_btrace_find_resume_thread (ptid_t ptid)
+{
+  struct thread_info *tp;
+
+  /* When asked to resume everything, we pick the current thread.  */
+  if (ptid_equal (minus_one_ptid, ptid) || ptid_is_pid (ptid))
+    ptid = inferior_ptid;
+
+  return find_thread_ptid (ptid);
+}
+
+/* Stop replaying a thread.  */
+
+static struct btrace_insn_iterator *
+record_btrace_start_replaying (struct thread_info *tp)
+{
+  volatile struct gdb_exception except;
+  struct btrace_insn_iterator *replay;
+  struct btrace_thread_info *btinfo;
+  int executing;
+
+  btinfo = &tp->btrace;
+  replay = NULL;
+
+  /* We can't start replaying without trace.  */
+  if (btinfo->begin == NULL)
+    return NULL;
+
+  /* Clear the executing flag to allow changes to the current frame.  */
+  executing = is_executing (tp->ptid);
+  set_executing (tp->ptid, 0);
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      struct frame_info *frame;
+      struct frame_id frame_id;
+      int upd_step_frame_id, upd_step_stack_frame_id;
+
+      /* The current frame without replaying - computed via normal unwind.  */
+      frame = get_current_frame ();
+      frame_id = get_frame_id (frame);
+
+      /* Check if we need to update any stepping-related frame id's.  */
+      upd_step_frame_id = frame_id_eq (frame_id,
+				       tp->control.step_frame_id);
+      upd_step_stack_frame_id = frame_id_eq (frame_id,
+					     tp->control.step_stack_frame_id);
+
+      /* We start replaying at the end of the branch trace.  This corresponds
+	 to the current instruction.  */
+      replay = xmalloc (sizeof (*replay));
+      btrace_insn_end (replay, btinfo);
+
+      /* We're not replaying, yet.  */
+      gdb_assert (btinfo->replay == NULL);
+      btinfo->replay = replay;
+
+      /* Make sure we're not using any stale registers.  */
+      registers_changed_ptid (tp->ptid);
+
+      /* The current frame with replaying - computed via btrace unwind.  */
+      frame = get_current_frame ();
+      frame_id = get_frame_id (frame);
+
+      /* Replace stepping related frames where necessary.  */
+      if (upd_step_frame_id)
+	tp->control.step_frame_id = frame_id;
+      if (upd_step_stack_frame_id)
+	tp->control.step_stack_frame_id = frame_id;
+    }
+
+  /* Restore the previous execution state.  */
+  set_executing (tp->ptid, executing);
+
+  if (except.reason < 0)
+    throw_exception (except);
+
+  return replay;
+}
+
+/* Stop replaying a thread.  */
+
+static void
+record_btrace_stop_replaying (struct thread_info *tp)
+{
+  struct btrace_thread_info *btinfo;
+
+  btinfo = &tp->btrace;
+
+  xfree (btinfo->replay);
+  btinfo->replay = NULL;
+
+  /* Make sure we're not leaving any stale registers.  */
+  registers_changed_ptid (tp->ptid);
+}
+
 /* The to_resume method of target record-btrace.  */
 
 static void
 record_btrace_resume (struct target_ops *ops, ptid_t ptid, int step,
 		      enum gdb_signal signal)
 {
+  struct thread_info *tp, *other;
+  enum btrace_thread_flag flag;
+
+  DEBUG ("resume %s: %s", target_pid_to_str (ptid), step ? "step" : "cont");
+
+  tp = record_btrace_find_resume_thread (ptid);
+
+  /* Stop replaying other threads if the thread to resume is not replaying.  */
+  if (tp != NULL && !btrace_is_replaying (tp)
+      && execution_direction != EXEC_REVERSE)
+    ALL_THREADS (other)
+      record_btrace_stop_replaying (other);
+
   /* As long as we're not replaying, just forward the request.  */
-  if (!record_btrace_is_replaying ())
+  if (!record_btrace_is_replaying () && execution_direction != EXEC_REVERSE)
     {
       for (ops = ops->beneath; ops != NULL; ops = ops->beneath)
 	if (ops->to_resume != NULL)
@@ -1249,7 +1386,209 @@ record_btrace_resume (struct target_ops *ops, ptid_t ptid, int step,
       error (_("Cannot find target for stepping."));
     }
 
-  error (_("You can't do this from here.  Do 'record goto end', first."));
+  /* Compute the btrace thread flag for the requested move.  */
+  if (step == 0)
+    flag = execution_direction == EXEC_REVERSE ? BTHR_RCONT : BTHR_CONT;
+  else
+    flag = execution_direction == EXEC_REVERSE ? BTHR_RSTEP : BTHR_STEP;
+
+  /* Find the thread to move.  */
+  if (ptid_equal (minus_one_ptid, ptid) || ptid_is_pid (ptid))
+    {
+      ALL_THREADS (tp)
+	record_btrace_resume_thread (tp, flag);
+    }
+  else if (tp == NULL)
+    error (_("Cannot find thread to resume."));
+  else
+    record_btrace_resume_thread (tp, flag);
+
+  /* We just indicate the resume intent here.  The actual stepping happens in
+     record_btrace_wait below.  */
+}
+
+/* Find a thread to move.  */
+
+static struct thread_info *
+record_btrace_find_thread_to_move (ptid_t ptid)
+{
+  struct thread_info *tp;
+
+  /* First check the parameter thread.  */
+  tp = find_thread_ptid (ptid);
+  if (tp != NULL && (tp->btrace.flags & BTHR_MOVE) != 0)
+    return tp;
+
+  /* Next check the current thread. */
+  tp = find_thread_ptid (inferior_ptid);
+  if (tp != NULL && (tp->btrace.flags & BTHR_MOVE) != 0)
+    return tp;
+
+  /* Otherwise, find one other thread that has been resumed.  */
+  ALL_THREADS (tp)
+    if ((tp->btrace.flags & BTHR_MOVE) != 0)
+      return tp;
+
+  return NULL;
+}
+
+/* Return a targetwait status indicating that we ran out of history.  */
+
+static struct target_waitstatus
+btrace_step_no_history (void)
+{
+  struct target_waitstatus status;
+
+  status.kind = TARGET_WAITKIND_NO_HISTORY;
+
+  return status;
+}
+
+/* Return a targetwait status indicating that we stopped.  */
+
+static struct target_waitstatus
+btrace_step_stopped (void)
+{
+  struct target_waitstatus status;
+
+  status.kind = TARGET_WAITKIND_STOPPED;
+  status.value.sig = GDB_SIGNAL_TRAP;
+
+  return status;
+}
+
+/* Clear the record histories.  */
+
+static void
+record_btrace_clear_histories (struct btrace_thread_info *btinfo)
+{
+  xfree (btinfo->insn_history);
+  xfree (btinfo->call_history);
+
+  btinfo->insn_history = NULL;
+  btinfo->call_history = NULL;
+}
+
+/* Step a single thread.  */
+
+static struct target_waitstatus
+record_btrace_step_thread (struct thread_info *tp)
+{
+  struct btrace_insn_iterator *replay, end;
+  struct btrace_thread_info *btinfo;
+  struct address_space *aspace;
+  struct inferior *inf;
+  enum btrace_thread_flag flags;
+  unsigned int steps;
+
+  btinfo = &tp->btrace;
+  replay = btinfo->replay;
+
+  flags = btinfo->flags & BTHR_MOVE;
+  btinfo->flags &= ~BTHR_MOVE;
+
+  DEBUG ("stepping %d (%s): %u", tp->num, target_pid_to_str (tp->ptid), flags);
+
+  switch (flags)
+    {
+    default:
+      internal_error (__FILE__, __LINE__, _("invalid stepping type."));
+
+    case BTHR_STEP:
+      /* We're done if we're not replaying.  */
+      if (replay == NULL)
+	return btrace_step_no_history ();
+
+      /* We are always able to step at least once.  */
+      steps = btrace_insn_next (replay, 1);
+      gdb_assert (steps == 1);
+
+      /* Determine the end of the instruction trace.  */
+      btrace_insn_end (&end, btinfo);
+
+      /* We stop replaying if we reached the end of the trace.  */
+      if (btrace_insn_cmp (replay, &end) == 0)
+	record_btrace_stop_replaying (tp);
+
+      return btrace_step_stopped ();
+
+    case BTHR_RSTEP:
+      /* Start replaying if we're not already doing so.  */
+      if (replay == NULL)
+	replay = record_btrace_start_replaying (tp);
+
+      /* If we can't step any further, we reached the end of the history.  */
+      steps = btrace_insn_prev (replay, 1);
+      if (steps == 0)
+	return btrace_step_no_history ();
+
+      return btrace_step_stopped ();
+
+    case BTHR_CONT:
+      /* We're done if we're not replaying.  */
+      if (replay == NULL)
+	return btrace_step_no_history ();
+
+      inf = find_inferior_pid (ptid_get_pid (tp->ptid));
+      aspace = inf->aspace;
+
+      /* Determine the end of the instruction trace.  */
+      btrace_insn_end (&end, btinfo);
+
+      for (;;)
+	{
+	  const struct btrace_insn *insn;
+
+	  /* We are always able to step at least once.  */
+	  steps = btrace_insn_next (replay, 1);
+	  gdb_assert (steps == 1);
+
+	  /* We stop replaying if we reached the end of the trace.  */
+	  if (btrace_insn_cmp (replay, &end) == 0)
+	    {
+	      record_btrace_stop_replaying (tp);
+	      return btrace_step_no_history ();
+	    }
+
+	  insn = btrace_insn_get (replay);
+	  gdb_assert (insn);
+
+	  DEBUG ("stepping %d (%s) ... %s", tp->num,
+		 target_pid_to_str (tp->ptid),
+		 core_addr_to_string_nz (insn->pc));
+
+	  if (breakpoint_here_p (aspace, insn->pc))
+	    return btrace_step_stopped ();
+	}
+
+    case BTHR_RCONT:
+      /* Start replaying if we're not already doing so.  */
+      if (replay == NULL)
+	replay = record_btrace_start_replaying (tp);
+
+      inf = find_inferior_pid (ptid_get_pid (tp->ptid));
+      aspace = inf->aspace;
+
+      for (;;)
+	{
+	  const struct btrace_insn *insn;
+
+	  /* If we can't step any further, we're done.  */
+	  steps = btrace_insn_prev (replay, 1);
+	  if (steps == 0)
+	    return btrace_step_no_history ();
+
+	  insn = btrace_insn_get (replay);
+	  gdb_assert (insn);
+
+	  DEBUG ("stepping %d (%s): reverse~ ... %s", tp->num,
+		 target_pid_to_str (tp->ptid),
+		 core_addr_to_string_nz (insn->pc));
+
+	  if (breakpoint_here_p (aspace, insn->pc))
+	    return btrace_step_stopped ();
+	}
+    }
 }
 
 /* The to_wait method of target record-btrace.  */
@@ -1258,8 +1597,12 @@ static ptid_t
 record_btrace_wait (struct target_ops *ops, ptid_t ptid,
 		    struct target_waitstatus *status, int options)
 {
+  struct thread_info *tp, *other;
+
+  DEBUG ("wait %s (0x%x)", target_pid_to_str (ptid), options);
+
   /* As long as we're not replaying, just forward the request.  */
-  if (!record_btrace_is_replaying ())
+  if (!record_btrace_is_replaying () && execution_direction != EXEC_REVERSE)
     {
       for (ops = ops->beneath; ops != NULL; ops = ops->beneath)
 	if (ops->to_wait != NULL)
@@ -1268,7 +1611,40 @@ record_btrace_wait (struct target_ops *ops, ptid_t ptid,
       error (_("Cannot find target for waiting."));
     }
 
-  error (_("You can't do this from here.  Do 'record goto end', first."));
+  /* Let's find a thread to move.  */
+  tp = record_btrace_find_thread_to_move (ptid);
+  if (tp == NULL)
+    {
+      DEBUG ("wait %s: no thread", target_pid_to_str (ptid));
+
+      status->kind = TARGET_WAITKIND_IGNORE;
+      return minus_one_ptid;
+    }
+
+  /* We only move a single thread.  We're not able to correlate threads.  */
+  *status = record_btrace_step_thread (tp);
+
+  /* Stop all other threads. */
+  if (!non_stop)
+    ALL_THREADS (other)
+      other->btrace.flags &= ~BTHR_MOVE;
+
+  /* Start record histories anew from the current position.  */
+  record_btrace_clear_histories (&tp->btrace);
+
+  /* GDB seems to need this.  Without, a stale PC seems to be used resulting in
+     the current location to be displayed incorrectly.  */
+  registers_changed_ptid (tp->ptid);
+
+  return tp->ptid;
+}
+
+/* The to_can_execute_reverse method of target record-btrace.  */
+
+static int
+record_btrace_can_execute_reverse (void)
+{
+  return 1;
 }
 
 /* The to_find_new_threads method of target record-btrace.  */
@@ -1301,32 +1677,20 @@ record_btrace_set_replay (struct thread_info *tp,
   btinfo = &tp->btrace;
 
   if (it == NULL || it->function == NULL)
-    {
-      if (btinfo->replay == NULL)
-	return;
-
-      xfree (btinfo->replay);
-      btinfo->replay = NULL;
-    }
+    record_btrace_stop_replaying (tp);
   else
     {
       if (btinfo->replay == NULL)
-	btinfo->replay = xmalloc (sizeof (*btinfo->replay));
+	record_btrace_start_replaying (tp);
       else if (btrace_insn_cmp (btinfo->replay, it) == 0)
 	return;
 
       *btinfo->replay = *it;
+      registers_changed_ptid (tp->ptid);
     }
 
-  /* Clear the function call and instruction histories so we start anew
-     from the new replay position.  */
-  xfree (btinfo->insn_history);
-  xfree (btinfo->call_history);
-
-  btinfo->insn_history = NULL;
-  btinfo->call_history = NULL;
-
-  registers_changed_ptid (tp->ptid);
+  /* Start anew from the new replay position.  */
+  record_btrace_clear_histories (btinfo);
 }
 
 /* The to_goto_record_begin method of target record-btrace.  */
@@ -1425,6 +1789,7 @@ init_record_btrace_ops (void)
   ops->to_goto_record_begin = record_btrace_goto_begin;
   ops->to_goto_record_end = record_btrace_goto_end;
   ops->to_goto_record = record_btrace_goto;
+  ops->to_can_execute_reverse = record_btrace_can_execute_reverse;
   ops->to_stratum = record_stratum;
   ops->to_magic = OPS_MAGIC;
 }
diff --git a/gdb/testsuite/gdb.btrace/delta.exp b/gdb/testsuite/gdb.btrace/delta.exp
index 9ee2629..49d151e 100644
--- a/gdb/testsuite/gdb.btrace/delta.exp
+++ b/gdb/testsuite/gdb.btrace/delta.exp
@@ -61,3 +61,16 @@ gdb_test "record instruction-history /f 1" "
 1\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tmov *\\\$0x0,%eax\r" "delta, 4.2"
 gdb_test "record function-call-history /c 1" "
 1\tmain\r" "delta, 4.3"
+
+# check that we can reverse-stepi that instruction
+gdb_test "reverse-stepi"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 1 instructions in 1 functions for .*\r
+Replay in progress\.  At instruction 1\." "delta, 5.1"
+
+# and back
+gdb_test "stepi"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 1 instructions in 1 functions for .*" "delta, 5.2"
diff --git a/gdb/testsuite/gdb.btrace/finish.exp b/gdb/testsuite/gdb.btrace/finish.exp
new file mode 100644
index 0000000..87ebfe1
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/finish.exp
@@ -0,0 +1,70 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile x86-record_goto.S
+if [prepare_for_testing finish.exp $testfile $srcfile] {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# trace the call to the test function
+gdb_test_no_output "record btrace"
+gdb_test "next"
+
+# let's go somewhere where we can finish
+gdb_test "record goto 32" ".*fun1\.1.*" "finish, 1.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 32\." "finish, 1.2"
+
+# let's finish into fun2
+gdb_test "finish" ".*fun2\.3.*" "finish, 2.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 35\." "finish, 2.2"
+
+# now let's reverse-finish into fun3
+gdb_test "reverse-finish" ".*fun3\.3.*" "finish, 3.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 27\." "finish, 3.2"
+
+# finish again - into fun4
+gdb_test "finish" ".*fun4\.5.*" "finish, 4.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 39\." "finish, 4.2"
+
+# and reverse-finish again - into main
+gdb_test "reverse-finish" ".*main\.2.*" "finish, 5.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 1\." "finish, 5.2"
diff --git a/gdb/testsuite/gdb.btrace/multi-thread-step.c b/gdb/testsuite/gdb.btrace/multi-thread-step.c
new file mode 100644
index 0000000..487565b
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/multi-thread-step.c
@@ -0,0 +1,53 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2013 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <markus.t.metzger@intel.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <pthread.h>
+
+static pthread_barrier_t barrier;
+static int global;
+
+static void *
+test (void *arg)
+{
+  pthread_barrier_wait (&barrier);
+
+  global = 42; /* bp.1 */
+
+  pthread_barrier_wait (&barrier);
+
+  global = 42; /* bp.2 */
+
+  return arg;
+}
+
+int
+main (void)
+{
+  pthread_t th;
+
+  pthread_barrier_init (&barrier, NULL, 2);
+  pthread_create (&th, NULL, test, NULL);
+
+  test (NULL);
+
+  pthread_join (th, NULL);
+  pthread_barrier_destroy (&barrier);
+
+  return 0; /* bp.3 */
+}
diff --git a/gdb/testsuite/gdb.btrace/multi-thread-step.exp b/gdb/testsuite/gdb.btrace/multi-thread-step.exp
new file mode 100644
index 0000000..d247845
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/multi-thread-step.exp
@@ -0,0 +1,99 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile
+if {[gdb_compile_pthreads "$srcdir/$subdir/$srcfile" "$binfile" executable {debug}] != "" } {
+    return -1
+}
+clean_restart $testfile
+
+if ![runto_main] {
+    return -1
+}
+
+# set up breakpoints
+set bp_1 [gdb_get_line_number "bp.1" $srcfile]
+set bp_2 [gdb_get_line_number "bp.2" $srcfile]
+set bp_3 [gdb_get_line_number "bp.3" $srcfile]
+
+proc gdb_cont_to_line { line test } {
+	gdb_breakpoint $line
+	gdb_continue_to_breakpoint "$test" ".*$line\r\n.*"
+	delete_breakpoints
+}
+
+# trace the code between the two breakpoints
+delete_breakpoints
+gdb_cont_to_line $srcfile:$bp_1 "mts, 0.1"
+# make sure GDB knows about the new thread
+gdb_test "info threads" ".*" "mts, 0.2"
+gdb_test_no_output "record btrace" "mts, 0.3"
+gdb_cont_to_line $srcfile:$bp_2 "mts, 0.4"
+
+# navigate in the trace history for both threads
+gdb_test "thread 1" ".*" "mts, 1.1"
+gdb_test "record goto begin" ".*" "mts, 1.2"
+gdb_test "info record" ".*Replay in progress\.  At instruction 1\." "mts, 1.3"
+gdb_test "thread 2" ".*" "mts, 1.4"
+gdb_test "record goto begin" ".*" "mts, 1.5"
+gdb_test "info record" ".*Replay in progress\.  At instruction 1\." "mts, 1.6"
+
+# step both threads
+gdb_test "thread 1" ".*" "mts, 2.1"
+gdb_test "info record" ".*Replay in progress\.  At instruction 1\." "mts, 2.2"
+gdb_test "stepi" ".*" "mts, 2.3"
+gdb_test "info record" ".*Replay in progress\.  At instruction 2\." "mts, 2.4"
+gdb_test "thread 2" ".*" "mts, 2.5"
+gdb_test "info record" ".*Replay in progress\.  At instruction 1\." "mts, 2.6"
+gdb_test "stepi" ".*" "mts, 2.7"
+gdb_test "info record" ".*Replay in progress\.  At instruction 2\." "mts, 2.8"
+
+# run to the end of the history for both threads
+gdb_test "thread 1" ".*" "mts, 3.1"
+gdb_test "info record" ".*Replay in progress\.  At instruction 2\." "mts, 3.2"
+gdb_test "continue" "No more reverse-execution history.*" "mts, 3.3"
+gdb_test "thread 2" ".*" "mts, 3.4"
+gdb_test "info record" ".*Replay in progress\.  At instruction 2\." "mts, 3.5"
+gdb_test "continue" "No more reverse-execution history.*" "mts, 3.6"
+
+# reverse-step both threads
+gdb_test "thread 1" ".*" "mts, 4.1"
+gdb_test "reverse-stepi" ".*" "mts, 4.2"
+gdb_test "info record" ".*Replay in progress\..*" "mts, 4.3"
+gdb_test "thread 2" ".*" "mts, 4.4"
+gdb_test "reverse-stepi" ".*" "mts, 4.5"
+gdb_test "info record" ".*Replay in progress\..*" "mts, 4.6"
+
+# both threads are still replaying
+gdb_test "thread 1" ".*" "mts, 5.1"
+gdb_test "info record" ".*Replay in progress\..*" "mts, 5.2"
+gdb_test "thread 2" ".*" "mts, 5.3"
+gdb_test "info record" ".*Replay in progress\..*" "mts, 5.4"
+
+# navigate back into the history for thread 1 and continue thread 2
+gdb_test "thread 1" ".*" "mts, 6.1"
+gdb_test "record goto begin" ".*" "mts, 6.2"
+gdb_test "info record" ".*Replay in progress\.  At instruction 1\." "mts, 6.3"
+gdb_test "thread 2" ".*" "mts, 6.4"
+gdb_test "record goto end" ".*" "mts, 6.5"
+gdb_cont_to_line $srcfile:$bp_3 "mts, 6.6"
diff --git a/gdb/testsuite/gdb.btrace/next.exp b/gdb/testsuite/gdb.btrace/next.exp
new file mode 100644
index 0000000..12a5e8e
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/next.exp
@@ -0,0 +1,89 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile x86-record_goto.S
+if [prepare_for_testing next.exp $testfile $srcfile] {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# trace the call to the test function
+gdb_test_no_output "record btrace"
+gdb_test "next"
+
+# we start with stepping to make sure that the trace is fetched automatically
+# the call is outside of our trace
+gdb_test "reverse-next" ".*main\.2.*" "next, 1.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 1\." "next, 1.2"
+
+# we can't reverse-step any further
+gdb_test "reverse-next" "No more reverse-execution history\.\r
+.*main\.2.*" "next, 1.3"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 1\." "next, 1.4"
+
+# but we can step back again
+gdb_test "next" ".*main\.3.*" "next, 1.5"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r" "next, 1.6"
+
+# let's go somewhere where we can step some more
+gdb_test "record goto 22" ".*fun3\.2.*" "next, 2.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 22\." "next, 2.2"
+
+gdb_test "next" ".*fun3\.3.*" "next, 2.3"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 27\." "next, 2.4"
+
+gdb_test "next" ".*fun3\.4.*" "next, 2.5"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 37\." "next, 2.6"
+
+# and back again
+gdb_test "reverse-next" ".*fun3\.3.*" "next, 3.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 27\." "next, 3.2"
+
+gdb_test "reverse-next" ".*fun3\.2.*" "next, 3.3"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 22\." "next, 3.4"
diff --git a/gdb/testsuite/gdb.btrace/nexti.exp b/gdb/testsuite/gdb.btrace/nexti.exp
new file mode 100644
index 0000000..559a9b7
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/nexti.exp
@@ -0,0 +1,89 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile x86-record_goto.S
+if [prepare_for_testing nexti.exp $testfile $srcfile] {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# trace the call to the test function
+gdb_test_no_output "record btrace"
+gdb_test "next"
+
+# we start with stepping to make sure that the trace is fetched automatically
+# the call is outside of our trace
+gdb_test "reverse-nexti" ".*main\.2.*" "nexti, 1.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 1\." "nexti, 1.2"
+
+# we can't reverse-step any further
+gdb_test "reverse-nexti" "No more reverse-execution history\.\r
+.*main\.2.*" "nexti, 1.3"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 1\." "nexti, 1.4"
+
+# but we can step back again
+gdb_test "nexti" ".*main\.3.*" "next, 1.5"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r" "next, 1.6"
+
+# let's go somewhere where we can step some more
+gdb_test "record goto 22" ".*fun3\.2.*" "nexti, 2.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 22\." "nexti, 2.2"
+
+gdb_test "nexti" ".*fun3\.3.*" "nexti, 2.3"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 27\." "nexti, 2.4"
+
+gdb_test "nexti" ".*fun3\.4.*" "nexti, 2.5"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 37\." "nexti, 2.6"
+
+# and back again
+gdb_test "reverse-nexti" ".*fun3\.3.*" "nexti, 3.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 27\." "nexti, 3.2"
+
+gdb_test "reverse-nexti" ".*fun3\.2.*" "nexti, 3.3"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 22\." "nexti, 3.4"
diff --git a/gdb/testsuite/gdb.btrace/record_goto.c b/gdb/testsuite/gdb.btrace/record_goto.c
index 1250708..90537f9 100644
--- a/gdb/testsuite/gdb.btrace/record_goto.c
+++ b/gdb/testsuite/gdb.btrace/record_goto.c
@@ -19,33 +19,33 @@
 
 void
 fun1 (void)
-{
-}
+{		/* fun1.1 */
+}		/* fun1.2 */
 
 void
 fun2 (void)
-{
-  fun1 ();
-}
+{		/* fun2.1 */
+  fun1 ();	/* fun2.2 */
+}		/* fun2.3 */
 
 void
 fun3 (void)
-{
-  fun1 ();
-  fun2 ();
-}
+{		/* fun3.1 */
+  fun1 ();	/* fun3.2 */
+  fun2 ();	/* fun3.3 */
+}		/* fun3.4 */
 
 void
 fun4 (void)
-{
-  fun1 ();
-  fun2 ();
-  fun3 ();
-}
+{		/* fun4.1 */
+  fun1 ();	/* fun4.2 */
+  fun2 ();	/* fun4.3 */
+  fun3 ();	/* fun4.4 */
+}		/* fun4.5 */
 
 int
 main (void)
-{
-  fun4 ();
-  return 0;
-}
+{		/* main.1 */
+  fun4 ();	/* main.2 */
+  return 0;	/* main.3 */
+}		/* main.4 */
diff --git a/gdb/testsuite/gdb.btrace/rn-dl-bind.c b/gdb/testsuite/gdb.btrace/rn-dl-bind.c
new file mode 100644
index 0000000..4930297
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/rn-dl-bind.c
@@ -0,0 +1,37 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2013 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <markus.t.metzger@intel.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <stdlib.h>
+
+int test (void)
+{
+  int ret;
+
+  ret = strtoul ("42", NULL, 10);	/* test.1 */
+  return ret;				/* test.2 */
+}					/* test.3 */
+
+int
+main (void)
+{
+  int ret;
+
+  ret = test ();			/* main.1 */
+  return ret;				/* main.2 */
+}					/* main.3 */
diff --git a/gdb/testsuite/gdb.btrace/rn-dl-bind.exp b/gdb/testsuite/gdb.btrace/rn-dl-bind.exp
new file mode 100644
index 0000000..4d803f9
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/rn-dl-bind.exp
@@ -0,0 +1,48 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile
+if [prepare_for_testing $testfile.exp $testfile $srcfile {c++ debug}] {
+    return -1
+}
+if ![runto_main] {
+    return -1
+}
+
+# trace the code for the call to test
+gdb_test_no_output "record btrace" "rn-dl-bind, 0.1"
+gdb_test "next" ".*main\.2.*" "rn-dl-bind, 0.2"
+
+# just dump the function-call-history to help debugging
+gdb_test_no_output "set record function-call-history-size 0" "rn-dl-bind, 0.3"
+gdb_test "record function-call-history /cli 1" ".*" "rn-dl-bind, 0.4"
+
+# check that we can reverse-next and next
+gdb_test "reverse-next" ".*main\.1.*" "rn-dl-bind, 1.1"
+gdb_test "next" ".*main\.2.*" "rn-dl-bind, 1.2"
+
+# now go into test and try to reverse-next and next over the library call
+gdb_test "reverse-step" ".*test\.3.*" "rn-dl-bind, 2.1"
+gdb_test "reverse-step" ".*test\.2.*" "rn-dl-bind, 2.2"
+gdb_test "reverse-next" ".*test\.1.*" "rn-dl-bind, 2.3"
+gdb_test "next" ".*test\.2.*" "rn-dl-bind, 2.4"
diff --git a/gdb/testsuite/gdb.btrace/step.exp b/gdb/testsuite/gdb.btrace/step.exp
new file mode 100644
index 0000000..bb8942e
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/step.exp
@@ -0,0 +1,113 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile x86-record_goto.S
+if [prepare_for_testing step.exp $testfile $srcfile] {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# trace the call to the test function
+gdb_test_no_output "record btrace"
+gdb_test "next"
+
+# let's start by stepping back into the function we just returned from
+gdb_test "reverse-step" ".*fun4\.5.*" "step, 1.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 39\." "step, 1.2"
+
+# again
+gdb_test "reverse-step" ".*fun3\.4.*" "step, 2.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 37\." "step, 2.2"
+
+# and again
+gdb_test "reverse-step" ".*fun2\.3.*" "step, 3.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 35\." "step, 3.2"
+
+# once more
+gdb_test "reverse-step" ".*fun1\.2.*" "step, 4.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 33\." "step, 4.2"
+
+# and out again the other side
+gdb_test "reverse-step" ".*fun2\.2.*" "step, 5.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 30\." "step, 5.2"
+
+# once again
+gdb_test "reverse-step" ".*fun3\.3.*" "step, 6.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 27\." "step, 6.2"
+
+# and back the way we came
+gdb_test "step" ".*fun2\.2.*" "step, 7.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 30\." "step, 7.2"
+
+gdb_test "step" ".*fun1\.2.*" "step, 8.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 33\." "step, 8.2"
+
+gdb_test "step" ".*fun2\.3.*" "step, 9.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 35\." "step, 9.2"
+
+gdb_test "step" ".*fun3\.4.*" "step, 10.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 37\." "step, 10.2"
+
+gdb_test "step" ".*fun4\.5.*" "step, 11.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 39\." "step, 11.2"
+
+gdb_test "step" ".*main\.3.*" "step, 12.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r" "step, 12.2"
diff --git a/gdb/testsuite/gdb.btrace/stepi.exp b/gdb/testsuite/gdb.btrace/stepi.exp
new file mode 100644
index 0000000..22f1574
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/stepi.exp
@@ -0,0 +1,114 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile x86-record_goto.S
+if [prepare_for_testing stepi.exp $testfile $srcfile] {
+    return -1
+}
+
+global gdb_prompt
+
+if ![runto_main] {
+    return -1
+}
+
+# trace the call to the test function
+gdb_test_no_output "record btrace"
+gdb_test "next"
+
+# we start with stepping to make sure that the trace is fetched automatically
+gdb_test "reverse-stepi" ".*fun4\.5.*" "stepi, 1.1"
+gdb_test "reverse-stepi" ".*fun4\.5.*" "stepi, 1.2"
+
+# let's check where we are in the trace
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 39\." "stepi, 1.3"
+
+# let's step forward and check again
+gdb_test "stepi" ".*fun4\.5.*" "stepi, 2.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 40\." "stepi, 2.2"
+
+# with the next step, we stop replaying
+gdb_test "stepi" ".*main\.3.*" "stepi, 2.3"
+gdb_test_multiple "info record" "stepi, 2.4" {
+	-re "Replay in progress.*$gdb_prompt $" { fail "stepi, 2.4" }
+	-re ".*$gdb_prompt $" { pass "stepi, 2.4" }
+}
+
+# let's step from a goto position somewhere in the middle
+gdb_test "record goto 22" ".*fun3\.2.*" "stepi, 3.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 22\." "stepi, 3.2"
+gdb_test "stepi" ".*fun1\.1.*" "stepi, 3.3"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 23\." "stepi, 3.4"
+
+# and back again
+gdb_test "reverse-stepi" ".*fun3\.2.*" "stepi, 4.1"
+gdb_test "reverse-stepi" ".*fun3\.1.*" "stepi, 4.2"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 21\." "stepi, 4.3"
+
+# let's try to step off the left end
+gdb_test "record goto begin" ".*main\.2.*" "stepi, 5.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 1\." "stepi, 5.2"
+gdb_test "reverse-stepi" "No more reverse-execution history\.\r
+.*main\.2.*" "stepi, 5.3"
+gdb_test "reverse-stepi" "No more reverse-execution history\.\r
+.*main\.2.*" "stepi, 5.4"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 1\." "stepi, 5.5"
+
+# we can step forward, though
+gdb_test "stepi" ".*fun4\.1.*" "stepi, 6.1"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 2\." "stepi, 6.2"
+
+# let's try to step off the left end again
+gdb_test "reverse-stepi" ".*main\.2.*" "stepi, 7.1"
+gdb_test "reverse-stepi" "No more reverse-execution history\.\r
+.*main\.2.*" "stepi, 7.2"
+gdb_test "reverse-stepi" "No more reverse-execution history\.\r
+.*main\.2.*" "stepi, 7.3"
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 40 instructions in 16 functions for .*\r
+Replay in progress\.  At instruction 1\." "stepi, 7.4"
diff --git a/gdb/testsuite/gdb.btrace/tailcall.exp b/gdb/testsuite/gdb.btrace/tailcall.exp
index b9520c4..bc1ff29 100644
--- a/gdb/testsuite/gdb.btrace/tailcall.exp
+++ b/gdb/testsuite/gdb.btrace/tailcall.exp
@@ -77,3 +77,27 @@ gdb_test "up" "
 #1\[^\r\n\]*foo \\(\\) at x86-tailcall.c:29\r\n.*" "up to foo"
 gdb_test "up" "
 #2\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*" "up to main"
+gdb_test "down" "
+#1\[^\r\n\]*foo \\(\\) at x86-tailcall.c:29\r\n.*" "down to foo"
+
+# test stepping into and out of tailcalls.
+gdb_test "finish" "
+\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*" "step, 1.1"
+gdb_test "reverse-step" "
+\[^\r\n\]*bar \\(\\) at x86-tailcall.c:24\r\n.*" "step, 1.2"
+gdb_test "reverse-finish" "
+\[^\r\n\]*foo \\(\\) at x86-tailcall.c:29\r\n.*" "step, 1.3"
+gdb_test "reverse-step" "
+\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*" "step, 1.4"
+gdb_test "next" "
+\[^\r\n\]*main \\(\\) at x86-tailcall.c:39\r\n.*" "step, 1.5"
+gdb_test "reverse-next" "
+\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*" "step, 1.6"
+gdb_test "step" "
+\[^\r\n\]*foo \\(\\) at x86-tailcall.c:29\r\n.*" "step, 1.7"
+gdb_test "finish" "
+\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*" "step, 1.8"
+gdb_test "reverse-step" "
+\[^\r\n\]*bar \\(\\) at x86-tailcall.c:24\r\n.*" "step, 1.9"
+gdb_test "finish" "
+\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*" "step, 1.10"
-- 
1.8.3.1

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

* [patch v8 12/24] btrace: add replay position to btrace thread info
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (11 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 20/24] record-btrace: add record goto target methods Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-12  9:16 ` [patch v8 24/24] record-btrace: add (reverse-)stepping support Markus Metzger
                   ` (11 subsequent siblings)
  24 siblings, 0 replies; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

Add a branch trace instruction iterator pointing to the current replay position
to the branch trace thread info struct.

Free the iterator when btrace is cleared.

Start at the replay position for the instruction and function-call histories.

2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

    * btrace.h (replay) <replay>: New.
    (btrace_is_replaying): New.
    * btrace.c (btrace_clear): Free replay iterator.
    (btrace_is_replaying): New.
    * record-btrace.c (record_btrace_is_replaying): New.
    (record_btrace_info): Print insn number if replaying.
    (record_btrace_insn_history): Start at replay position.
    (record_btrace_call_history): Start at replay position.
    (init_record_btrace_ops): Init to_record_is_replaying.


---
 gdb/btrace.c        | 10 +++++++
 gdb/btrace.h        |  6 ++++
 gdb/record-btrace.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++------
 3 files changed, 88 insertions(+), 8 deletions(-)

diff --git a/gdb/btrace.c b/gdb/btrace.c
index d391ad5..ad15481 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -770,9 +770,11 @@ btrace_clear (struct thread_info *tp)
 
   xfree (btinfo->insn_history);
   xfree (btinfo->call_history);
+  xfree (btinfo->replay);
 
   btinfo->insn_history = NULL;
   btinfo->call_history = NULL;
+  btinfo->replay = NULL;
 }
 
 /* See btrace.h.  */
@@ -1328,3 +1330,11 @@ btrace_set_call_history (struct btrace_thread_info *btinfo,
   btinfo->call_history->begin = *begin;
   btinfo->call_history->end = *end;
 }
+
+/* See btrace.h.  */
+
+int
+btrace_is_replaying (struct thread_info *tp)
+{
+  return tp->btrace.replay != NULL;
+}
diff --git a/gdb/btrace.h b/gdb/btrace.h
index f4b2cc1..0a48253 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -187,6 +187,9 @@ struct btrace_thread_info
 
   /* The function call history iterator.  */
   struct btrace_call_history *call_history;
+
+  /* The current replay position.  NULL if not replaying.  */
+  struct btrace_insn_iterator *replay;
 };
 
 /* Enable branch tracing for a thread.  */
@@ -308,4 +311,7 @@ extern void btrace_set_call_history (struct btrace_thread_info *,
 				     const struct btrace_call_iterator *begin,
 				     const struct btrace_call_iterator *end);
 
+/* Determine if branch tracing is currently replaying TP.  */
+extern int btrace_is_replaying (struct thread_info *tp);
+
 #endif /* BTRACE_H */
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 45d5e4a..fc692aa 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -237,6 +237,10 @@ record_btrace_info (void)
   printf_unfiltered (_("Recorded %u instructions in %u functions for thread "
 		       "%d (%s).\n"), insns, calls, tp->num,
 		     target_pid_to_str (tp->ptid));
+
+  if (btrace_is_replaying (tp))
+    printf_unfiltered (_("Replay in progress.  At instruction %u.\n"),
+		       btrace_insn_number (btinfo->replay));
 }
 
 /* Print an unsigned int.  */
@@ -301,13 +305,34 @@ record_btrace_insn_history (int size, int flags)
   history = btinfo->insn_history;
   if (history == NULL)
     {
-      /* No matter the direction, we start with the tail of the trace.  */
-      btrace_insn_end (&begin, btinfo);
-      end = begin;
+      struct btrace_insn_iterator *replay;
 
       DEBUG ("insn-history (0x%x): %d", flags, size);
 
-      covered = btrace_insn_prev (&begin, context);
+      /* If we're replaying, we start at the replay position.  Otherwise, we
+	 start at the tail of the trace.  */
+      replay = btinfo->replay;
+      if (replay != NULL)
+	begin = *replay;
+      else
+	btrace_insn_end (&begin, btinfo);
+
+      /* We start from here and expand in the requested direction.  Then we
+	 expand in the other direction, as well, to fill up any remaining
+	 context.  */
+      end = begin;
+      if (size < 0)
+	{
+	  /* We want the current position covered, as well.  */
+	  covered = btrace_insn_next (&end, 1);
+	  covered += btrace_insn_prev (&begin, context - covered);
+	  covered += btrace_insn_next (&end, context - covered);
+	}
+      else
+	{
+	  covered = btrace_insn_next (&end, context);
+	  covered += btrace_insn_prev (&begin, context - covered);
+	}
     }
   else
     {
@@ -561,13 +586,37 @@ record_btrace_call_history (int size, int flags)
   history = btinfo->call_history;
   if (history == NULL)
     {
-      /* No matter the direction, we start with the tail of the trace.  */
-      btrace_call_end (&begin, btinfo);
-      end = begin;
+      struct btrace_insn_iterator *replay;
 
       DEBUG ("call-history (0x%x): %d", flags, size);
 
-      covered = btrace_call_prev (&begin, context);
+      /* If we're replaying, we start at the replay position.  Otherwise, we
+	 start at the tail of the trace.  */
+      replay = btinfo->replay;
+      if (replay != NULL)
+	{
+	  begin.function = replay->function;
+	  begin.btinfo = btinfo;
+	}
+      else
+	btrace_call_end (&begin, btinfo);
+
+      /* We start from here and expand in the requested direction.  Then we
+	 expand in the other direction, as well, to fill up any remaining
+	 context.  */
+      end = begin;
+      if (size < 0)
+	{
+	  /* We want the current position covered, as well.  */
+	  covered = btrace_call_next (&end, 1);
+	  covered += btrace_call_prev (&begin, context - covered);
+	  covered += btrace_call_next (&end, context - covered);
+	}
+      else
+	{
+	  covered = btrace_call_next (&end, context);
+	  covered += btrace_call_prev (&begin, context- covered);
+	}
     }
   else
     {
@@ -688,6 +737,20 @@ record_btrace_call_history_from (ULONGEST from, int size, int flags)
   record_btrace_call_history_range (begin, end, flags);
 }
 
+/* The to_record_is_replaying method of target record-btrace.  */
+
+static int
+record_btrace_is_replaying (void)
+{
+  struct thread_info *tp;
+
+  ALL_THREADS (tp)
+    if (btrace_is_replaying (tp))
+      return 1;
+
+  return 0;
+}
+
 /* Initialize the record-btrace target ops.  */
 
 static void
@@ -714,6 +777,7 @@ init_record_btrace_ops (void)
   ops->to_call_history = record_btrace_call_history;
   ops->to_call_history_from = record_btrace_call_history_from;
   ops->to_call_history_range = record_btrace_call_history_range;
+  ops->to_record_is_replaying = record_btrace_is_replaying;
   ops->to_stratum = record_stratum;
   ops->to_magic = OPS_MAGIC;
 }
-- 
1.8.3.1

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

* [patch v8 11/24] record-btrace: make ranges include begin and end
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (21 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 04/24] frame: add frame_is_tailcall function Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-13 17:56   ` Pedro Alves
  2013-12-12  9:16 ` [patch v8 05/24] frame: artificial frame id's Markus Metzger
  2013-12-12 14:07 ` [patch v8 00/24] record-btrace: reverse Jan Kratochvil
  24 siblings, 1 reply; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

The "record function-call-history" and "record instruction-history" commands
accept a range "begin, end".  End is not included in both cases.  Include it.

Reviewed-by: Eli Zaretskii

2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

	* record-btrace.c (record_btrace_insn_history_range): Include
	end.
	(record_btrace_insn_history_from): Adjust range.
	(record_btrace_call_history_range): Include
	end.
	(record_btrace_call_history_from): Adjust range.

testsuite/
	* gdb.btrace/function_call_history.exp: Update tests.
	* gdb.btrace/instruction_history.exp: Update tests.

doc/
	* gdb.texinfo (Process Record and Replay): Update documentation.


---
 gdb/doc/gdb.texinfo                                |  6 ++--
 gdb/record-btrace.c                                | 38 ++++++++++++++++------
 gdb/target.h                                       |  4 +--
 gdb/testsuite/gdb.btrace/function_call_history.exp |  5 +--
 gdb/testsuite/gdb.btrace/instruction_history.exp   |  9 +++--
 5 files changed, 41 insertions(+), 21 deletions(-)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index a469709..684053a 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6456,8 +6456,7 @@ Disassembles ten more instructions before the last disassembly.
 
 @item record instruction-history @var{begin} @var{end}
 Disassembles instructions beginning with instruction number
-@var{begin} until instruction number @var{end}.  The instruction
-number @var{end} is not included.
+@var{begin} until instruction number @var{end}.
 @end table
 
 This command may not be available for all recording methods.
@@ -6527,8 +6526,7 @@ Prints ten more functions before the last ten-line print.
 
 @item record function-call-history @var{begin} @var{end}
 Prints functions beginning with function number @var{begin} until
-function number @var{end}.  The function number @var{end} is not
-included.
+function number @var{end}.
 @end table
 
 This command may not be available for all recording methods.
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 9470d85..45d5e4a 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -368,7 +368,7 @@ record_btrace_insn_history_range (ULONGEST from, ULONGEST to, int flags)
   if (low != from || high != to)
     error (_("Bad range."));
 
-  if (high <= low)
+  if (high < low)
     error (_("Bad range."));
 
   btinfo = require_btrace ();
@@ -377,10 +377,17 @@ record_btrace_insn_history_range (ULONGEST from, ULONGEST to, int flags)
   if (found == 0)
     error (_("Range out of bounds."));
 
-  /* Silently truncate the range, if necessary.  */
   found = btrace_find_insn_by_number (&end, btinfo, high);
   if (found == 0)
-    btrace_insn_end (&end, btinfo);
+    {
+      /* Silently truncate the range.  */
+      btrace_insn_end (&end, btinfo);
+    }
+  else
+    {
+      /* We want both begin and end to be inclusive.  */
+      btrace_insn_next (&end, 1);
+    }
 
   btrace_insn_history (uiout, &begin, &end, flags);
   btrace_set_insn_history (btinfo, &begin, &end);
@@ -396,6 +403,8 @@ record_btrace_insn_history_from (ULONGEST from, int size, int flags)
   ULONGEST begin, end, context;
 
   context = abs (size);
+  if (context == 0)
+    error (_("Bad record instruction-history-size."));
 
   if (size < 0)
     {
@@ -404,12 +413,12 @@ record_btrace_insn_history_from (ULONGEST from, int size, int flags)
       if (from < context)
 	begin = 0;
       else
-	begin = from - context;
+	begin = from - context + 1;
     }
   else
     {
       begin = from;
-      end = from + context;
+      end = from + context - 1;
 
       /* Check for wrap-around.  */
       if (end < begin)
@@ -619,7 +628,7 @@ record_btrace_call_history_range (ULONGEST from, ULONGEST to, int flags)
   if (low != from || high != to)
     error (_("Bad range."));
 
-  if (high <= low)
+  if (high < low)
     error (_("Bad range."));
 
   btinfo = require_btrace ();
@@ -628,10 +637,17 @@ record_btrace_call_history_range (ULONGEST from, ULONGEST to, int flags)
   if (found == 0)
     error (_("Range out of bounds."));
 
-  /* Silently truncate the range, if necessary.  */
   found = btrace_find_call_by_number (&end, btinfo, high);
   if (found == 0)
-    btrace_call_end (&end, btinfo);
+    {
+      /* Silently truncate the range.  */
+      btrace_call_end (&end, btinfo);
+    }
+  else
+    {
+      /* We want both begin and end to be inclusive.  */
+      btrace_call_next (&end, 1);
+    }
 
   btrace_call_history (uiout, btinfo, &begin, &end, flags);
   btrace_set_call_history (btinfo, &begin, &end);
@@ -647,6 +663,8 @@ record_btrace_call_history_from (ULONGEST from, int size, int flags)
   ULONGEST begin, end, context;
 
   context = abs (size);
+  if (context == 0)
+    error (_("Bad record function-call-history-size."));
 
   if (size < 0)
     {
@@ -655,12 +673,12 @@ record_btrace_call_history_from (ULONGEST from, int size, int flags)
       if (from < context)
 	begin = 0;
       else
-	begin = from - context;
+	begin = from - context + 1;
     }
   else
     {
       begin = from;
-      end = from + context;
+      end = from + context - 1;
 
       /* Check for wrap-around.  */
       if (end < begin)
diff --git a/gdb/target.h b/gdb/target.h
index f22e5c6..1e1467e 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -869,7 +869,7 @@ struct target_ops
     void (*to_insn_history_from) (ULONGEST from, int size, int flags);
 
     /* Disassemble a section of the recorded execution trace from instruction
-       BEGIN (inclusive) to instruction END (exclusive).  */
+       BEGIN (inclusive) to instruction END (inclusive).  */
     void (*to_insn_history_range) (ULONGEST begin, ULONGEST end, int flags);
 
     /* Print a function trace of the recorded execution trace.
@@ -884,7 +884,7 @@ struct target_ops
     void (*to_call_history_from) (ULONGEST begin, int size, int flags);
 
     /* Print a function trace of an execution trace section from function BEGIN
-       (inclusive) to function END (exclusive).  */
+       (inclusive) to function END (inclusive).  */
     void (*to_call_history_range) (ULONGEST begin, ULONGEST end, int flags);
 
     /* Nonzero if TARGET_OBJECT_LIBRARIES_SVR4 may be read with a
diff --git a/gdb/testsuite/gdb.btrace/function_call_history.exp b/gdb/testsuite/gdb.btrace/function_call_history.exp
index a5f072c..1dfb5e3 100644
--- a/gdb/testsuite/gdb.btrace/function_call_history.exp
+++ b/gdb/testsuite/gdb.btrace/function_call_history.exp
@@ -222,9 +222,10 @@ set expected_range "4\tinc\r
 10\tinc\r"
 
 # show functions in instruction range
-gdb_test "record function-call-history 4,11" $expected_range "absolute instruction range"
+gdb_test "record function-call-history 4,10" $expected_range "absolute instruction range"
 gdb_test "record function-call-history 4,+7" $expected_range "relative positive instruction range"
-gdb_test "record function-call-history 11,-7" $expected_range "relative negative instruction range"
+gdb_test "record function-call-history 10,-7" $expected_range "relative negative instruction range"
+gdb_test "record function-call-history 4,4" "4\tinc\r" "single entry"
 
 # set bp after fib recursion and continue
 set bp_location [gdb_get_line_number "bp.2" $testfile.c]
diff --git a/gdb/testsuite/gdb.btrace/instruction_history.exp b/gdb/testsuite/gdb.btrace/instruction_history.exp
index 6878d27..edf529c 100644
--- a/gdb/testsuite/gdb.btrace/instruction_history.exp
+++ b/gdb/testsuite/gdb.btrace/instruction_history.exp
@@ -65,7 +65,7 @@ if { $traced != 6 } {
 }
 
 # test that we see the expected instructions
-gdb_test "record instruction-history 2,7" "
+gdb_test "record instruction-history 2,6" "
 2\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
 3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax\r
 4\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
@@ -79,20 +79,23 @@ gdb_test "record instruction-history /f 2,+5" "
 5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
 6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
 
-gdb_test "record instruction-history /p 7,-5" "
+gdb_test "record instruction-history /p 6,-5" "
 2\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
 3\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax\r
 4\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
 5\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
 6\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
 
-gdb_test "record instruction-history /pf 2,7" "
+gdb_test "record instruction-history /pf 2,6" "
 2\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
 3\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax\r
 4\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
 5\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
 6\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
 
+gdb_test "record instruction-history 2,2" "
+2\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
+
 # the following tests are checking the iterators
 # to avoid lots of regexps, we just check the number of lines that
 # were printed during command execution.
-- 
1.8.3.1

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

* [patch v8 08/24] record-btrace: start counting at one
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (8 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 09/24] btrace: increase buffer size Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-12  9:16 ` [patch v8 21/24] record-btrace: extend unwinder Markus Metzger
                   ` (14 subsequent siblings)
  24 siblings, 0 replies; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

The record instruction-history and record-function-call-history commands start
counting instructions at zero.  This is somewhat unintuitive when we start
navigating in the recorded instruction history.  Start at one, instead.

Approved-by: Jan Kratochvil

2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

    * btrace.c (ftrace_new_function): Start counting at one.

testsuite/
    * gdb.btrace/instruction_history.exp: Update.
    * gdb.btrace/function_call_history.exp: Update.


---
 gdb/btrace.c                                       |   8 +-
 gdb/record-btrace.c                                |   4 +-
 gdb/testsuite/gdb.btrace/function_call_history.exp | 198 ++++++++++-----------
 gdb/testsuite/gdb.btrace/instruction_history.exp   |  60 +++----
 4 files changed, 138 insertions(+), 132 deletions(-)

diff --git a/gdb/btrace.c b/gdb/btrace.c
index 975b60c..d391ad5 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -208,7 +208,13 @@ ftrace_new_function (struct btrace_function *prev,
   bfun->lbegin = INT_MAX;
   bfun->lend = INT_MIN;
 
-  if (prev != NULL)
+  if (prev == NULL)
+    {
+      /* Start counting at one.  */
+      bfun->number = 1;
+      bfun->insn_offset = 1;
+    }
+  else
     {
       gdb_assert (prev->flow.next == NULL);
       prev->flow.next = bfun;
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 0888c6b..466aef2 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -227,11 +227,11 @@ record_btrace_info (void)
 
       btrace_call_end (&call, btinfo);
       btrace_call_prev (&call, 1);
-      calls = btrace_call_number (&call) + 1;
+      calls = btrace_call_number (&call);
 
       btrace_insn_end (&insn, btinfo);
       btrace_insn_prev (&insn, 1);
-      insns = btrace_insn_number (&insn) + 1;
+      insns = btrace_insn_number (&insn);
     }
 
   printf_unfiltered (_("Recorded %u instructions in %u functions for thread "
diff --git a/gdb/testsuite/gdb.btrace/function_call_history.exp b/gdb/testsuite/gdb.btrace/function_call_history.exp
index 7658637..d694d5c 100644
--- a/gdb/testsuite/gdb.btrace/function_call_history.exp
+++ b/gdb/testsuite/gdb.btrace/function_call_history.exp
@@ -40,81 +40,81 @@ gdb_continue_to_breakpoint "cont to $bp_location" ".*$testfile.c:$bp_location.*"
 # show function call history with unlimited size, we expect to see all 21 entries
 gdb_test_no_output "set record function-call-history-size 0"
 gdb_test "record function-call-history" "
-0\tmain\r
-1\tinc\r
-2\tmain\r
-3\tinc\r
-4\tmain\r
-5\tinc\r
-6\tmain\r
-7\tinc\r
-8\tmain\r
-9\tinc\r
-10\tmain\r
-11\tinc\r
-12\tmain\r
-13\tinc\r
-14\tmain\r
-15\tinc\r
-16\tmain\r
-17\tinc\r
-18\tmain\r
-19\tinc\r
-20\tmain\r" "record function-call-history - with size unlimited"
+1\tmain\r
+2\tinc\r
+3\tmain\r
+4\tinc\r
+5\tmain\r
+6\tinc\r
+7\tmain\r
+8\tinc\r
+9\tmain\r
+10\tinc\r
+11\tmain\r
+12\tinc\r
+13\tmain\r
+14\tinc\r
+15\tmain\r
+16\tinc\r
+17\tmain\r
+18\tinc\r
+19\tmain\r
+20\tinc\r
+21\tmain\r" "record function-call-history - with size unlimited"
 
 # show function call history with size of 21, we expect to see all 21 entries
 gdb_test_no_output "set record function-call-history-size 21"
 # show function call history
-gdb_test "record function-call-history 0" "
-0\tmain\r
-1\tinc\r
-2\tmain\r
-3\tinc\r
-4\tmain\r
-5\tinc\r
-6\tmain\r
-7\tinc\r
-8\tmain\r
-9\tinc\r
-10\tmain\r
-11\tinc\r
-12\tmain\r
-13\tinc\r
-14\tmain\r
-15\tinc\r
-16\tmain\r
-17\tinc\r
-18\tmain\r
-19\tinc\r
-20\tmain\r" "record function-call-history - show all 21 entries"
+gdb_test "record function-call-history 1" "
+1\tmain\r
+2\tinc\r
+3\tmain\r
+4\tinc\r
+5\tmain\r
+6\tinc\r
+7\tmain\r
+8\tinc\r
+9\tmain\r
+10\tinc\r
+11\tmain\r
+12\tinc\r
+13\tmain\r
+14\tinc\r
+15\tmain\r
+16\tinc\r
+17\tmain\r
+18\tinc\r
+19\tmain\r
+20\tinc\r
+21\tmain\r" "record function-call-history - show all 21 entries"
 
 # show first 15 entries
 gdb_test_no_output "set record function-call-history-size 15"
-gdb_test "record function-call-history 0" "
-0\tmain\r
-1\tinc\r
-2\tmain\r
-3\tinc\r
-4\tmain\r
-5\tinc\r
-6\tmain\r
-7\tinc\r
-8\tmain\r
-9\tinc\r
-10\tmain\r
-11\tinc\r
-12\tmain\r
-13\tinc\r
-14\tmain\r" "record function-call-history - show first 15 entries"
+gdb_test "record function-call-history 1" "
+1\tmain\r
+2\tinc\r
+3\tmain\r
+4\tinc\r
+5\tmain\r
+6\tinc\r
+7\tmain\r
+8\tinc\r
+9\tmain\r
+10\tinc\r
+11\tmain\r
+12\tinc\r
+13\tmain\r
+14\tinc\r
+15\tmain\r" "record function-call-history - show first 15 entries"
 
 # show last 6 entries
 gdb_test "record function-call-history +" "
-15\tinc\r
-16\tmain\r
-17\tinc\r
-18\tmain\r
-19\tinc\r
-20\tmain\r" "record function-call-history - show last 6 entries"
+16\tinc\r
+17\tmain\r
+18\tinc\r
+19\tmain\r
+20\tinc\r
+21\tmain\r" "record function-call-history - show last 6 entries"
 
 # moving further should not work
 gdb_test "record function-call-history +" "At the end of the branch trace record\\." "record function-call-history - at the end (1)"
@@ -124,30 +124,30 @@ gdb_test "record function-call-history +" "At the end of the branch trace record
 
 # moving back showing the latest 15 function calls
 gdb_test "record function-call-history -" "
-6\tmain\r
-7\tinc\r
-8\tmain\r
-9\tinc\r
-10\tmain\r
-11\tinc\r
-12\tmain\r
-13\tinc\r
-14\tmain\r
-15\tinc\r
-16\tmain\r
-17\tinc\r
-18\tmain\r
-19\tinc\r
-20\tmain\r" "record function-call-history - show last 15 entries"
+7\tmain\r
+8\tinc\r
+9\tmain\r
+10\tinc\r
+11\tmain\r
+12\tinc\r
+13\tmain\r
+14\tinc\r
+15\tmain\r
+16\tinc\r
+17\tmain\r
+18\tinc\r
+19\tmain\r
+20\tinc\r
+21\tmain\r" "record function-call-history - show last 15 entries"
 
 # moving further back shows the 6 first function calls
 gdb_test "record function-call-history -" "
-0\tmain\r
-1\tinc\r
-2\tmain\r
-3\tinc\r
-4\tmain\r
-5\tinc\r" "record function-call-history - show first 6 entries"
+1\tmain\r
+2\tinc\r
+3\tmain\r
+4\tinc\r
+5\tmain\r
+6\tinc\r" "record function-call-history - show first 6 entries"
 
 # moving further back shouldn't work
 gdb_test "record function-call-history -" "At the start of the branch trace record\\." "record function-call-history - at the start (1)"
@@ -186,18 +186,18 @@ gdb_test "record function-call-history /l +" "
 gdb_test "record function-call-history /l +" "At the end of the branch trace record\\." "record function-call-history /l - at the end (1)"
 gdb_test "record function-call-history /l" "At the end of the branch trace record\\." "record function-call-history /l - at the end (2)"
 
-set expected_range "3\tinc\r
-4\tmain\r
-5\tinc\r
-6\tmain\r
-7\tinc\r
-8\tmain\r
-9\tinc\r"
+set expected_range "4\tinc\r
+5\tmain\r
+6\tinc\r
+7\tmain\r
+8\tinc\r
+9\tmain\r
+10\tinc\r"
 
 # show functions in instruction range
-gdb_test "record function-call-history 3,10" $expected_range "absolute instruction range"
-gdb_test "record function-call-history 3,+7" $expected_range "relative positive instruction range"
-gdb_test "record function-call-history 10,-7" $expected_range "relative negative instruction range"
+gdb_test "record function-call-history 4,11" $expected_range "absolute instruction range"
+gdb_test "record function-call-history 4,+7" $expected_range "relative positive instruction range"
+gdb_test "record function-call-history 11,-7" $expected_range "relative negative instruction range"
 
 # set bp after fib recursion and continue
 set bp_location [gdb_get_line_number "bp.2" $testfile.c]
@@ -208,8 +208,7 @@ gdb_continue_to_breakpoint "cont to $bp_location" ".*$testfile.c:$bp_location.*"
 # so we limit the output to only show the latest 11 function calls
 gdb_test_no_output "set record function-call-history-size 11"
 gdb_test "record function-call-history" "
-20\tmain\r
-21\tfib\r
+21\tmain\r
 22\tfib\r
 23\tfib\r
 24\tfib\r
@@ -218,4 +217,5 @@ gdb_test "record function-call-history" "
 27\tfib\r
 28\tfib\r
 29\tfib\r
-30\tmain" "show recursive function call history"
+30\tfib\r
+31\tmain" "show recursive function call history"
diff --git a/gdb/testsuite/gdb.btrace/instruction_history.exp b/gdb/testsuite/gdb.btrace/instruction_history.exp
index 377dca1..6878d27 100644
--- a/gdb/testsuite/gdb.btrace/instruction_history.exp
+++ b/gdb/testsuite/gdb.btrace/instruction_history.exp
@@ -65,33 +65,33 @@ if { $traced != 6 } {
 }
 
 # test that we see the expected instructions
-gdb_test "record instruction-history 1,6" "
-1\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
-2\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax\r
-3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
-4\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
-5\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
-
-gdb_test "record instruction-history /f 1,+5" "
-1\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
-2\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax\r
-3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
-4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
-5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
-
-gdb_test "record instruction-history /p 6,-5" "
-1\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
-2\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax\r
-3\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
-4\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
-5\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
-
-gdb_test "record instruction-history /pf 1,6" "
-1\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
-2\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax\r
-3\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
-4\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
-5\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
+gdb_test "record instruction-history 2,7" "
+2\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
+3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax\r
+4\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
+5\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
+6\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
+
+gdb_test "record instruction-history /f 2,+5" "
+2\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
+3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax\r
+4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
+5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
+6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
+
+gdb_test "record instruction-history /p 7,-5" "
+2\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
+3\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax\r
+4\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
+5\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
+6\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
+
+gdb_test "record instruction-history /pf 2,7" "
+2\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
+3\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax\r
+4\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
+5\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
+6\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
 
 # the following tests are checking the iterators
 # to avoid lots of regexps, we just check the number of lines that
@@ -117,7 +117,7 @@ proc test_lines_length { command message } {
 # all $traced instructions
 gdb_test_no_output "set record instruction-history-size 0"
 set message "record instruction-history - unlimited"
-set lines [test_lines_length "record instruction-history 0" $message]
+set lines [test_lines_length "record instruction-history 1" $message]
 if { $traced != $lines } {
     fail $message
 } else {
@@ -126,7 +126,7 @@ if { $traced != $lines } {
 
 gdb_test_no_output "set record instruction-history-size $traced"
 set message "record instruction-history - traced"
-set lines [test_lines_length "record instruction-history 0" $message]
+set lines [test_lines_length "record instruction-history 1" $message]
 if { $traced != $lines } {
     fail $message
 } else {
@@ -137,7 +137,7 @@ if { $traced != $lines } {
 set history_size 3
 gdb_test_no_output "set record instruction-history-size $history_size"
 set message "browse history forward start"
-set lines [test_lines_length "record instruction-history 0" $message]
+set lines [test_lines_length "record instruction-history 1" $message]
 if { $lines != $history_size } {
     fail $message
 } else {
-- 
1.8.3.1

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

* [patch v8 10/24] record-btrace: optionally indent function call history
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (5 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 18/24] record-btrace: add to_wait and to_resume target methods Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-12  9:16 ` [patch v8 02/24] btrace: uppercase btrace_read_type Markus Metzger
                   ` (17 subsequent siblings)
  24 siblings, 0 replies; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

Add a new modifier /c to the "record function-call-history" command to
indent the function name based on its depth in the call stack.

Also reorder the optional fields to have the indentation at the very beginning.
Prefix the insn range (/i modifier) with "inst ".
Prefix the source line (/l modifier) with "at ".
Change the range syntax from "begin-end" to "begin,end" to allow copy&paste to
the "record instruction-history" and "list" commands.

Adjust the respective tests and add new tests for the /c modifier.

There is one known bug regarding indentation that results from the fact that we
have the current instruction already inside the branch trace.  When the current
instruction is the first (and only) instruction in a function on the outermost
level for which we have not seen the call, the indentation starts at level 1
with 2 leading spaces.

Reviewed-by: Eli Zaretskii

2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

    * record.h (enum record_print_flag)
    <record_print_indent_calls>: New.
    * record.c (get_call_history_modifiers): Recognize /c modifier.
    (_initialize_record): Document /c modifier.
    * record-btrace.c (btrace_call_history): Add btinfo parameter.
    Reorder fields.  Optionally indent the function name.  Update
    all users.
    * NEWS: Announce changes.

testsuite/
    * gdb.btrace/function_call_history.exp: Fix expected field
    order for "record function-call-history".
    Add new tests for "record function-call-history /c".
    * gdb.btrace/exception.cc: New.
    * gdb.btrace/exception.exp: New.
    * gdb.btrace/tailcall.exp: New.
    * gdb.btrace/x86-tailcall.S: New.
    * gdb.btrace/x86-tailcall.c: New.
    * gdb.btrace/unknown_functions.c: New.
    * gdb.btrace/unknown_functions.exp: New.
    * gdb.btrace/Makefile.in (EXECUTABLES): Add new.

doc/
    * gdb.texinfo (Process Record and Replay): Document new /c
    modifier accepted by "record function-call-history".


---
 gdb/NEWS                                           |   6 +
 gdb/doc/gdb.texinfo                                |  12 +-
 gdb/record-btrace.c                                |  33 ++-
 gdb/record.c                                       |   4 +
 gdb/record.h                                       |   3 +
 gdb/testsuite/gdb.btrace/Makefile.in               |   3 +-
 gdb/testsuite/gdb.btrace/exception.cc              |  56 +++++
 gdb/testsuite/gdb.btrace/exception.exp             |  65 +++++
 gdb/testsuite/gdb.btrace/function_call_history.exp | 112 +++++++--
 gdb/testsuite/gdb.btrace/tailcall.exp              |  60 +++++
 gdb/testsuite/gdb.btrace/unknown_functions.c       |  45 ++++
 gdb/testsuite/gdb.btrace/unknown_functions.exp     |  58 +++++
 gdb/testsuite/gdb.btrace/x86-tailcall.S            | 279 +++++++++++++++++++++
 gdb/testsuite/gdb.btrace/x86-tailcall.c            |  39 +++
 14 files changed, 737 insertions(+), 38 deletions(-)
 create mode 100644 gdb/testsuite/gdb.btrace/exception.cc
 create mode 100755 gdb/testsuite/gdb.btrace/exception.exp
 create mode 100644 gdb/testsuite/gdb.btrace/tailcall.exp
 create mode 100644 gdb/testsuite/gdb.btrace/unknown_functions.c
 create mode 100644 gdb/testsuite/gdb.btrace/unknown_functions.exp
 create mode 100644 gdb/testsuite/gdb.btrace/x86-tailcall.S
 create mode 100644 gdb/testsuite/gdb.btrace/x86-tailcall.c

diff --git a/gdb/NEWS b/gdb/NEWS
index eff057f..39404cc 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -135,6 +135,12 @@ show code-cache
   accept a verbosity level.  0 means "off", 1 provides basic debugging
   output, and values of 2 or greater provides more verbose output.
 
+* The command 'record function-call-history' supports a new modifier '/c' to
+  indent the function names based on their call stack depth.
+  The fields for the '/i' and '/l' modifier have been reordered.
+  The instruction range is now prefixed with 'insn'.
+  The source line range is now prefixed with 'at'.
+
 * New command-line options
 --configuration
   Display the details of GDB configure-time options.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 7dfa9f6..a469709 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6482,7 +6482,9 @@ line for each sequence of instructions that belong to the same
 function giving the name of that function, the source lines
 for this instruction sequence (if the @code{/l} modifier is
 specified), and the instructions numbers that form the sequence (if
-the @code{/i} modifier is specified).
+the @code{/i} modifier is specified).  The function names are indented
+to reflect the call stack depth if the @code{/c} modifier is
+specified.
 
 @smallexample
 (@value{GDBP}) @b{list 1, 10}
@@ -6496,10 +6498,10 @@ the @code{/i} modifier is specified).
 8     foo ();
 9     ...
 10  @}
-(@value{GDBP}) @b{record function-call-history /l}
-1  foo.c:6-8   bar
-2  foo.c:2-3   foo
-3  foo.c:9-10  bar
+(@value{GDBP}) @b{record function-call-history /lc}
+1  bar     at foo.c:6,8
+2    foo   at foo.c:2,3
+3  bar     at foo.c:9,10
 @end smallexample
 
 By default, ten lines are printed.  This can be changed using the
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 466aef2..9470d85 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -434,7 +434,7 @@ btrace_call_history_insn_range (struct ui_out *uiout,
   end = begin + size - 1;
 
   ui_out_field_uint (uiout, "insn begin", begin);
-  ui_out_text (uiout, "-");
+  ui_out_text (uiout, ",");
   ui_out_field_uint (uiout, "insn end", end);
 }
 
@@ -466,7 +466,7 @@ btrace_call_history_src_line (struct ui_out *uiout,
   if (end == begin)
     return;
 
-  ui_out_text (uiout, "-");
+  ui_out_text (uiout, ",");
   ui_out_field_int (uiout, "max line", end);
 }
 
@@ -474,6 +474,7 @@ btrace_call_history_src_line (struct ui_out *uiout,
 
 static void
 btrace_call_history (struct ui_out *uiout,
+		     const struct btrace_thread_info *btinfo,
 		     const struct btrace_call_iterator *begin,
 		     const struct btrace_call_iterator *end,
 		     enum record_print_flag flags)
@@ -497,23 +498,33 @@ btrace_call_history (struct ui_out *uiout,
       ui_out_field_uint (uiout, "index", bfun->number);
       ui_out_text (uiout, "\t");
 
+      if ((flags & RECORD_PRINT_INDENT_CALLS) != 0)
+	{
+	  int level = bfun->level + btinfo->level, i;
+
+	  for (i = 0; i < level; ++i)
+	    ui_out_text (uiout, "  ");
+	}
+
+      if (sym != NULL)
+	ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (sym));
+      else if (msym != NULL)
+	ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (msym));
+      else if (!ui_out_is_mi_like_p (uiout))
+	ui_out_field_string (uiout, "function", "??");
+
       if ((flags & RECORD_PRINT_INSN_RANGE) != 0)
 	{
+	  ui_out_text (uiout, _("\tinst "));
 	  btrace_call_history_insn_range (uiout, bfun);
-	  ui_out_text (uiout, "\t");
 	}
 
       if ((flags & RECORD_PRINT_SRC_LINE) != 0)
 	{
+	  ui_out_text (uiout, _("\tat "));
 	  btrace_call_history_src_line (uiout, bfun);
-	  ui_out_text (uiout, "\t");
 	}
 
-      if (sym != NULL)
-	ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (sym));
-      else if (msym != NULL)
-	ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (msym));
-
       ui_out_text (uiout, "\n");
     }
 }
@@ -570,7 +581,7 @@ record_btrace_call_history (int size, int flags)
     }
 
   if (covered > 0)
-    btrace_call_history (uiout, &begin, &end, flags);
+    btrace_call_history (uiout, btinfo, &begin, &end, flags);
   else
     {
       if (size < 0)
@@ -622,7 +633,7 @@ record_btrace_call_history_range (ULONGEST from, ULONGEST to, int flags)
   if (found == 0)
     btrace_call_end (&end, btinfo);
 
-  btrace_call_history (uiout, &begin, &end, flags);
+  btrace_call_history (uiout, btinfo, &begin, &end, flags);
   btrace_set_call_history (btinfo, &begin, &end);
 
   do_cleanups (uiout_cleanup);
diff --git a/gdb/record.c b/gdb/record.c
index ec2a042..e78070b 100644
--- a/gdb/record.c
+++ b/gdb/record.c
@@ -575,6 +575,9 @@ get_call_history_modifiers (char **arg)
 	    case 'i':
 	      modifiers |= RECORD_PRINT_INSN_RANGE;
 	      break;
+	    case 'c':
+	      modifiers |= RECORD_PRINT_INDENT_CALLS;
+	      break;
 	    default:
 	      error (_("Invalid modifier: %c."), *args);
 	    }
@@ -809,6 +812,7 @@ function.\n\
 Without modifiers, it prints the function name.\n\
 With a /l modifier, the source file and line number range is included.\n\
 With a /i modifier, the instruction number range is included.\n\
+With a /c modifier, the output is indented based on the call stack depth.\n\
 With no argument, prints ten more lines after the previous ten-line print.\n\
 \"record function-call-history -\" prints ten lines before a previous ten-line \
 print.\n\
diff --git a/gdb/record.h b/gdb/record.h
index 124c32b..323b8d1 100644
--- a/gdb/record.h
+++ b/gdb/record.h
@@ -40,6 +40,9 @@ enum record_print_flag
 
   /* Print the instruction number range (if applicable).  */
   RECORD_PRINT_INSN_RANGE = (1 << 1),
+
+  /* Indent based on call stack depth (if applicable).  */
+  RECORD_PRINT_INDENT_CALLS = (1 << 2)
 };
 
 /* Wrapper for target_read_memory that prints a debug message if
diff --git a/gdb/testsuite/gdb.btrace/Makefile.in b/gdb/testsuite/gdb.btrace/Makefile.in
index f4c06d1..5c70700 100644
--- a/gdb/testsuite/gdb.btrace/Makefile.in
+++ b/gdb/testsuite/gdb.btrace/Makefile.in
@@ -1,7 +1,8 @@
 VPATH = @srcdir@
 srcdir = @srcdir@
 
-EXECUTABLES   = enable function_call_history instruction_history
+EXECUTABLES   = enable function_call_history instruction_history tailcall \
+  exception
 
 MISCELLANEOUS =
 
diff --git a/gdb/testsuite/gdb.btrace/exception.cc b/gdb/testsuite/gdb.btrace/exception.cc
new file mode 100644
index 0000000..029a4bc
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/exception.cc
@@ -0,0 +1,56 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2013 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <markus.t.metzger@intel.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+static void
+bad (void)
+{
+  throw 42;
+}
+
+static void
+bar (void)
+{
+  bad ();
+}
+
+static void
+foo (void)
+{
+  bar ();
+}
+
+static void
+test (void)
+{
+  try
+    {
+      foo ();
+    }
+  catch (...)
+    {
+    }
+}
+
+int
+main (void)
+{
+  test ();
+  test (); /* bp.1  */
+  return 0; /* bp.2  */
+}
diff --git a/gdb/testsuite/gdb.btrace/exception.exp b/gdb/testsuite/gdb.btrace/exception.exp
new file mode 100755
index 0000000..042aa97
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/exception.exp
@@ -0,0 +1,65 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile exception.cc
+if [prepare_for_testing $testfile.exp $testfile $srcfile {c++ debug}] {
+    return -1
+}
+if ![runto_main] {
+    return -1
+}
+
+# we want to see the full trace for this test
+gdb_test_no_output "set record function-call-history-size 0"
+
+# set bp
+set bp_1 [gdb_get_line_number "bp.1" $srcfile]
+set bp_2 [gdb_get_line_number "bp.2" $srcfile]
+gdb_breakpoint $bp_1
+gdb_breakpoint $bp_2
+
+# trace the code between the two breakpoints
+gdb_continue_to_breakpoint "cont to bp.1" ".*$srcfile:$bp_1\r\n.*"
+gdb_test_no_output "record btrace"
+gdb_continue_to_breakpoint "cont to bp.2" ".*$srcfile:$bp_2\r\n.*"
+
+# show the flat branch trace
+send_gdb "record function-call-history 1\n"
+gdb_expect_list "exception - flat" "\r\n$gdb_prompt $" {"\r
+1\ttest\\(\\)\r
+2\tfoo\\(\\)\r
+3\tbar\\(\\)\r
+4\tbad\\(\\)\r" "\r
+\[0-9\]*\ttest\\(\\)"}
+
+# show the branch trace with calls indented
+#
+# here we see a known bug that the indentation starts at level 1 with
+# two leading spaces instead of level 0 without leading spaces.
+send_gdb "record function-call-history /c 1\n"
+gdb_expect_list "exception - calls indented" "\r\n$gdb_prompt $" {"\r
+1\t  test\\(\\)\r
+2\t    foo\\(\\)\r
+3\t      bar\\(\\)\r
+4\t        bad\\(\\)\r" "\r
+\[0-9\]*\t  test\\(\\)"}
diff --git a/gdb/testsuite/gdb.btrace/function_call_history.exp b/gdb/testsuite/gdb.btrace/function_call_history.exp
index d694d5c..a5f072c 100644
--- a/gdb/testsuite/gdb.btrace/function_call_history.exp
+++ b/gdb/testsuite/gdb.btrace/function_call_history.exp
@@ -62,6 +62,30 @@ gdb_test "record function-call-history" "
 20\tinc\r
 21\tmain\r" "record function-call-history - with size unlimited"
 
+# show indented function call history with unlimited size
+gdb_test "record function-call-history /c 1" "
+1\tmain\r
+2\t  inc\r
+3\tmain\r
+4\t  inc\r
+5\tmain\r
+6\t  inc\r
+7\tmain\r
+8\t  inc\r
+9\tmain\r
+10\t  inc\r
+11\tmain\r
+12\t  inc\r
+13\tmain\r
+14\t  inc\r
+15\tmain\r
+16\t  inc\r
+17\tmain\r
+18\t  inc\r
+19\tmain\r
+20\t  inc\r
+21\tmain\r" "indented record function-call-history - with size unlimited"
+
 # show function call history with size of 21, we expect to see all 21 entries
 gdb_test_no_output "set record function-call-history-size 21"
 # show function call history
@@ -155,32 +179,35 @@ gdb_test "record function-call-history -" "At the start of the branch trace reco
 # make sure we cannot move any further back
 gdb_test "record function-call-history -" "At the start of the branch trace record\\." "record function-call-history - at the start (2)"
 
+# don't mess around with path names
+gdb_test_no_output "set filename-display basename"
+
 # moving forward again, but this time with file and line number, expected to see the first 15 entries
 gdb_test "record function-call-history /l +" "
-.*$srcfile:40-41\tmain\r
-.*$srcfile:22-24\tinc\r
-.*$srcfile:40-41\tmain\r
-.*$srcfile:22-24\tinc\r
-.*$srcfile:40-41\tmain\r
-.*$srcfile:22-24\tinc\r
-.*$srcfile:40-41\tmain\r
-.*$srcfile:22-24\tinc\r
-.*$srcfile:40-41\tmain\r
-.*$srcfile:22-24\tinc\r
-.*$srcfile:40-41\tmain\r
-.*$srcfile:22-24\tinc\r
-.*$srcfile:40-41\tmain\r
-.*$srcfile:22-24\tinc\r
-.*$srcfile:40-41\tmain\r" "record function-call-history /l - show first 15 entries"
+\[0-9\]*\tmain\tat $srcfile:40,41\r
+\[0-9\]*\tinc\tat $srcfile:22,24\r
+\[0-9\]*\tmain\tat $srcfile:40,41\r
+\[0-9\]*\tinc\tat $srcfile:22,24\r
+\[0-9\]*\tmain\tat $srcfile:40,41\r
+\[0-9\]*\tinc\tat $srcfile:22,24\r
+\[0-9\]*\tmain\tat $srcfile:40,41\r
+\[0-9\]*\tinc\tat $srcfile:22,24\r
+\[0-9\]*\tmain\tat $srcfile:40,41\r
+\[0-9\]*\tinc\tat $srcfile:22,24\r
+\[0-9\]*\tmain\tat $srcfile:40,41\r
+\[0-9\]*\tinc\tat $srcfile:22,24\r
+\[0-9\]*\tmain\tat $srcfile:40,41\r
+\[0-9\]*\tinc\tat $srcfile:22,24\r
+\[0-9\]*\tmain\tat $srcfile:40,41\r" "record function-call-history /l - show first 15 entries"
 
 # moving forward and expect to see the latest 6 entries
 gdb_test "record function-call-history /l +" "
-.*$srcfile:22-24\tinc\r
-.*$srcfile:40-41\tmain\r
-.*$srcfile:22-24\tinc\r
-.*$srcfile:40-41\tmain\r
-.*$srcfile:22-24\tinc\r
-.*$srcfile:40-43\tmain\r" "record function-call-history /l - show last 6 entries"
+\[0-9\]*\tinc\tat $srcfile:22,24\r
+\[0-9\]*\tmain\tat $srcfile:40,41\r
+\[0-9\]*\tinc\tat $srcfile:22,24\r
+\[0-9\]*\tmain\tat $srcfile:40,41\r
+\[0-9\]*\tinc\tat $srcfile:22,24\r
+\[0-9\]*\tmain\tat $srcfile:40,43\r" "record function-call-history /l - show last 6 entries"
 
 # moving further forward shouldn't work
 gdb_test "record function-call-history /l +" "At the end of the branch trace record\\." "record function-call-history /l - at the end (1)"
@@ -219,3 +246,46 @@ gdb_test "record function-call-history" "
 29\tfib\r
 30\tfib\r
 31\tmain" "show recursive function call history"
+
+# show indented function call history for fib
+gdb_test "record function-call-history /c 21, +11" "
+21\tmain\r
+22\t  fib\r
+23\t    fib\r
+24\t  fib\r
+25\t    fib\r
+26\t      fib\r
+27\t    fib\r
+28\t      fib\r
+29\t    fib\r
+30\t  fib\r
+31\tmain" "indented record function-call-history - fib"
+
+# make sure we can handle incomplete trace with respect to indentation
+if ![runto_main] {
+    return -1
+}
+# navigate to the fib in line 24 above
+gdb_breakpoint fib
+gdb_continue_to_breakpoint "cont to fib.1"
+gdb_continue_to_breakpoint "cont to fib.2"
+gdb_continue_to_breakpoint "cont to fib.3"
+gdb_continue_to_breakpoint "cont to fib.4"
+
+# start tracing
+gdb_test_no_output "record btrace"
+
+# continue until line 30 above
+delete_breakpoints
+set bp_location [gdb_get_line_number "bp.2" $testfile.c]
+gdb_breakpoint $bp_location
+gdb_continue_to_breakpoint "cont to bp.2" ".*$testfile.c:$bp_location\r\n.*"
+
+# let's look at the trace. we expect to see the tail of the above listing.
+gdb_test "record function-call-history /c" "
+1\t      fib\r
+2\t    fib\r
+3\t      fib\r
+4\t    fib\r
+5\t  fib\r
+6\tmain" "indented record function-call-history - fib"
diff --git a/gdb/testsuite/gdb.btrace/tailcall.exp b/gdb/testsuite/gdb.btrace/tailcall.exp
new file mode 100644
index 0000000..74c1ab7
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/tailcall.exp
@@ -0,0 +1,60 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile x86-tailcall.S
+
+set opts {}
+if [info exists COMPILE] {
+    # make check RUNTESTFLAGS="gdb.btrace/tailcall.exp COMPILE=1"
+    standard_testfile x86-tailcall.c
+    lappend opts debug optimize=-O2
+} elseif { ![istarget x86_64-*-* ] || ![is_lp64_target] } {
+    verbose "Skipping ${testfile}."
+    return
+}
+
+if [prepare_for_testing tailcall.exp $testfile $srcfile $opts] {
+    return -1
+}
+if ![runto_main] {
+    return -1
+}
+
+# we want to see the full trace for this test
+gdb_test_no_output "set record function-call-history-size 0"
+
+# trace the call to foo
+gdb_test_no_output "record btrace"
+gdb_test "next"
+
+# show the flat branch trace
+gdb_test "record function-call-history 1" "
+1\tfoo\r
+2\tbar\r
+3\tmain" "tailcall - flat"
+
+# show the branch trace with calls indented
+gdb_test "record function-call-history /c 1" "
+1\t  foo\r
+2\t    bar\r
+3\tmain" "tailcall - calls indented"
diff --git a/gdb/testsuite/gdb.btrace/unknown_functions.c b/gdb/testsuite/gdb.btrace/unknown_functions.c
new file mode 100644
index 0000000..178c3e9
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/unknown_functions.c
@@ -0,0 +1,45 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2013 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <markus.t.metzger@intel.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+static int foo (void);
+
+int test (void)
+{
+  return foo ();
+}
+
+static int
+bar (void)
+{
+  return 42;
+}
+
+static int
+foo (void)
+{
+  return bar ();
+}
+
+int
+main (void)
+{
+  test ();
+  test ();
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.btrace/unknown_functions.exp b/gdb/testsuite/gdb.btrace/unknown_functions.exp
new file mode 100644
index 0000000..0c1739f
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/unknown_functions.exp
@@ -0,0 +1,58 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile
+
+# discard local symbols
+set ldflags "additional_flags=-Wl,-x"
+if [prepare_for_testing $testfile.exp $testfile $srcfile $ldflags] {
+    return -1
+}
+if ![runto test] {
+    return -1
+}
+
+# we want to see the full trace for this test
+gdb_test_no_output "set record function-call-history-size 0"
+
+# trace from one call of test to the next
+gdb_test_no_output "record btrace"
+gdb_continue_to_breakpoint "cont to test" ".*test.*"
+
+# show the flat branch trace
+gdb_test "record function-call-history 1" "
+1\t\\\?\\\?\r
+2\t\\\?\\\?\r
+3\t\\\?\\\?\r
+4\ttest\r
+5\tmain\r
+6\ttest" "unknown - flat"
+
+# show the branch trace with calls indented
+gdb_test "record function-call-history /c 1" "
+1\t    \\\?\\\?\r
+2\t      \\\?\\\?\r
+3\t    \\\?\\\?\r
+4\t  test\r
+5\tmain\r
+6\t  test" "unknown - calls indented"
diff --git a/gdb/testsuite/gdb.btrace/x86-tailcall.S b/gdb/testsuite/gdb.btrace/x86-tailcall.S
new file mode 100644
index 0000000..edf9a3b
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/x86-tailcall.S
@@ -0,0 +1,279 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2013 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <markus.t.metzger@intel.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+   This file has been generated using:
+   gcc -S -O2 -dA -g x86-tailcall.c -o x86-tailcall.S  */
+
+	.file	"x86-tailcall.c"
+	.section	.debug_abbrev,"",@progbits
+.Ldebug_abbrev0:
+	.section	.debug_info,"",@progbits
+.Ldebug_info0:
+	.section	.debug_line,"",@progbits
+.Ldebug_line0:
+	.text
+.Ltext0:
+	.p2align 4,,15
+	.type	bar, @function
+bar:
+.LFB0:
+	.file 1 "x86-tailcall.c"
+	# x86-tailcall.c:22
+	.loc 1 22 0
+	.cfi_startproc
+	# basic block 2
+	# x86-tailcall.c:24
+	.loc 1 24 0
+	movl	$42, %eax
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	bar, .-bar
+	.p2align 4,,15
+	.type	foo, @function
+foo:
+.LFB1:
+	# x86-tailcall.c:28
+	.loc 1 28 0
+	.cfi_startproc
+	# basic block 2
+	# x86-tailcall.c:29
+	.loc 1 29 0
+	jmp	bar
+	.cfi_endproc
+.LFE1:
+	.size	foo, .-foo
+	.p2align 4,,15
+.globl main
+	.type	main, @function
+main:
+.LFB2:
+	# x86-tailcall.c:34
+	.loc 1 34 0
+	.cfi_startproc
+	# basic block 2
+	# x86-tailcall.c:37
+	.loc 1 37 0
+	call	foo
+.LVL0:
+	addl	$1, %eax
+.LVL1:
+	# x86-tailcall.c:39
+	.loc 1 39 0
+	ret
+	.cfi_endproc
+.LFE2:
+	.size	main, .-main
+.Letext0:
+	.section	.debug_loc,"",@progbits
+.Ldebug_loc0:
+.LLST0:
+	.quad	.LVL0-.Ltext0	# Location list begin address (*.LLST0)
+	.quad	.LVL1-.Ltext0	# Location list end address (*.LLST0)
+	.value	0x3	# Location expression size
+	.byte	0x70	# DW_OP_breg0
+	.sleb128 1
+	.byte	0x9f	# DW_OP_stack_value
+	.quad	.LVL1-.Ltext0	# Location list begin address (*.LLST0)
+	.quad	.LFE2-.Ltext0	# Location list end address (*.LLST0)
+	.value	0x1	# Location expression size
+	.byte	0x50	# DW_OP_reg0
+	.quad	0x0	# Location list terminator begin (*.LLST0)
+	.quad	0x0	# Location list terminator end (*.LLST0)
+	.section	.debug_info
+	.long	0x9c	# Length of Compilation Unit Info
+	.value	0x3	# DWARF version number
+	.long	.Ldebug_abbrev0	# Offset Into Abbrev. Section
+	.byte	0x8	# Pointer Size (in bytes)
+	.uleb128 0x1	# (DIE (0xb) DW_TAG_compile_unit)
+	.long	.LASF0	# DW_AT_producer: "GNU C 4.4.4 20100726 (Red Hat 4.4.4-13)"
+	.byte	0x1	# DW_AT_language
+	.long	.LASF1	# DW_AT_name: "x86-tailcall.c"
+	.long	.LASF2	# DW_AT_comp_dir: ""
+	.quad	.Ltext0	# DW_AT_low_pc
+	.quad	.Letext0	# DW_AT_high_pc
+	.long	.Ldebug_line0	# DW_AT_stmt_list
+	.uleb128 0x2	# (DIE (0x2d) DW_TAG_subprogram)
+	.ascii "bar\0"	# DW_AT_name
+	.byte	0x1	# DW_AT_decl_file (x86-tailcall.c)
+	.byte	0x15	# DW_AT_decl_line
+	.byte	0x1	# DW_AT_prototyped
+	.long	0x4b	# DW_AT_type
+	.quad	.LFB0	# DW_AT_low_pc
+	.quad	.LFE0	# DW_AT_high_pc
+	.byte	0x1	# DW_AT_frame_base
+	.byte	0x9c	# DW_OP_call_frame_cfa
+	.uleb128 0x3	# (DIE (0x4b) DW_TAG_base_type)
+	.byte	0x4	# DW_AT_byte_size
+	.byte	0x5	# DW_AT_encoding
+	.ascii "int\0"	# DW_AT_name
+	.uleb128 0x2	# (DIE (0x52) DW_TAG_subprogram)
+	.ascii "foo\0"	# DW_AT_name
+	.byte	0x1	# DW_AT_decl_file (x86-tailcall.c)
+	.byte	0x1b	# DW_AT_decl_line
+	.byte	0x1	# DW_AT_prototyped
+	.long	0x4b	# DW_AT_type
+	.quad	.LFB1	# DW_AT_low_pc
+	.quad	.LFE1	# DW_AT_high_pc
+	.byte	0x1	# DW_AT_frame_base
+	.byte	0x9c	# DW_OP_call_frame_cfa
+	.uleb128 0x4	# (DIE (0x70) DW_TAG_subprogram)
+	.byte	0x1	# DW_AT_external
+	.long	.LASF3	# DW_AT_name: "main"
+	.byte	0x1	# DW_AT_decl_file (x86-tailcall.c)
+	.byte	0x21	# DW_AT_decl_line
+	.byte	0x1	# DW_AT_prototyped
+	.long	0x4b	# DW_AT_type
+	.quad	.LFB2	# DW_AT_low_pc
+	.quad	.LFE2	# DW_AT_high_pc
+	.byte	0x1	# DW_AT_frame_base
+	.byte	0x9c	# DW_OP_call_frame_cfa
+	.uleb128 0x5	# (DIE (0x8f) DW_TAG_variable)
+	.long	.LASF4	# DW_AT_name: "answer"
+	.byte	0x1	# DW_AT_decl_file (x86-tailcall.c)
+	.byte	0x23	# DW_AT_decl_line
+	.long	0x4b	# DW_AT_type
+	.long	.LLST0	# DW_AT_location
+	.byte	0x0	# end of children of DIE 0x70
+	.byte	0x0	# end of children of DIE 0xb
+	.section	.debug_abbrev
+	.uleb128 0x1	# (abbrev code)
+	.uleb128 0x11	# (TAG: DW_TAG_compile_unit)
+	.byte	0x1	# DW_children_yes
+	.uleb128 0x25	# (DW_AT_producer)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x13	# (DW_AT_language)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x1b	# (DW_AT_comp_dir)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x11	# (DW_AT_low_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x12	# (DW_AT_high_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x10	# (DW_AT_stmt_list)
+	.uleb128 0x6	# (DW_FORM_data4)
+	.byte	0x0
+	.byte	0x0
+	.uleb128 0x2	# (abbrev code)
+	.uleb128 0x2e	# (TAG: DW_TAG_subprogram)
+	.byte	0x0	# DW_children_no
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0x8	# (DW_FORM_string)
+	.uleb128 0x3a	# (DW_AT_decl_file)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3b	# (DW_AT_decl_line)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x27	# (DW_AT_prototyped)
+	.uleb128 0xc	# (DW_FORM_flag)
+	.uleb128 0x49	# (DW_AT_type)
+	.uleb128 0x13	# (DW_FORM_ref4)
+	.uleb128 0x11	# (DW_AT_low_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x12	# (DW_AT_high_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x40	# (DW_AT_frame_base)
+	.uleb128 0xa	# (DW_FORM_block1)
+	.byte	0x0
+	.byte	0x0
+	.uleb128 0x3	# (abbrev code)
+	.uleb128 0x24	# (TAG: DW_TAG_base_type)
+	.byte	0x0	# DW_children_no
+	.uleb128 0xb	# (DW_AT_byte_size)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3e	# (DW_AT_encoding)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0x8	# (DW_FORM_string)
+	.byte	0x0
+	.byte	0x0
+	.uleb128 0x4	# (abbrev code)
+	.uleb128 0x2e	# (TAG: DW_TAG_subprogram)
+	.byte	0x1	# DW_children_yes
+	.uleb128 0x3f	# (DW_AT_external)
+	.uleb128 0xc	# (DW_FORM_flag)
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x3a	# (DW_AT_decl_file)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3b	# (DW_AT_decl_line)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x27	# (DW_AT_prototyped)
+	.uleb128 0xc	# (DW_FORM_flag)
+	.uleb128 0x49	# (DW_AT_type)
+	.uleb128 0x13	# (DW_FORM_ref4)
+	.uleb128 0x11	# (DW_AT_low_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x12	# (DW_AT_high_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x40	# (DW_AT_frame_base)
+	.uleb128 0xa	# (DW_FORM_block1)
+	.byte	0x0
+	.byte	0x0
+	.uleb128 0x5	# (abbrev code)
+	.uleb128 0x34	# (TAG: DW_TAG_variable)
+	.byte	0x0	# DW_children_no
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x3a	# (DW_AT_decl_file)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3b	# (DW_AT_decl_line)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x49	# (DW_AT_type)
+	.uleb128 0x13	# (DW_FORM_ref4)
+	.uleb128 0x2	# (DW_AT_location)
+	.uleb128 0x6	# (DW_FORM_data4)
+	.byte	0x0
+	.byte	0x0
+	.byte	0x0
+	.section	.debug_pubnames,"",@progbits
+	.long	0x17	# Length of Public Names Info
+	.value	0x2	# DWARF Version
+	.long	.Ldebug_info0	# Offset of Compilation Unit Info
+	.long	0xa0	# Compilation Unit Length
+	.long	0x70	# DIE offset
+	.ascii "main\0"	# external name
+	.long	0x0
+	.section	.debug_aranges,"",@progbits
+	.long	0x2c	# Length of Address Ranges Info
+	.value	0x2	# DWARF Version
+	.long	.Ldebug_info0	# Offset of Compilation Unit Info
+	.byte	0x8	# Size of Address
+	.byte	0x0	# Size of Segment Descriptor
+	.value	0x0	# Pad to 16 byte boundary
+	.value	0x0
+	.quad	.Ltext0	# Address
+	.quad	.Letext0-.Ltext0	# Length
+	.quad	0x0
+	.quad	0x0
+	.section	.debug_str,"MS",@progbits,1
+.LASF0:
+	.string	"GNU C 4.4.4 20100726 (Red Hat 4.4.4-13)"
+.LASF3:
+	.string	"main"
+.LASF4:
+	.string	"answer"
+.LASF2:
+	.string	""
+.LASF1:
+	.string	"x86-tailcall.c"
+	.ident	"GCC: (GNU) 4.4.4 20100726 (Red Hat 4.4.4-13)"
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gdb/testsuite/gdb.btrace/x86-tailcall.c b/gdb/testsuite/gdb.btrace/x86-tailcall.c
new file mode 100644
index 0000000..9e3b183
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/x86-tailcall.c
@@ -0,0 +1,39 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2013 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <markus.t.metzger@intel.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+static __attribute__ ((noinline)) int
+bar (void)
+{
+  return 42;
+}
+
+static __attribute__ ((noinline)) int
+foo (void)
+{
+  return bar ();
+}
+
+int
+main (void)
+{
+  int answer;
+
+  answer = foo ();
+  return ++answer;
+}
-- 
1.8.3.1

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

* [patch v8 23/24] record-btrace: show trace from enable location
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (16 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 07/24] record-btrace: fix insn range in function call history Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-13 19:50   ` Pedro Alves
  2013-12-12  9:16 ` [patch v8 22/24] btrace, gdbserver: read branch trace incrementally Markus Metzger
                   ` (6 subsequent siblings)
  24 siblings, 1 reply; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

The btrace record target shows the branch trace from the location of the first
branch destination.  This is the first BTS records.

After adding incremental updates, we can now add a dummy record for the current
PC when we enable tracing so we show the trace from the location where branch
tracing has been enabled.

Approved-by: Jan Kratochvil.
2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

    * btrace.c: Include regcache.h.
    (btrace_add_pc): New.
    (btrace_enable): Call btrace_add_pc.
    (btrace_is_empty): New.
    * btrace.h (btrace_is_empty): New.
    * record-btrace.c (require_btrace, record_btrace_info): Call
    btrace_is_empty.

testsuite/
    * gdb.btrace/exception.exp: Update.
    * gdb.btrace/instruction_history.exp: Update.
    * gdb.btrace/record_goto.exp: Update.
    * gdb.btrace/tailcall.exp: Update.
    * gdb.btrace/unknown_functions.exp: Update.
    * gdb.btrace/delta.exp: New.


---
 gdb/btrace.c                                     | 51 ++++++++++++
 gdb/btrace.h                                     |  4 +
 gdb/record-btrace.c                              |  8 +-
 gdb/testsuite/gdb.btrace/delta.exp               | 63 +++++++++++++++
 gdb/testsuite/gdb.btrace/exception.exp           | 18 +++--
 gdb/testsuite/gdb.btrace/instruction_history.exp | 78 +++++++++----------
 gdb/testsuite/gdb.btrace/record_goto.exp         | 99 ++++++++++++------------
 gdb/testsuite/gdb.btrace/tailcall.exp            | 16 ++--
 gdb/testsuite/gdb.btrace/unknown_functions.exp   | 22 +++---
 9 files changed, 238 insertions(+), 121 deletions(-)
 create mode 100644 gdb/testsuite/gdb.btrace/delta.exp

diff --git a/gdb/btrace.c b/gdb/btrace.c
index b25f882..b1e87b9 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -30,6 +30,7 @@
 #include "source.h"
 #include "filenames.h"
 #include "xml-support.h"
+#include "regcache.h"
 
 /* Print a record debug message.  Use do ... while (0) to avoid ambiguities
    when used in if statements.  */
@@ -663,6 +664,32 @@ btrace_compute_ftrace (struct btrace_thread_info *btinfo,
   btinfo->level = -level;
 }
 
+/* Add an entry for the current PC.  */
+
+static void
+btrace_add_pc (struct thread_info *tp)
+{
+  VEC (btrace_block_s) *btrace;
+  struct btrace_block *block;
+  struct regcache *regcache;
+  struct cleanup *cleanup;
+  CORE_ADDR pc;
+
+  regcache = get_thread_regcache (tp->ptid);
+  pc = regcache_read_pc (regcache);
+
+  btrace = NULL;
+  cleanup = make_cleanup (VEC_cleanup (btrace_block_s), &btrace);
+
+  block = VEC_safe_push (btrace_block_s, btrace, NULL);
+  block->begin = pc;
+  block->end = pc;
+
+  btrace_compute_ftrace (&tp->btrace, btrace);
+
+  do_cleanups (cleanup);
+}
+
 /* See btrace.h.  */
 
 void
@@ -677,6 +704,11 @@ btrace_enable (struct thread_info *tp)
   DEBUG ("enable thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
 
   tp->btrace.target = target_enable_btrace (tp->ptid);
+
+  /* Add an entry for the current PC so we start tracing from where we
+     enabled it.  */
+  if (tp->btrace.target != NULL)
+    btrace_add_pc (tp);
 }
 
 /* See btrace.h.  */
@@ -1463,3 +1495,22 @@ btrace_is_replaying (struct thread_info *tp)
 {
   return tp->btrace.replay != NULL;
 }
+
+/* See btrace.h.  */
+
+int
+btrace_is_empty (struct thread_info *tp)
+{
+  struct btrace_insn_iterator begin, end;
+  struct btrace_thread_info *btinfo;
+
+  btinfo = &tp->btrace;
+
+  if (btinfo->begin == NULL)
+    return 1;
+
+  btrace_insn_begin (&begin, btinfo);
+  btrace_insn_end (&end, btinfo);
+
+  return btrace_insn_cmp (&begin, &end) == 0;
+}
diff --git a/gdb/btrace.h b/gdb/btrace.h
index 0a48253..548ebba 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -314,4 +314,8 @@ extern void btrace_set_call_history (struct btrace_thread_info *,
 /* Determine if branch tracing is currently replaying TP.  */
 extern int btrace_is_replaying (struct thread_info *tp);
 
+/* Return non-zero if the branch trace for TP is empty; zero otherwise.  */
+extern int btrace_is_empty (struct thread_info *tp);
+
+
 #endif /* BTRACE_H */
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index df55848..1e8cfc1 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -65,7 +65,6 @@ static struct thread_info *
 require_btrace_thread (void)
 {
   struct thread_info *tp;
-  struct btrace_thread_info *btinfo;
 
   DEBUG ("require");
 
@@ -75,9 +74,7 @@ require_btrace_thread (void)
 
   btrace_fetch (tp);
 
-  btinfo = &tp->btrace;
-
-  if (btinfo->begin == NULL)
+  if (btrace_is_empty (tp))
     error (_("No trace."));
 
   return tp;
@@ -239,7 +236,8 @@ record_btrace_info (void)
   calls = 0;
 
   btinfo = &tp->btrace;
-  if (btinfo->begin != NULL)
+
+  if (!btrace_is_empty (tp))
     {
       struct btrace_call_iterator call;
       struct btrace_insn_iterator insn;
diff --git a/gdb/testsuite/gdb.btrace/delta.exp b/gdb/testsuite/gdb.btrace/delta.exp
new file mode 100644
index 0000000..9ee2629
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/delta.exp
@@ -0,0 +1,63 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile x86-record_goto.S
+if [prepare_for_testing delta.exp $testfile $srcfile] {
+    return -1
+}
+if ![runto_main] {
+    return -1
+}
+
+# proceed to some sequential code
+gdb_test "next"
+
+# start tracing
+gdb_test_no_output "record btrace"
+
+# we start without trace
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 0 instructions in 0 functions for .*" "delta, 1.1"
+gdb_test "record instruction-history" "No trace\." "delta, 1.2"
+gdb_test "record function-call-history" "No trace\." "delta, 1.3"
+
+# we record each single-step, even if we have not seen a branch, yet.
+gdb_test "stepi"
+
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 1 instructions in 1 functions for .*" "delta, 3.1"
+gdb_test "record instruction-history /f 1" "
+1\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tmov *\\\$0x0,%eax\r" "delta, 3.2"
+gdb_test "record function-call-history /c 1" "
+1\tmain\r" "delta, 3.3"
+
+# make sure we don't extend the trace when we ask again.
+gdb_test "info record" "
+Active record target: record-btrace\r
+Recorded 1 instructions in 1 functions for .*" "delta, 4.1"
+gdb_test "record instruction-history /f 1" "
+1\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tmov *\\\$0x0,%eax\r" "delta, 4.2"
+gdb_test "record function-call-history /c 1" "
+1\tmain\r" "delta, 4.3"
diff --git a/gdb/testsuite/gdb.btrace/exception.exp b/gdb/testsuite/gdb.btrace/exception.exp
index 042aa97..33594b1 100755
--- a/gdb/testsuite/gdb.btrace/exception.exp
+++ b/gdb/testsuite/gdb.btrace/exception.exp
@@ -46,10 +46,11 @@ gdb_continue_to_breakpoint "cont to bp.2" ".*$srcfile:$bp_2\r\n.*"
 # show the flat branch trace
 send_gdb "record function-call-history 1\n"
 gdb_expect_list "exception - flat" "\r\n$gdb_prompt $" {"\r
-1\ttest\\(\\)\r
-2\tfoo\\(\\)\r
-3\tbar\\(\\)\r
-4\tbad\\(\\)\r" "\r
+1\tmain\\(\\)\r
+2\ttest\\(\\)\r
+3\tfoo\\(\\)\r
+4\tbar\\(\\)\r
+5\tbad\\(\\)\r" "\r
 \[0-9\]*\ttest\\(\\)"}
 
 # show the branch trace with calls indented
@@ -58,8 +59,9 @@ gdb_expect_list "exception - flat" "\r\n$gdb_prompt $" {"\r
 # two leading spaces instead of level 0 without leading spaces.
 send_gdb "record function-call-history /c 1\n"
 gdb_expect_list "exception - calls indented" "\r\n$gdb_prompt $" {"\r
-1\t  test\\(\\)\r
-2\t    foo\\(\\)\r
-3\t      bar\\(\\)\r
-4\t        bad\\(\\)\r" "\r
+1\tmain\\(\\)\r
+2\t  test\\(\\)\r
+3\t    foo\\(\\)\r
+4\t      bar\\(\\)\r
+5\t        bad\\(\\)\r" "\r
 \[0-9\]*\t  test\\(\\)"}
diff --git a/gdb/testsuite/gdb.btrace/instruction_history.exp b/gdb/testsuite/gdb.btrace/instruction_history.exp
index edf529c..90bdc3f 100644
--- a/gdb/testsuite/gdb.btrace/instruction_history.exp
+++ b/gdb/testsuite/gdb.btrace/instruction_history.exp
@@ -56,45 +56,45 @@ gdb_test_multiple "info record" $testname {
     }
 }
 
-# we have exactly 6 instructions here
-set message "exactly 6 instructions"
-if { $traced != 6 } {
+# we have exactly 11 instructions here
+set message "exactly 11 instructions"
+if { $traced != 11 } {
     fail $message
 } else {
     pass $message
 }
 
 # test that we see the expected instructions
-gdb_test "record instruction-history 2,6" "
-2\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
-3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax\r
-4\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
-5\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
-6\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
-
-gdb_test "record instruction-history /f 2,+5" "
-2\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
-3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax\r
-4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
-5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
-6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
-
-gdb_test "record instruction-history /p 6,-5" "
-2\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
-3\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax\r
-4\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
-5\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
-6\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
-
-gdb_test "record instruction-history /pf 2,6" "
-2\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
-3\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax\r
-4\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
-5\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
-6\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
-
-gdb_test "record instruction-history 2,2" "
-2\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
+gdb_test "record instruction-history 3,7" "
+3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
+4\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax\r
+5\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
+6\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
+7\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
+
+gdb_test "record instruction-history /f 3,+5" "
+3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
+4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax\r
+5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
+6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
+7\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
+
+gdb_test "record instruction-history /p 7,-5" "
+3\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
+4\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax\r
+5\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
+6\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
+7\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
+
+gdb_test "record instruction-history /pf 3,7" "
+3\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
+4\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax\r
+5\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r
+6\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax\r
+7\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
+
+gdb_test "record instruction-history 3,3" "
+3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
 
 # the following tests are checking the iterators
 # to avoid lots of regexps, we just check the number of lines that
@@ -137,7 +137,7 @@ if { $traced != $lines } {
 }
 
 # test that the iterator works
-set history_size 3
+set history_size 4
 gdb_test_no_output "set record instruction-history-size $history_size"
 set message "browse history forward start"
 set lines [test_lines_length "record instruction-history 1" $message]
@@ -147,8 +147,6 @@ if { $lines != $history_size } {
     pass $message
 }
 
-set history_size 2
-gdb_test_no_output "set record instruction-history-size $history_size"
 set message "browse history forward middle"
 set lines [test_lines_length "record instruction-history +" $message]
 if { $lines != $history_size } {
@@ -159,7 +157,7 @@ if { $lines != $history_size } {
 
 set message "browse history forward last"
 set lines [test_lines_length "record instruction-history +" $message]
-if { $lines != 1 } {
+if { $lines != 3 } {
     fail $message
 } else {
     pass $message
@@ -170,8 +168,6 @@ gdb_test "record instruction-history" "At the end of the branch trace record\\."
 # make sure we cannot move further
 gdb_test "record instruction-history" "At the end of the branch trace record\\." "browse history forward beyond 2"
 
-set history_size 3
-gdb_test_no_output "set record instruction-history-size $history_size"
 set message "browse history backward last"
 set lines [test_lines_length "record instruction-history -" $message]
 if { $lines != $history_size } {
@@ -180,8 +176,6 @@ if { $lines != $history_size } {
     pass $message
 }
 
-set history_size 2
-gdb_test_no_output "set record instruction-history-size $history_size"
 set message "browse history backward middle"
 set lines [test_lines_length "record instruction-history -" $message]
 if { $lines != $history_size } {
@@ -192,7 +186,7 @@ if { $lines != $history_size } {
 
 set message "browse history backward first"
 set lines [test_lines_length "record instruction-history -" $message]
-if { $lines != 1 } {
+if { $lines != 3 } {
     fail $message
 } else {
     pass $message
diff --git a/gdb/testsuite/gdb.btrace/record_goto.exp b/gdb/testsuite/gdb.btrace/record_goto.exp
index 27c579b..12fabda 100644
--- a/gdb/testsuite/gdb.btrace/record_goto.exp
+++ b/gdb/testsuite/gdb.btrace/record_goto.exp
@@ -50,48 +50,49 @@ gdb_test "next"
 
 # start by listing all functions
 gdb_test "record function-call-history /ci 1, +20" "
-1\t  fun4\tinst 1,3\r
-2\t    fun1\tinst 4,7\r
-3\t  fun4\tinst 8,8\r
-4\t    fun2\tinst 9,11\r
-5\t      fun1\tinst 12,15\r
-6\t    fun2\tinst 16,17\r
-7\t  fun4\tinst 18,18\r
-8\t    fun3\tinst 19,21\r
-9\t      fun1\tinst 22,25\r
-10\t    fun3\tinst 26,26\r
-11\t      fun2\tinst 27,29\r
-12\t        fun1\tinst 30,33\r
-13\t      fun2\tinst 34,35\r
-14\t    fun3\tinst 36,37\r
-15\t  fun4\tinst 38,39\r" "record_goto - list all functions"
+1\tmain\tinst 1,1\r
+2\t  fun4\tinst 2,4\r
+3\t    fun1\tinst 5,8\r
+4\t  fun4\tinst 9,9\r
+5\t    fun2\tinst 10,12\r
+6\t      fun1\tinst 13,16\r
+7\t    fun2\tinst 17,18\r
+8\t  fun4\tinst 19,19\r
+9\t    fun3\tinst 20,22\r
+10\t      fun1\tinst 23,26\r
+11\t    fun3\tinst 27,27\r
+12\t      fun2\tinst 28,30\r
+13\t        fun1\tinst 31,34\r
+14\t      fun2\tinst 35,36\r
+15\t    fun3\tinst 37,38\r
+16\t  fun4\tinst 39,40\r" "record_goto - list all functions"
 
 # let's see if we can go back in history
-gdb_test "record goto 18" "
-.*fun4 \\(\\) at record_goto.c:43.*" "record_goto - goto 18"
+gdb_test "record goto 19" "
+.*fun4 \\(\\) at record_goto.c:43.*" "record_goto - goto 19"
 
 # the function call history should start at the new location
 gdb_test "record function-call-history /ci" "
-7\t  fun4\tinst 18,18\r
-8\t    fun3\tinst 19,21\r
-9\t      fun1\tinst 22,25\r" "record_goto - function-call-history from 18 forwards"
+8\t  fun4\tinst 19,19\r
+9\t    fun3\tinst 20,22\r
+10\t      fun1\tinst 23,26\r" "record_goto - function-call-history from 19 forwards"
 
 # the instruciton history should start at the new location
 gdb_test "record instruction-history" "
-18.*\r
 19.*\r
-20.*\r" "record_goto - instruciton-history from 18 forwards"
+20.*\r
+21.*\r" "record_goto - instruciton-history from 19 forwards"
 
 # let's go to another place in the history
-gdb_test "record goto 26" "
-.*fun3 \\(\\) at record_goto.c:35.*" "record_goto - goto 26"
+gdb_test "record goto 27" "
+.*fun3 \\(\\) at record_goto.c:35.*" "record_goto - goto 27"
 
 # check the back trace at that location
 gdb_test "backtrace" "
 #0.*fun3.*at record_goto.c:35.*\r
 #1.*fun4.*at record_goto.c:44.*\r
 #2.*main.*at record_goto.c:50.*\r
-Backtrace stopped: not enough registers or memory available to unwind further" "backtrace at 25"
+Backtrace stopped: not enough registers or memory available to unwind further" "backtrace at 27"
 
 # walk the backtrace
 gdb_test "up" "
@@ -101,25 +102,25 @@ gdb_test "up" "
 
 # the function call history should start at the new location
 gdb_test "record function-call-history /ci -" "
-8\t    fun3\tinst 19,21\r
-9\t      fun1\tinst 22,25\r
-10\t    fun3\tinst 26,26\r" "record_goto - function-call-history from 26 backwards"
+9\t    fun3\tinst 20,22\r
+10\t      fun1\tinst 23,26\r
+11\t    fun3\tinst 27,27\r" "record_goto - function-call-history from 27 backwards"
 
 # the instruciton history should start at the new location
 gdb_test "record instruction-history -" "
-24.*\r
 25.*\r
-26.*\r" "record_goto - instruciton-history from 26 backwards"
+26.*\r
+27.*\r" "record_goto - instruciton-history from 27 backwards"
 
 # test that we can go to the begin of the trace
 gdb_test "record goto begin" "
-.*fun4 \\(\\) at record_goto.c:40.*" "record_goto - goto begin"
+.*main \\(\\) at record_goto.c:49.*" "record_goto - goto begin"
 
 # check that we're filling up the context correctly
 gdb_test "record function-call-history /ci -" "
-1\t  fun4\tinst 1,3\r
-2\t    fun1\tinst 4,7\r
-3\t  fun4\tinst 8,8\r" "record_goto - function-call-history from begin backwards"
+1\tmain\tinst 1,1\r
+2\t  fun4\tinst 2,4\r
+3\t    fun1\tinst 5,8\r" "record_goto - function-call-history from begin backwards"
 
 # check that we're filling up the context correctly
 gdb_test "record instruction-history -" "
@@ -133,9 +134,9 @@ gdb_test "record goto 2" "
 
 # check that we're filling up the context correctly
 gdb_test "record function-call-history /ci -" "
-1\t  fun4\tinst 1,3\r
-2\t    fun1\tinst 4,7\r
-3\t  fun4\tinst 8,8\r" "record_goto - function-call-history from 2 backwards"
+1\tmain\tinst 1,1\r
+2\t  fun4\tinst 2,4\r
+3\t    fun1\tinst 5,8\r" "record_goto - function-call-history from 2 backwards"
 
 # check that we're filling up the context correctly
 gdb_test "record instruction-history -" "
@@ -149,28 +150,28 @@ gdb_test "record goto end" "
 
 # check that we're filling up the context correctly
 gdb_test "record function-call-history /ci" "
-13\t      fun2\tinst 34,35\r
-14\t    fun3\tinst 36,37\r
-15\t  fun4\tinst 38,39\r" "record_goto - function-call-history from end forwards"
+14\t      fun2\tinst 35,36\r
+15\t    fun3\tinst 37,38\r
+16\t  fun4\tinst 39,40\r" "record_goto - function-call-history from end forwards"
 
 # check that we're filling up the context correctly
 gdb_test "record instruction-history" "
-37.*\r
 38.*\r
-39.*\r" "record_goto - instruciton-history from end forwards"
+39.*\r
+40.*\r" "record_goto - instruciton-history from end forwards"
 
 # we should get the exact same history from the second to last instruction
-gdb_test "record goto 38" "
-.*fun4 \\(\\) at record_goto.c:44.*" "record_goto - goto 38"
+gdb_test "record goto 39" "
+.*fun4 \\(\\) at record_goto.c:44.*" "record_goto - goto 39"
 
 # check that we're filling up the context correctly
 gdb_test "record function-call-history /ci" "
-13\t      fun2\tinst 34,35\r
-14\t    fun3\tinst 36,37\r
-15\t  fun4\tinst 38,39\r" "record_goto - function-call-history from 38 forwards"
+14\t      fun2\tinst 35,36\r
+15\t    fun3\tinst 37,38\r
+16\t  fun4\tinst 39,40\r" "record_goto - function-call-history from 39 forwards"
 
 # check that we're filling up the context correctly
 gdb_test "record instruction-history" "
-37.*\r
 38.*\r
-39.*\r" "record_goto - instruciton-history from 38 forwards"
+39.*\r
+40.*\r" "record_goto - instruciton-history from 39 forwards"
diff --git a/gdb/testsuite/gdb.btrace/tailcall.exp b/gdb/testsuite/gdb.btrace/tailcall.exp
index 900d731..b9520c4 100644
--- a/gdb/testsuite/gdb.btrace/tailcall.exp
+++ b/gdb/testsuite/gdb.btrace/tailcall.exp
@@ -49,18 +49,20 @@ gdb_test "next"
 
 # show the flat branch trace
 gdb_test "record function-call-history 1" "
-1\tfoo\r
-2\tbar\r
-3\tmain" "tailcall - flat"
+1\tmain\r
+2\tfoo\r
+3\tbar\r
+4\tmain" "tailcall - flat"
 
 # show the branch trace with calls indented
 gdb_test "record function-call-history /c 1" "
-1\t  foo\r
-2\t    bar\r
-3\tmain" "tailcall - calls indented"
+1\tmain\r
+2\t  foo\r
+3\t    bar\r
+4\tmain" "tailcall - calls indented"
 
 # go into bar
-gdb_test "record goto 3" "
+gdb_test "record goto 4" "
 .*bar \\(\\) at .*x86-tailcall.c:24\r\n.*" "go to bar"
 
 # check the backtrace
diff --git a/gdb/testsuite/gdb.btrace/unknown_functions.exp b/gdb/testsuite/gdb.btrace/unknown_functions.exp
index 0c1739f..eee039b 100644
--- a/gdb/testsuite/gdb.btrace/unknown_functions.exp
+++ b/gdb/testsuite/gdb.btrace/unknown_functions.exp
@@ -41,18 +41,20 @@ gdb_continue_to_breakpoint "cont to test" ".*test.*"
 
 # show the flat branch trace
 gdb_test "record function-call-history 1" "
-1\t\\\?\\\?\r
+1\ttest\r
 2\t\\\?\\\?\r
 3\t\\\?\\\?\r
-4\ttest\r
-5\tmain\r
-6\ttest" "unknown - flat"
+4\t\\\?\\\?\r
+5\ttest\r
+6\tmain\r
+7\ttest" "unknown - flat"
 
 # show the branch trace with calls indented
 gdb_test "record function-call-history /c 1" "
-1\t    \\\?\\\?\r
-2\t      \\\?\\\?\r
-3\t    \\\?\\\?\r
-4\t  test\r
-5\tmain\r
-6\t  test" "unknown - calls indented"
+1\t  test\r
+2\t    \\\?\\\?\r
+3\t      \\\?\\\?\r
+4\t    \\\?\\\?\r
+5\t  test\r
+6\tmain\r
+7\t  test" "unknown - calls indented"
-- 
1.8.3.1

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

* [patch v8 07/24] record-btrace: fix insn range in function call history
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (15 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 15/24] frame, backtrace: allow targets to supply a frame unwinder Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-12  9:16 ` [patch v8 23/24] record-btrace: show trace from enable location Markus Metzger
                   ` (7 subsequent siblings)
  24 siblings, 0 replies; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

With the "/i" modifier, we print the instruction number range in the
"record function-call-history" command as [begin, end).

It would be more intuitive if we printed the range as [begin, end].

2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

	* record-btrace.c (btrace_call_history_insn_range): Print
	insn range as [begin, end].


---
 gdb/record-btrace.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 248dc2e..0888c6b 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -425,10 +425,13 @@ static void
 btrace_call_history_insn_range (struct ui_out *uiout,
 				const struct btrace_function *bfun)
 {
-  unsigned int begin, end;
+  unsigned int begin, end, size;
+
+  size = VEC_length (btrace_insn_s, bfun->insn);
+  gdb_assert (size > 0);
 
   begin = bfun->insn_offset;
-  end = begin + VEC_length (btrace_insn_s, bfun->insn);
+  end = begin + size - 1;
 
   ui_out_field_uint (uiout, "insn begin", begin);
   ui_out_text (uiout, "-");
-- 
1.8.3.1

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

* [patch v8 21/24] record-btrace: extend unwinder
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (9 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 08/24] record-btrace: start counting at one Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-13 19:45   ` Pedro Alves
  2013-12-12  9:16 ` [patch v8 20/24] record-btrace: add record goto target methods Markus Metzger
                   ` (13 subsequent siblings)
  24 siblings, 1 reply; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

Extend the always failing unwinder to provide the PC based on the call structure
detected in the branch trace.

The unwinder supports normal frames and tailcall frames.
Inline frames are not supported.

Reviewed-by: Eli Zaretskii

2013-04-24  Markus Metzger  <markus.t.metzger@intel.com>

	* frame.h (enum frame_type) <BTRACE_FRAME>: New.
	<BTRACE_TAILCALL_FRAME>: New.
	* frame. (frame_is_tailcall): Consider BTRACE_TAILCALL_FRAME.
	(fprint_frame_type): Support new frame types.
	* record.h (record_btrace_frame_unwind,
	record_btrace_tailcall_frame_unwind): New declarations.
	* dwarf2-frame: Include record.h
	(dwarf2_frame_cfa): Throw an error for btrace frames.
	* record-btrace.c: Include hashtab.h.
	(btrace_get_bfun_name): New.
	(btrace_call_history): Call btrace_get_bfun_name.
	(struct btrace_frame_cache): New.
	(bfcache): New.
	(bfcache_hash, bfcache_eq, bfcache_new): New.
	(btrace_get_frame_function): New.
	(record_btrace_frame_unwind_stop_reason): Allow unwinding.
	(record_btrace_frame_this_id): Compute own id.
	(record_btrace_frame_prev_register): Provide PC, throw_error
	for all other registers.
	(record_btrace_frame_sniffer): Detect btrace frames.
	(record_btrace_tailcall_frame_sniffer): New.
	(record_btrace_frame_dealloc_cache): New.
	(record_btrace_frame_unwind): Add new functions.
	(record_btrace_tailcall_frame_unwind): New.
	(_initialize_record_btrace): Allocate cache.
	* btrace.c (btrace_clear): Call reinit_frame_cache.
	* NEWS: Announce it.

testsuite/
	* gdb.btrace/record_goto.exp: Add backtrace test.
	* gdb.btrace/tailcall.exp: Add backtrace test.


---
 gdb/NEWS                                 |   2 +
 gdb/btrace.c                             |   4 +
 gdb/dwarf2-frame.c                       |   6 +
 gdb/frame.c                              |   8 +-
 gdb/frame.h                              |   7 +-
 gdb/record-btrace.c                      | 291 +++++++++++++++++++++++++++++--
 gdb/record.h                             |   4 +
 gdb/testsuite/gdb.btrace/record_goto.exp |  13 ++
 gdb/testsuite/gdb.btrace/tailcall.exp    |  17 ++
 9 files changed, 338 insertions(+), 14 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index b932c06..e0ac447 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -92,6 +92,8 @@ info exceptions REGEXP
   are listed.
 
 * The btrace record target now supports the 'record goto' command.
+  For locations inside the execution trace, the back trace is computed
+  based on the information stored in the execution trace.
 
 * New options
 
diff --git a/gdb/btrace.c b/gdb/btrace.c
index ad15481..1f35796 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -754,6 +754,10 @@ btrace_clear (struct thread_info *tp)
 
   DEBUG ("clear thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
 
+  /* Make sure btrace frames that may hold a pointer into the branch
+     trace data are destroyed.  */
+  reinit_frame_cache ();
+
   btinfo = &tp->btrace;
 
   it = btinfo->begin;
diff --git a/gdb/dwarf2-frame.c b/gdb/dwarf2-frame.c
index 4efa7d8..9617c12 100644
--- a/gdb/dwarf2-frame.c
+++ b/gdb/dwarf2-frame.c
@@ -31,6 +31,7 @@
 #include "objfiles.h"
 #include "regcache.h"
 #include "value.h"
+#include "record.h"
 
 #include "gdb_assert.h"
 #include <string.h>
@@ -1511,6 +1512,11 @@ dwarf2_frame_base_sniffer (struct frame_info *this_frame)
 CORE_ADDR
 dwarf2_frame_cfa (struct frame_info *this_frame)
 {
+  if (frame_unwinder_is (this_frame, &record_btrace_tailcall_frame_unwind)
+      || frame_unwinder_is (this_frame, &record_btrace_frame_unwind))
+    throw_error (NOT_AVAILABLE_ERROR,
+		 _("cfa not available for record btrace target"));
+
   while (get_frame_type (this_frame) == INLINE_FRAME)
     this_frame = get_prev_frame (this_frame);
   if (get_frame_unwind_stop_reason (this_frame) == UNWIND_UNAVAILABLE)
diff --git a/gdb/frame.c b/gdb/frame.c
index 3f2aabb..8745117 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -353,6 +353,12 @@ fprint_frame_type (struct ui_file *file, enum frame_type type)
     case SENTINEL_FRAME:
       fprintf_unfiltered (file, "SENTINEL_FRAME");
       return;
+    case BTRACE_FRAME:
+      fprintf_unfiltered (file, "BTRACE_FRAME");
+      return;
+    case BTRACE_TAILCALL_FRAME:
+      fprintf_unfiltered (file, "BTRACE_TAILCALL_FRAME");
+      return;
     default:
       fprintf_unfiltered (file, "<unknown type>");
       return;
@@ -2354,7 +2360,7 @@ frame_is_tailcall (struct frame_info *fi)
   enum frame_type type;
 
   type = get_frame_type (fi);
-  return type == TAILCALL_FRAME;
+  return type == TAILCALL_FRAME || type == BTRACE_TAILCALL_FRAME;
 }
 
 /* Level of the selected frame: 0 for innermost, 1 for its caller, ...
diff --git a/gdb/frame.h b/gdb/frame.h
index 71f07dd..b20b69f 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -222,7 +222,11 @@ enum frame_type
   ARCH_FRAME,
   /* Sentinel or registers frame.  This frame obtains register values
      direct from the inferior's registers.  */
-  SENTINEL_FRAME
+  SENTINEL_FRAME,
+  /* A branch tracing frame.  */
+  BTRACE_FRAME,
+  /* A branch tracing tail call frame.  */
+  BTRACE_TAILCALL_FRAME
 };
 
 /* For every stopped thread, GDB tracks two frames: current and
@@ -775,5 +779,4 @@ extern int frame_unwinder_is (struct frame_info *fi,
 
 extern int frame_is_tailcall (struct frame_info *fi);
 
-
 #endif /* !defined (FRAME_H)  */
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index d78c7a0..df55848 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -34,6 +34,7 @@
 #include "filenames.h"
 #include "regcache.h"
 #include "frame-unwind.h"
+#include "hashtab.h"
 
 /* The target_ops of record-btrace.  */
 static struct target_ops record_btrace_ops;
@@ -522,6 +523,28 @@ btrace_call_history_src_line (struct ui_out *uiout,
   ui_out_field_int (uiout, "max line", end);
 }
 
+/* Get the name of a branch trace function.  */
+
+static const char *
+btrace_get_bfun_name (const struct btrace_function *bfun)
+{
+  struct minimal_symbol *msym;
+  struct symbol *sym;
+
+  if (bfun == NULL)
+    return "??";
+
+  msym = bfun->msym;
+  sym = bfun->sym;
+
+  if (sym != NULL)
+    return SYMBOL_PRINT_NAME (sym);
+  else if (msym != NULL)
+    return SYMBOL_PRINT_NAME (msym);
+  else
+    return "??";
+}
+
 /* Disassemble a section of the recorded function trace.  */
 
 static void
@@ -543,8 +566,8 @@ btrace_call_history (struct ui_out *uiout,
       struct symbol *sym;
 
       bfun = btrace_call_get (&it);
-      msym = bfun->msym;
       sym = bfun->sym;
+      msym = bfun->msym;
 
       /* Print the function index.  */
       ui_out_field_uint (uiout, "index", bfun->number);
@@ -907,13 +930,100 @@ record_btrace_prepare_to_store (struct target_ops *ops,
       }
 }
 
+/* The branch trace frame cache.  */
+
+struct btrace_frame_cache
+{
+  /* The thread.  */
+  struct thread_info *tp;
+
+  /* The frame info.  */
+  struct frame_info *frame;
+
+  /* The branch trace function segment.  */
+  const struct btrace_function *bfun;
+};
+
+/* A struct btrace_frame_cache hash table indexed by NEXT.  */
+
+static htab_t bfcache;
+
+/* hash_f for htab_create_alloc of bfcache.  */
+
+static hashval_t
+bfcache_hash (const void *arg)
+{
+  const struct btrace_frame_cache *cache = arg;
+
+  return htab_hash_pointer (cache->frame);
+}
+
+/* eq_f for htab_create_alloc of bfcache.  */
+
+static int
+bfcache_eq (const void *arg1, const void *arg2)
+{
+  const struct btrace_frame_cache *cache1 = arg1;
+  const struct btrace_frame_cache *cache2 = arg2;
+
+  return cache1->frame == cache2->frame;
+}
+
+/* Create a new btrace frame cache.  */
+
+static struct btrace_frame_cache *
+bfcache_new (struct frame_info *frame)
+{
+  struct btrace_frame_cache *cache;
+  void **slot;
+
+  cache = FRAME_OBSTACK_ZALLOC (struct btrace_frame_cache);
+  cache->frame = frame;
+
+  slot = htab_find_slot (bfcache, cache, INSERT);
+  gdb_assert (*slot == NULL);
+  *slot = cache;
+
+  return cache;
+}
+
+/* Extract the branch trace function from a branch trace frame.  */
+
+static const struct btrace_function *
+btrace_get_frame_function (struct frame_info *frame)
+{
+  const struct btrace_frame_cache *cache;
+  const struct btrace_function *bfun;
+  struct btrace_frame_cache pattern;
+  void **slot;
+
+  pattern.frame = frame;
+
+  slot = htab_find_slot (bfcache, &pattern, NO_INSERT);
+  if (slot == NULL)
+    return NULL;
+
+  cache = *slot;
+  return cache->bfun;
+}
+
 /* Implement stop_reason method for record_btrace_frame_unwind.  */
 
 static enum unwind_stop_reason
 record_btrace_frame_unwind_stop_reason (struct frame_info *this_frame,
 					void **this_cache)
 {
-  return UNWIND_UNAVAILABLE;
+  const struct btrace_frame_cache *cache;
+  const struct btrace_function *bfun;
+
+  cache = *this_cache;
+  bfun = cache->bfun;
+  gdb_assert (bfun != NULL);
+
+  if (bfun->up == NULL)
+    return UNWIND_UNAVAILABLE;
+
+  return UNWIND_NO_REASON;
 }
 
 /* Implement this_id method for record_btrace_frame_unwind.  */
@@ -922,7 +1032,27 @@ static void
 record_btrace_frame_this_id (struct frame_info *this_frame, void **this_cache,
 			     struct frame_id *this_id)
 {
-  /* Leave there the outer_frame_id value.  */
+  const struct btrace_frame_cache *cache;
+  const struct btrace_function *bfun;
+  CORE_ADDR code, special;
+
+  cache = *this_cache;
+
+  bfun = cache->bfun;
+  gdb_assert (bfun != NULL);
+
+  while (bfun->segment.prev != NULL)
+    bfun = bfun->segment.prev;
+
+  code = get_frame_func (this_frame);
+  special = bfun->number;
+
+  *this_id = frame_id_build_artificial (code, special);
+
+  DEBUG ("[frame] %s id: (!stack, pc=%s, special=%s)",
+	 btrace_get_bfun_name (cache->bfun),
+	 core_addr_to_string_nz (this_id->code_addr),
+	 core_addr_to_string_nz (this_id->special_addr));
 }
 
 /* Implement prev_register method for record_btrace_frame_unwind.  */
@@ -932,8 +1062,47 @@ record_btrace_frame_prev_register (struct frame_info *this_frame,
 				   void **this_cache,
 				   int regnum)
 {
-  throw_error (NOT_AVAILABLE_ERROR,
-              _("Registers are not available in btrace record history"));
+  const struct btrace_frame_cache *cache;
+  const struct btrace_function *bfun, *caller;
+  const struct btrace_insn *insn;
+  struct gdbarch *gdbarch;
+  CORE_ADDR pc;
+  int pcreg;
+
+  gdbarch = get_frame_arch (this_frame);
+  pcreg = gdbarch_pc_regnum (gdbarch);
+  if (pcreg < 0 || regnum != pcreg)
+    throw_error (NOT_AVAILABLE_ERROR,
+		 _("Registers are not available in btrace record history"));
+
+  cache = *this_cache;
+  bfun = cache->bfun;
+  gdb_assert (bfun != NULL);
+
+  caller = bfun->up;
+  if (caller == NULL)
+    throw_error (NOT_AVAILABLE_ERROR,
+		 _("No caller in btrace record history"));
+
+  if ((bfun->flags & BFUN_UP_LINKS_TO_RET) != 0)
+    {
+      insn = VEC_index (btrace_insn_s, caller->insn, 0);
+      pc = insn->pc;
+    }
+  else
+    {
+      insn = VEC_last (btrace_insn_s, caller->insn);
+      pc = insn->pc;
+
+      if ((bfun->flags & BFUN_UP_LINKS_TO_TAILCALL) == 0)
+	pc += gdb_insn_length (gdbarch, pc);
+    }
+
+  DEBUG ("[frame] unwound PC in %s on level %d: %s",
+	 btrace_get_bfun_name (bfun), bfun->level,
+	 core_addr_to_string_nz (pc));
+
+  return frame_unwind_got_address (this_frame, regnum, pc);
 }
 
 /* Implement sniffer method for record_btrace_frame_unwind.  */
@@ -943,15 +1112,99 @@ record_btrace_frame_sniffer (const struct frame_unwind *self,
 			     struct frame_info *this_frame,
 			     void **this_cache)
 {
+  const struct btrace_function *bfun;
+  struct btrace_frame_cache *cache;
   struct thread_info *tp;
-  struct btrace_thread_info *btinfo;
-  struct btrace_insn_iterator *replay;
+  struct frame_info *next;
 
   /* THIS_FRAME does not contain a reference to its thread.  */
   tp = find_thread_ptid (inferior_ptid);
   gdb_assert (tp != NULL);
 
-  return btrace_is_replaying (tp);
+  bfun = NULL;
+  next = get_next_frame (this_frame);
+  if (next == NULL)
+    {
+      const struct btrace_insn_iterator *replay;
+
+      replay = tp->btrace.replay;
+      if (replay != NULL)
+	bfun = replay->function;
+    }
+  else
+    {
+      const struct btrace_function *callee;
+
+      callee = btrace_get_frame_function (next);
+      if (callee != NULL && (callee->flags & BFUN_UP_LINKS_TO_TAILCALL) == 0)
+	bfun = callee->up;
+    }
+
+  if (bfun == NULL)
+    return 0;
+
+  DEBUG ("[frame] sniffed frame for %s on level %d",
+	 btrace_get_bfun_name (bfun), bfun->level);
+
+  /* This is our frame.  Initialize the frame cache.  */
+  cache = bfcache_new (this_frame);
+  cache->tp = tp;
+  cache->bfun = bfun;
+
+  *this_cache = cache;
+  return 1;
+}
+
+/* Implement sniffer method for record_btrace_tailcall_frame_unwind.  */
+
+static int
+record_btrace_tailcall_frame_sniffer (const struct frame_unwind *self,
+				      struct frame_info *this_frame,
+				      void **this_cache)
+{
+  const struct btrace_function *bfun, *callee;
+  struct btrace_frame_cache *cache;
+  struct frame_info *next;
+
+  next = get_next_frame (this_frame);
+  if (next == NULL)
+    return 0;
+
+  callee = btrace_get_frame_function (next);
+  if (callee == NULL)
+    return 0;
+
+  if ((callee->flags & BFUN_UP_LINKS_TO_TAILCALL) == 0)
+    return 0;
+
+  bfun = callee->up;
+  if (bfun == NULL)
+    return 0;
+
+  DEBUG ("[frame] sniffed tailcall frame for %s on level %d",
+	 btrace_get_bfun_name (bfun), bfun->level);
+
+  /* This is our frame.  Initialize the frame cache.  */
+  cache = bfcache_new (this_frame);
+  cache->tp = find_thread_ptid (inferior_ptid);
+  cache->bfun = bfun;
+
+  *this_cache = cache;
+  return 1;
+}
+
+static void
+record_btrace_frame_dealloc_cache (struct frame_info *self, void *this_cache)
+{
+  struct btrace_frame_cache *cache;
+  void **slot;
+
+  cache = this_cache;
+
+  slot = htab_find_slot (bfcache, cache, NO_INSERT);
+  gdb_assert (slot != NULL);
+
+  htab_remove_elt (bfcache, cache);
 }
 
 /* btrace recording does not store previous memory content, neither the stack
@@ -960,14 +1213,26 @@ record_btrace_frame_sniffer (const struct frame_unwind *self,
    Therefore this unwinder reports any possibly unwound registers as
    <unavailable>.  */
 
-static const struct frame_unwind record_btrace_frame_unwind =
+const struct frame_unwind record_btrace_frame_unwind =
 {
-  NORMAL_FRAME,
+  BTRACE_FRAME,
   record_btrace_frame_unwind_stop_reason,
   record_btrace_frame_this_id,
   record_btrace_frame_prev_register,
   NULL,
-  record_btrace_frame_sniffer
+  record_btrace_frame_sniffer,
+  record_btrace_frame_dealloc_cache
+};
+
+const struct frame_unwind record_btrace_tailcall_frame_unwind =
+{
+  BTRACE_TAILCALL_FRAME,
+  record_btrace_frame_unwind_stop_reason,
+  record_btrace_frame_this_id,
+  record_btrace_frame_prev_register,
+  NULL,
+  record_btrace_tailcall_frame_sniffer,
+  record_btrace_frame_dealloc_cache
 };
 
 /* The to_resume method of target record-btrace.  */
@@ -1155,6 +1420,7 @@ init_record_btrace_ops (void)
   ops->to_store_registers = record_btrace_store_registers;
   ops->to_prepare_to_store = record_btrace_prepare_to_store;
   ops->to_get_unwinder = &record_btrace_frame_unwind;
+  ops->to_get_tailcall_unwinder = &record_btrace_tailcall_frame_unwind;
   ops->to_resume = record_btrace_resume;
   ops->to_wait = record_btrace_wait;
   ops->to_find_new_threads = record_btrace_find_new_threads;
@@ -1190,4 +1456,7 @@ _initialize_record_btrace (void)
 
   init_record_btrace_ops ();
   add_target (&record_btrace_ops);
+
+  bfcache = htab_create_alloc (50, bfcache_hash, bfcache_eq, NULL,
+			       xcalloc, xfree);
 }
diff --git a/gdb/record.h b/gdb/record.h
index 323b8d1..f670ca5 100644
--- a/gdb/record.h
+++ b/gdb/record.h
@@ -32,6 +32,10 @@ extern struct cmd_list_element *set_record_cmdlist;
 extern struct cmd_list_element *show_record_cmdlist;
 extern struct cmd_list_element *info_record_cmdlist;
 
+/* Unwinders for some record targets.  */
+extern const struct frame_unwind record_btrace_frame_unwind;
+extern const struct frame_unwind record_btrace_tailcall_frame_unwind;
+
 /* A list of flags specifying what record target methods should print.  */
 enum record_print_flag
 {
diff --git a/gdb/testsuite/gdb.btrace/record_goto.exp b/gdb/testsuite/gdb.btrace/record_goto.exp
index ce27392..27c579b 100644
--- a/gdb/testsuite/gdb.btrace/record_goto.exp
+++ b/gdb/testsuite/gdb.btrace/record_goto.exp
@@ -86,6 +86,19 @@ gdb_test "record instruction-history" "
 gdb_test "record goto 26" "
 .*fun3 \\(\\) at record_goto.c:35.*" "record_goto - goto 26"
 
+# check the back trace at that location
+gdb_test "backtrace" "
+#0.*fun3.*at record_goto.c:35.*\r
+#1.*fun4.*at record_goto.c:44.*\r
+#2.*main.*at record_goto.c:50.*\r
+Backtrace stopped: not enough registers or memory available to unwind further" "backtrace at 25"
+
+# walk the backtrace
+gdb_test "up" "
+.*fun4.*at record_goto.c:44.*" "up to fun4"
+gdb_test "up" "
+.*main.*at record_goto.c:50.*" "up to main"
+
 # the function call history should start at the new location
 gdb_test "record function-call-history /ci -" "
 8\t    fun3\tinst 19,21\r
diff --git a/gdb/testsuite/gdb.btrace/tailcall.exp b/gdb/testsuite/gdb.btrace/tailcall.exp
index 74c1ab7..900d731 100644
--- a/gdb/testsuite/gdb.btrace/tailcall.exp
+++ b/gdb/testsuite/gdb.btrace/tailcall.exp
@@ -58,3 +58,20 @@ gdb_test "record function-call-history /c 1" "
 1\t  foo\r
 2\t    bar\r
 3\tmain" "tailcall - calls indented"
+
+# go into bar
+gdb_test "record goto 3" "
+.*bar \\(\\) at .*x86-tailcall.c:24\r\n.*" "go to bar"
+
+# check the backtrace
+gdb_test "backtrace" "
+#0.*bar \\(\\) at x86-tailcall.c:24\r
+#1.*foo \\(\\) at x86-tailcall.c:29\r
+#2.*main \\(\\) at x86-tailcall.c:37\r
+Backtrace stopped: not enough registers or memory available to unwind further" "backtrace in bar"
+
+# walk the backtrace
+gdb_test "up" "
+#1\[^\r\n\]*foo \\(\\) at x86-tailcall.c:29\r\n.*" "up to foo"
+gdb_test "up" "
+#2\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*" "up to main"
-- 
1.8.3.1

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

* [patch v8 19/24] record-btrace: provide target_find_new_threads method
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (19 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 17/24] record-btrace: provide xfer_partial target method Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-12  9:16 ` [patch v8 04/24] frame: add frame_is_tailcall function Markus Metzger
                   ` (3 subsequent siblings)
  24 siblings, 0 replies; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

The "info threads" command tries to read memory, which is not possible during
replay.  This results in an error message and aborts the command without showing
the existing threads.

Provide a to_find_new_threads target method to skip the search while replaying.

Approved-by: Jan Kratochvil
2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

	* record-btrace.c (record_btrace_find_new_threads): New.
	(init_record_btrace_ops): Initialize to_find_new_threads.


---
 gdb/record-btrace.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index f2aa4fa..e8cd33a 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -992,6 +992,24 @@ record_btrace_wait (struct target_ops *ops, ptid_t ptid,
   error (_("You can't do this from here.  Do 'record goto end', first."));
 }
 
+/* The to_find_new_threads method of target record-btrace.  */
+
+static void
+record_btrace_find_new_threads (struct target_ops *ops)
+{
+  /* Don't expect new threads if we're replaying.  */
+  if (record_btrace_is_replaying ())
+    return;
+
+  /* Forward the request.  */
+  for (ops = ops->beneath; ops != NULL; ops = ops->beneath)
+    if (ops->to_find_new_threads != NULL)
+      {
+	ops->to_find_new_threads (ops);
+	break;
+      }
+}
+
 /* Initialize the record-btrace target ops.  */
 
 static void
@@ -1026,6 +1044,7 @@ init_record_btrace_ops (void)
   ops->to_get_unwinder = &record_btrace_frame_unwind;
   ops->to_resume = record_btrace_resume;
   ops->to_wait = record_btrace_wait;
+  ops->to_find_new_threads = record_btrace_find_new_threads;
   ops->to_stratum = record_stratum;
   ops->to_magic = OPS_MAGIC;
 }
-- 
1.8.3.1

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

* [patch v8 09/24] btrace: increase buffer size
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (7 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 02/24] btrace: uppercase btrace_read_type Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-12  9:16 ` [patch v8 08/24] record-btrace: start counting at one Markus Metzger
                   ` (15 subsequent siblings)
  24 siblings, 0 replies; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

Try to allocate as much buffer as we can for each thread with a maximum
of 64KB.

2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

	* common/linux-btrace.c (linux_enable_btrace): Enlarge buffer.


---
 gdb/common/linux-btrace.c | 25 +++++++++++++++----------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/gdb/common/linux-btrace.c b/gdb/common/linux-btrace.c
index aa2b0ae..801505b 100644
--- a/gdb/common/linux-btrace.c
+++ b/gdb/common/linux-btrace.c
@@ -423,7 +423,7 @@ struct btrace_target_info *
 linux_enable_btrace (ptid_t ptid)
 {
   struct btrace_target_info *tinfo;
-  int pid;
+  int pid, pg;
 
   tinfo = xzalloc (sizeof (*tinfo));
   tinfo->ptid = ptid;
@@ -451,17 +451,22 @@ linux_enable_btrace (ptid_t ptid)
   if (tinfo->file < 0)
     goto err;
 
-  /* We hard-code the trace buffer size.
-     At some later time, we should make this configurable.  */
-  tinfo->size = 1;
-  tinfo->buffer = mmap (NULL, perf_event_mmap_size (tinfo),
-			PROT_READ, MAP_SHARED, tinfo->file, 0);
-  if (tinfo->buffer == MAP_FAILED)
-    goto err_file;
+  /* We try to allocate as much buffer as we can get.
+     We could allow the user to specify the size of the buffer, but then
+     we'd leave this search for the maximum buffer size to him.  */
+  for (pg = 4; pg >= 0; --pg)
+    {
+      /* The number of pages we request needs to be a power of two.  */
+      tinfo->size = 1 << pg;
+      tinfo->buffer = mmap (NULL, perf_event_mmap_size (tinfo),
+			    PROT_READ, MAP_SHARED, tinfo->file, 0);
+      if (tinfo->buffer == MAP_FAILED)
+	continue;
 
-  return tinfo;
+      return tinfo;
+    }
 
- err_file:
+  /* We were not able to allocate any buffer.  */
   close (tinfo->file);
 
  err:
-- 
1.8.3.1

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

* [patch v8 02/24] btrace: uppercase btrace_read_type
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (6 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 10/24] record-btrace: optionally indent function call history Markus Metzger
@ 2013-12-12  9:16 ` Markus Metzger
  2013-12-12  9:16 ` [patch v8 09/24] btrace: increase buffer size Markus Metzger
                   ` (16 subsequent siblings)
  24 siblings, 0 replies; 73+ messages in thread
From: Markus Metzger @ 2013-12-12  9:16 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches, Pedro Alves

CC:  Pedro Alves  <palves@redhat.com>

2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>

	* common/btrace-common.h (btrace_read_type) <btrace_read_all>:
	Change to ...
	(btrace_read_type) <BTRACE_READ_ALL>: ... this.  Update users.
	(btrace_read_type) <btrace_read_new>: Change to ...
	(btrace_read_type) <BTRACE_READ_NEW>: ... this.  Update users.


---
 gdb/btrace.c               | 2 +-
 gdb/common/btrace-common.h | 4 ++--
 gdb/common/linux-btrace.c  | 2 +-
 gdb/gdbserver/server.c     | 4 ++--
 gdb/remote.c               | 4 ++--
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/gdb/btrace.c b/gdb/btrace.c
index 3230a3e..46e73db 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -401,7 +401,7 @@ btrace_fetch (struct thread_info *tp)
   if (btinfo->target == NULL)
     return;
 
-  btrace = target_read_btrace (btinfo->target, btrace_read_new);
+  btrace = target_read_btrace (btinfo->target, BTRACE_READ_NEW);
   if (VEC_empty (btrace_block_s, btrace))
     return;
 
diff --git a/gdb/common/btrace-common.h b/gdb/common/btrace-common.h
index b157c7c..1986868 100644
--- a/gdb/common/btrace-common.h
+++ b/gdb/common/btrace-common.h
@@ -64,10 +64,10 @@ struct btrace_target_info;
 enum btrace_read_type
 {
   /* Send all available trace.  */
-  btrace_read_all,
+  BTRACE_READ_ALL,
 
   /* Send all available trace, if it changed.  */
-  btrace_read_new
+  BTRACE_READ_NEW
 };
 
 #endif /* BTRACE_COMMON_H */
diff --git a/gdb/common/linux-btrace.c b/gdb/common/linux-btrace.c
index 2e0cd8e..aa2b0ae 100644
--- a/gdb/common/linux-btrace.c
+++ b/gdb/common/linux-btrace.c
@@ -509,7 +509,7 @@ linux_read_btrace (struct btrace_target_info *tinfo,
   unsigned long data_head, retries = 5;
   size_t buffer_size;
 
-  if (type == btrace_read_new && !linux_btrace_has_changed (tinfo))
+  if (type == BTRACE_READ_NEW && !linux_btrace_has_changed (tinfo))
     return NULL;
 
   header = perf_event_header (tinfo);
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index e0af785..1a110e0 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1377,9 +1377,9 @@ handle_qxfer_btrace (const char *annex,
     }
 
   if (strcmp (annex, "all") == 0)
-    type = btrace_read_all;
+    type = BTRACE_READ_ALL;
   else if (strcmp (annex, "new") == 0)
-    type = btrace_read_new;
+    type = BTRACE_READ_NEW;
   else
     {
       strcpy (own_buf, "E.Bad annex.");
diff --git a/gdb/remote.c b/gdb/remote.c
index 2ac8c36..8472e2a 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -11446,10 +11446,10 @@ remote_read_btrace (struct btrace_target_info *tinfo,
 
   switch (type)
     {
-    case btrace_read_all:
+    case BTRACE_READ_ALL:
       annex = "all";
       break;
-    case btrace_read_new:
+    case BTRACE_READ_NEW:
       annex = "new";
       break;
     default:
-- 
1.8.3.1

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

* Re: [patch v8 00/24] record-btrace: reverse
  2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
                   ` (23 preceding siblings ...)
  2013-12-12  9:16 ` [patch v8 05/24] frame: artificial frame id's Markus Metzger
@ 2013-12-12 14:07 ` Jan Kratochvil
  2013-12-12 14:19   ` Metzger, Markus T
  24 siblings, 1 reply; 73+ messages in thread
From: Jan Kratochvil @ 2013-12-12 14:07 UTC (permalink / raw)
  To: Markus Metzger; +Cc: gdb-patches, Pedro Alves

On Thu, 12 Dec 2013 10:15:23 +0100, Markus Metzger wrote:
> This is a small update of v7 addressing review comments from Jan and Pedro.

The patchset is OK for commit from me, just Pedro had some questions,
hopefully already answered in this patchset.  Sorry it took so long.


Thanks,
Jan

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

* RE: [patch v8 00/24] record-btrace: reverse
  2013-12-12 14:07 ` [patch v8 00/24] record-btrace: reverse Jan Kratochvil
@ 2013-12-12 14:19   ` Metzger, Markus T
  0 siblings, 0 replies; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-12 14:19 UTC (permalink / raw)
  To: Jan Kratochvil, Pedro Alves; +Cc: gdb-patches

> -----Original Message-----
> From: Jan Kratochvil [mailto:jan.kratochvil@redhat.com]
> Sent: Thursday, December 12, 2013 2:22 PM


> On Thu, 12 Dec 2013 10:15:23 +0100, Markus Metzger wrote:
> > This is a small update of v7 addressing review comments from Jan and
> Pedro.
> 
> The patchset is OK for commit from me, just Pedro had some questions,
> hopefully already answered in this patchset.  Sorry it took so long.

Thanks a lot for your review and the good feedback!

I'll wait for Pedro's OK, as well.  I already incorporated the feedback he
had and I cc'ed him on the respective patches.

Regards,
Markus.

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

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

* Re: [patch v8 24/24] record-btrace: add (reverse-)stepping support
  2013-12-12  9:16 ` [patch v8 24/24] record-btrace: add (reverse-)stepping support Markus Metzger
@ 2013-12-12 16:36   ` Eli Zaretskii
  2013-12-13 19:22   ` Pedro Alves
  1 sibling, 0 replies; 73+ messages in thread
From: Eli Zaretskii @ 2013-12-12 16:36 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

> From: Markus Metzger <markus.t.metzger@intel.com>
> Cc: gdb-patches@sourceware.org, Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 12 Dec 2013 10:15:47 +0100
> 
> Non-stop mode is not working.  Do not allow record-btrace in non-stop mode.

Thanks.

> +  The target does not record data and does therefore not allow reading
> +  memory or registers.            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

"and therefore does not allow ..."

The documentation parts are OK with this change.

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

* Re: [patch v8 05/24] frame: artificial frame id's
  2013-12-12  9:16 ` [patch v8 05/24] frame: artificial frame id's Markus Metzger
@ 2013-12-12 19:39   ` Pedro Alves
  2013-12-12 19:55     ` Jan Kratochvil
  0 siblings, 1 reply; 73+ messages in thread
From: Pedro Alves @ 2013-12-12 19:39 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/12/2013 09:15 AM, Markus Metzger wrote:
> At the moment, a frame must have a stack - except for the outer frame.
> 
> When we analyze the recorded execution for "record btrace" to detect
> function calls and compute a back trace at some point in the recorded
> execution history, we will end up with frames without a stack.

To be more precise, the frames do have a stack, but it's <unavailable>.
I.e., it existed, but we didn't capture it, so we can't retrieve it.
I presume "p $sp" etc. will show <unavailable>, right?

> 
> To prepare for this, support frame_id's without a stack component.

So special_addr will be a made up number, right?  Will each such
frame have its own special_addr ?

> CC:  Pedro Alves  <palves@redhat.com>
> 
> 2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>
> 
> 	* frame.h (frame_id_build_artificial): New.
> 	* frame.c (frame_id_build_artificial): New.
> 	(frame_id_p): An artificial frame is valid.
> 	(frame_id_eq): A frame is equal to itself.
> 
> 
> ---
>  gdb/frame.c | 30 +++++++++++++++++++++++-------
>  gdb/frame.h |  6 ++++++
>  2 files changed, 29 insertions(+), 7 deletions(-)
> 
> diff --git a/gdb/frame.c b/gdb/frame.c
> index ddd5e70..37d780e 100644
> --- a/gdb/frame.c
> +++ b/gdb/frame.c
> @@ -526,6 +526,21 @@ frame_id_build_wild (CORE_ADDR stack_addr)
>    return id;
>  }
>  
> +/* See frame.h.  */
> +
> +struct frame_id
> +frame_id_build_artificial (CORE_ADDR code_addr,
> +			   CORE_ADDR special_addr)
> +{
> +  struct frame_id id = null_frame_id;
> +
> +  id.code_addr = code_addr;
> +  id.code_addr_p = 1;
> +  id.special_addr = special_addr;
> +  id.special_addr_p = 1;
> +  return id;
> +}
> +
>  int
>  frame_id_p (struct frame_id l)
>  {
> @@ -536,6 +551,9 @@ frame_id_p (struct frame_id l)
>    /* outer_frame_id is also valid.  */
>    if (!p && memcmp (&l, &outer_frame_id, sizeof (l)) == 0)
>      p = 1;
> +  /* An artificial frame is also valid.  */
> +  if (!p && l.code_addr_p && l.special_addr_p)
> +    p = 1;
>    if (frame_debug)
>      {
>        fprintf_unfiltered (gdb_stdlog, "{ frame_id_p (l=");
> @@ -559,13 +577,11 @@ frame_id_eq (struct frame_id l, struct frame_id r)
>  {
>    int eq;
>  
> -  if (!l.stack_addr_p && l.special_addr_p
> -      && !r.stack_addr_p && r.special_addr_p)
> -    /* The outermost frame marker is equal to itself.  This is the
> -       dodgy thing about outer_frame_id, since between execution steps
> -       we might step into another function - from which we can't
> -       unwind either.  More thought required to get rid of
> -       outer_frame_id.  */
> +  if (memcmp (&l, &r, sizeof (l)) == 0)
> +    /* Every frame is equal to itself.
> +       This is the dodgy thing about outer_frame_id, since between execution
> +       steps we might step into another function - from which we can't unwind
> +       either.  More thought required to get rid of outer_frame_id.  */
>      eq = 1;
>    else if (!l.stack_addr_p || !r.stack_addr_p)
>      /* Like a NaN, if either ID is invalid, the result is false.

Looks like frame_ id_eq (null_frame_id, null_frame_id) now
returns true, while it returns false before.  If you discussed
all this and came to the conclusion it's OK, please document
it in the commit log.  In any case (I'm not sure offhand if
that's OK), the NaN comment above is no longer correct,
and neither is this one:

  /* Make the sentinel frame's ID valid, but invalid.  That way all
     comparisons with it should fail.  */
  frame->this_id.p = 1;
  frame->this_id.value = null_frame_id;

> diff --git a/gdb/frame.h b/gdb/frame.h
> index e4e6b25..71f07dd 100644
> --- a/gdb/frame.h
> +++ b/gdb/frame.h
> @@ -174,6 +174,12 @@ extern struct frame_id frame_id_build_special (CORE_ADDR stack_addr,
>     as the special identifier address are set to indicate wild cards.  */
>  extern struct frame_id frame_id_build_wild (CORE_ADDR stack_addr);
>  
> +/* Construct an artificial frame ID.  The first parameter is the frame's
> +   constant code address (typically the function entry point), and the
> +   second the frame's special identifier address.  */
> +extern struct frame_id frame_id_build_artificial (CORE_ADDR code_addr,
> +						  CORE_ADDR special_addr);
> +
>  /* Returns non-zero when L is a valid frame (a valid frame has a
>     non-zero .base).  The outermost frame is valid even without an
>     ID.  */
> 


-- 
Pedro Alves

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

* Re: [patch v8 05/24] frame: artificial frame id's
  2013-12-12 19:39   ` Pedro Alves
@ 2013-12-12 19:55     ` Jan Kratochvil
  2013-12-13  8:04       ` Metzger, Markus T
  0 siblings, 1 reply; 73+ messages in thread
From: Jan Kratochvil @ 2013-12-12 19:55 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Markus Metzger, gdb-patches

On Thu, 12 Dec 2013 20:39:09 +0100, Pedro Alves wrote:
> To be more precise, the frames do have a stack, but it's <unavailable>.
> I.e., it existed, but we didn't capture it, so we can't retrieve it.
> I presume "p $sp" etc. will show <unavailable>, right?

Right.


> > To prepare for this, support frame_id's without a stack component.
> 
> So special_addr will be a made up number, right?  Will each such
> frame have its own special_addr ?

Yes.


> Looks like frame_ id_eq (null_frame_id, null_frame_id) now
> returns true, while it returns false before.  If you discussed
> all this and came to the conclusion it's OK, please document
> it in the commit log.  In any case (I'm not sure offhand if
> that's OK), the NaN comment above is no longer correct,
> and neither is this one:

That was not needed / intentional so it would be better to keep the original
behavior.


Thanks,
Jan

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

* RE: [patch v8 05/24] frame: artificial frame id's
  2013-12-12 19:55     ` Jan Kratochvil
@ 2013-12-13  8:04       ` Metzger, Markus T
  2013-12-13 11:27         ` Jan Kratochvil
  2013-12-13 12:09         ` Pedro Alves
  0 siblings, 2 replies; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-13  8:04 UTC (permalink / raw)
  To: Jan Kratochvil, Pedro Alves; +Cc: gdb-patches

> -----Original Message-----
> From: Jan Kratochvil [mailto:jan.kratochvil@redhat.com]
> Sent: Thursday, December 12, 2013 8:56 PM

Thanks for your feedback, Pedro.

Jan already answered some of your questions.
[...]

> > Looks like frame_ id_eq (null_frame_id, null_frame_id) now
> > returns true, while it returns false before.  If you discussed
> > all this and came to the conclusion it's OK, please document
> > it in the commit log.  In any case (I'm not sure offhand if
> > that's OK), the NaN comment above is no longer correct,
> > and neither is this one:
> 
> That was not needed / intentional so it would be better to keep the original
> behavior.

Would it be OK to have no frame be equal to null_frame_id?

diff --git a/gdb/frame.c b/gdb/frame.c
index 37d780e..efda1cc 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -577,16 +577,17 @@ frame_id_eq (struct frame_id l, struct frame_id r)
 {
   int eq;
 
-  if (memcmp (&l, &r, sizeof (l)) == 0)
-    /* Every frame is equal to itself.
+  if (memcmp (&l, &null_frame_id, sizeof (l)) == 0
+      || memcmp (&r, &null_frame_id, sizeof (r)) == 0)
+    /* Like a NaN, if either ID is invalid, the result is false.
+       Note that a frame ID is invalid iff it is the null frame ID.  */
+    eq = 0;
+  else if (memcmp (&l, &r, sizeof (l)) == 0)
+    /* Every valid frame is equal to itself.
        This is the dodgy thing about outer_frame_id, since between execution
        steps we might step into another function - from which we can't unwind
        either.  More thought required to get rid of outer_frame_id.  */
     eq = 1;
-  else if (!l.stack_addr_p || !r.stack_addr_p)
-    /* Like a NaN, if either ID is invalid, the result is false.
-       Note that a frame ID is invalid iff it is the null frame ID.  */
-    eq = 0;
   else if (l.stack_addr != r.stack_addr)
     /* If .stack addresses are different, the frames are different.  */
     eq = 0;


Regards,
Markus.

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

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

* Re: [patch v8 05/24] frame: artificial frame id's
  2013-12-13  8:04       ` Metzger, Markus T
@ 2013-12-13 11:27         ` Jan Kratochvil
  2013-12-13 11:42           ` Metzger, Markus T
  2013-12-13 12:09         ` Pedro Alves
  1 sibling, 1 reply; 73+ messages in thread
From: Jan Kratochvil @ 2013-12-13 11:27 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: Pedro Alves, gdb-patches

On Fri, 13 Dec 2013 09:04:11 +0100, Metzger, Markus T wrote:
> Would it be OK to have no frame be equal to null_frame_id?
> 
> diff --git a/gdb/frame.c b/gdb/frame.c
> index 37d780e..efda1cc 100644
> --- a/gdb/frame.c
> +++ b/gdb/frame.c
> @@ -577,16 +577,17 @@ frame_id_eq (struct frame_id l, struct frame_id r)
>  {
>    int eq;
>  
> -  if (memcmp (&l, &r, sizeof (l)) == 0)
> -    /* Every frame is equal to itself.
> +  if (memcmp (&l, &null_frame_id, sizeof (l)) == 0
> +      || memcmp (&r, &null_frame_id, sizeof (r)) == 0)
> +    /* Like a NaN, if either ID is invalid, the result is false.
> +       Note that a frame ID is invalid iff it is the null frame ID.  */
> +    eq = 0;
> +  else if (memcmp (&l, &r, sizeof (l)) == 0)
> +    /* Every valid frame is equal to itself.
>         This is the dodgy thing about outer_frame_id, since between execution
>         steps we might step into another function - from which we can't unwind
>         either.  More thought required to get rid of outer_frame_id.  */
>      eq = 1;
> -  else if (!l.stack_addr_p || !r.stack_addr_p)
> -    /* Like a NaN, if either ID is invalid, the result is false.
> -       Note that a frame ID is invalid iff it is the null frame ID.  */
> -    eq = 0;
>    else if (l.stack_addr != r.stack_addr)
>      /* If .stack addresses are different, the frames are different.  */
>      eq = 0;

It looks OK to me.  I have filed:
	py-finishbreakpoint.c incorrect frame_id_eq
	https://sourceware.org/bugzilla/show_bug.cgi?id=16324

I have found in create_sentinel_frame:
  /* Make the sentinel frame's ID valid, but invalid.  That way all
     comparisons with it should fail.  */
  frame->this_id.p = 1;
  frame->this_id.value = null_frame_id;

Although I haven't found any code (besides the python above) to depend on it.


Jan

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

* RE: [patch v8 05/24] frame: artificial frame id's
  2013-12-13 11:27         ` Jan Kratochvil
@ 2013-12-13 11:42           ` Metzger, Markus T
  0 siblings, 0 replies; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-13 11:42 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches, Jan Kratochvil

> -----Original Message-----
> From: Jan Kratochvil [mailto:jan.kratochvil@redhat.com]
> Sent: Friday, December 13, 2013 12:27 PM


> It looks OK to me.  I have filed:
> 	py-finishbreakpoint.c incorrect frame_id_eq
> 	https://sourceware.org/bugzilla/show_bug.cgi?id=16324

Are you OK with this, as well, Pedro?

Tests run without regression.  Do you want me to send the full
patch, as well?  I just sent the diff on top of my original patch,
so far.

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

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

* Re: [patch v8 05/24] frame: artificial frame id's
  2013-12-13  8:04       ` Metzger, Markus T
  2013-12-13 11:27         ` Jan Kratochvil
@ 2013-12-13 12:09         ` Pedro Alves
  2013-12-13 13:01           ` Metzger, Markus T
  1 sibling, 1 reply; 73+ messages in thread
From: Pedro Alves @ 2013-12-13 12:09 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: Jan Kratochvil, gdb-patches

On 12/13/2013 08:04 AM, Metzger, Markus T wrote:
>> -----Original Message-----
>> From: Jan Kratochvil [mailto:jan.kratochvil@redhat.com]
>> Sent: Thursday, December 12, 2013 8:56 PM
> 
> Thanks for your feedback, Pedro.
> 
> Jan already answered some of your questions.
> [...]
> 
>>> Looks like frame_ id_eq (null_frame_id, null_frame_id) now
>>> returns true, while it returns false before.  If you discussed
>>> all this and came to the conclusion it's OK, please document
>>> it in the commit log.  In any case (I'm not sure offhand if
>>> that's OK), the NaN comment above is no longer correct,
>>> and neither is this one:
>>
>> That was not needed / intentional so it would be better to keep the original
>> behavior.
> 
> Would it be OK to have no frame be equal to null_frame_id?

That looks messy and piling hacks.  Seem to me the NaN test
could be replaced by frame_id_p checks instead.  But,
please see (git 005ca36a) and the history behind this code:

https://sourceware.org/ml/gdb-patches/2009-09/msg00271.html
https://sourceware.org/ml/gdb-patches/2009-08/msg00524.html

The exiting code&comments ...

int
frame_id_eq (struct frame_id l, struct frame_id r)
{
  int eq;

  if (!l.stack_addr_p && l.special_addr_p
      && !r.stack_addr_p && r.special_addr_p)
    /* The outermost frame marker is equal to itself.  This is the
       dodgy thing about outer_frame_id, since between execution steps
       we might step into another function - from which we can't
       unwind either.  More thought required to get rid of
       outer_frame_id.  */
    eq = 1;



and this in infrun.c:

     NOTE: frame_id_eq will never report two invalid frame IDs as
     being equal, so to get into this block, both the current and
     previous frame must have valid frame IDs.  */
  /* The outer_frame_id check is a heuristic to detect stepping
     through startup code.  If we step over an instruction which
     sets the stack pointer from an invalid value to a valid value,
     we may detect that as a subroutine call from the mythical
     "outermost" function.  This could be fixed by marking
     outermost frames as !stack_p,code_p,special_p.  Then the
     initial outermost frame, before sp was valid, would
     have code_addr == &_start.  See the comment in frame_id_eq
     for more.  */
  if (!frame_id_eq (get_stack_frame_id (frame),
		    ecs->event_thread->control.step_stack_frame_id)
      && (frame_id_eq (frame_unwind_caller_id (get_current_frame ()),
		       ecs->event_thread->control.step_stack_frame_id)
	  && (!frame_id_eq (ecs->event_thread->control.step_stack_frame_id,
			    outer_frame_id)
	      || step_start_function != find_pc_function (stop_pc))))
    {


... seems to me was headed in the direction of having different
outermost frame ids for different functions, IOW, frame ids
with code_p, but different code_addr's, and still
with {!stack_addr_p,special_addr_p}.  The code above seems to want
that those would still compare equal, as they're all outermost.
I don't think we actually do create such ids anywhere, though,
and I'm not sure whether ignoring the code_addr like that is
really the best way to go.

In any case, I think we do have several distinct cases:

 #1 - we're in the outermost frame / entry pointer, haven't
   setup the stack yet.
 #2 - we're in the outermost frame (identified by debug info or ABI,
   e.g., frame pointer == 0), we've setup the stack.
 #3 - we're in the outermost frame (identified by debug info),
     but stack pointer/address is unavailable (haven't collected register).
 #4 - non outermost frame, there a stack, and it is known.
 #5 - non outermost frame, there a stack, and it is unavailable.

And I don't think it's a good idea to make fixing these outermost
issues harder, by introducing reuse of the same frame id markers
for different things.

I've slowly been moving the code in the direction
of identifying the outermost frame not by id, but by having
the unwinder identify that with:

 this_frame->unwind->stop_reason () == UNWIND_OUTERMOST.

So with that aside, we still have:

 - invalid stack addr
 - valid stack addr
 - valid, but unavailable stack addr

Take a look at this patch:

  https://sourceware.org/ml/gdb-patches/2013-04/msg00541.html

It seems to me your frames are exactly like those, though
it sounds like you may have more than one frame with stack
unavailable, and with the same code address, but that should
be identified as different frames (please confirm).  In that
case, it seems like you could use the artificial depth to
distinguish them.

-- 
Pedro Alves

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

* RE: [patch v8 05/24] frame: artificial frame id's
  2013-12-13 12:09         ` Pedro Alves
@ 2013-12-13 13:01           ` Metzger, Markus T
  2013-12-13 15:44             ` Pedro Alves
  0 siblings, 1 reply; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-13 13:01 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Jan Kratochvil, gdb-patches

> -----Original Message-----
> From: gdb-patches-owner@sourceware.org [mailto:gdb-patches-
> owner@sourceware.org] On Behalf Of Pedro Alves


> Take a look at this patch:
> 
>   https://sourceware.org/ml/gdb-patches/2013-04/msg00541.html
> 
> It seems to me your frames are exactly like those, though
> it sounds like you may have more than one frame with stack
> unavailable, and with the same code address, but that should
> be identified as different frames (please confirm).  In that
> case, it seems like you could use the artificial depth to
> distinguish them.

I have several frames, none of them has an available stack.
It's either my frames or some other frames.  They are never
mixed.

I may also have frames with the same code address and they
would need to be identified as different frames - confirmed.

I'm assigning a unique identifier to special so I can distinguish
frames that have the same code address.  That identifier is
preserved during stepping.

Afaik, the artificial depth is used for artificial frames beneath
a real frame.  It's used for inline and tailcall frames.  The
artificial_depth field is a simple counter.

This does not quite match what I need.  Since all frames in my
stack are artificial, I wouldn't have a real frame on top from
which to start counting.  So I would count from the sentinel
frame and the artificial_depth would be equal to the frame level.

I could imagine that this would cause problems during stepping
when trying to detect stepping into subroutines by comparing
frame_id's.


I could set stack_p to -1 instead of 0, though.  Looks like this
already addresses some of the problems you listed.

Do you have tests that demonstrate the issues you mentioned?
The tests in the repo behave identical with or without my changes.

regards,
markus.

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

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

* Re: [patch v8 05/24] frame: artificial frame id's
  2013-12-13 13:01           ` Metzger, Markus T
@ 2013-12-13 15:44             ` Pedro Alves
  2013-12-13 17:51               ` Pedro Alves
  0 siblings, 1 reply; 73+ messages in thread
From: Pedro Alves @ 2013-12-13 15:44 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: Jan Kratochvil, gdb-patches

On 12/13/2013 12:59 PM, Metzger, Markus T wrote:
>> -----Original Message-----
>> From: gdb-patches-owner@sourceware.org [mailto:gdb-patches-
>> owner@sourceware.org] On Behalf Of Pedro Alves
> 
> 
>> Take a look at this patch:
>>
>>   https://sourceware.org/ml/gdb-patches/2013-04/msg00541.html
>>
>> It seems to me your frames are exactly like those, though
>> it sounds like you may have more than one frame with stack
>> unavailable, and with the same code address, but that should
>> be identified as different frames (please confirm).  In that
>> case, it seems like you could use the artificial depth to
>> distinguish them.
> 
> I have several frames, none of them has an available stack.
> It's either my frames or some other frames.  They are never
> mixed.
> 
> I may also have frames with the same code address and they
> would need to be identified as different frames - confirmed.
> 
> I'm assigning a unique identifier to special so I can distinguish
> frames that have the same code address.  That identifier is
> preserved during stepping.

Thanks for the explanation.

> Afaik, the artificial depth is used for artificial frames beneath
> a real frame.  It's used for inline and tailcall frames.  The
> artificial_depth field is a simple counter.

Yes, I see that wouldn't work.  Too many assumptions around what
artificial_depth means all over.

> I could set stack_p to -1 instead of 0, though.  Looks like this
> already addresses some of the problems you listed.

Yeah.  Let me clean up that patch a little (and write a test
for it), and you should then able to build such frames, and
I don't think you'll need to change frame_id_eq then.

> Do you have tests that demonstrate the issues you mentioned?
> The tests in the repo behave identical with or without my changes.

For stepping around the program entry point, I don't think
there's any presently, and to trigger the issues, we'd need
to undo the hack in infrun.c at least.  The "tfind" case I
mention in the patch I pointed to is similar though ("tfind"
is kind of like "si" over trace frames).

-- 
Pedro Alves

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

* Re: [patch v8 05/24] frame: artificial frame id's
  2013-12-13 15:44             ` Pedro Alves
@ 2013-12-13 17:51               ` Pedro Alves
  2013-12-18 13:30                 ` Metzger, Markus T
  0 siblings, 1 reply; 73+ messages in thread
From: Pedro Alves @ 2013-12-13 17:51 UTC (permalink / raw)
  Cc: Metzger, Markus T, Jan Kratochvil, gdb-patches

On 12/13/2013 03:44 PM, Pedro Alves wrote:
> Yeah.  Let me clean up that patch a little (and write a test
> for it), and you should then able to build such frames, and
> I don't think you'll need to change frame_id_eq then.

https://sourceware.org/ml/gdb-patches/2013-12/msg00535.html

-- 
Pedro Alves

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

* Re: [patch v8 11/24] record-btrace: make ranges include begin and end
  2013-12-12  9:16 ` [patch v8 11/24] record-btrace: make ranges include begin and end Markus Metzger
@ 2013-12-13 17:56   ` Pedro Alves
  0 siblings, 0 replies; 73+ messages in thread
From: Pedro Alves @ 2013-12-13 17:56 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/12/2013 09:15 AM, Markus Metzger wrote:
> The "record function-call-history" and "record instruction-history" commands
> accept a range "begin, end".  End is not included in both cases.  Include it.

User interface changes should have accompanying NEWS entries, IMO.

(there may be other similar cases in the series)

-- 
Pedro Alves

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

* Re: [patch v8 15/24] frame, backtrace: allow targets to supply a frame unwinder
  2013-12-12  9:16 ` [patch v8 15/24] frame, backtrace: allow targets to supply a frame unwinder Markus Metzger
@ 2013-12-13 18:27   ` Pedro Alves
  2013-12-16  9:18     ` Metzger, Markus T
  0 siblings, 1 reply; 73+ messages in thread
From: Pedro Alves @ 2013-12-13 18:27 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/12/2013 09:15 AM, Markus Metzger wrote:
> Allow targets to supply their own target-specific frame unwinders; one for
> normal frames and one for tailcall frames.  If a target-specific unwinder
> is supplied, it will be chosen before any other unwinder.

These ...

> 
> diff --git a/gdb/dwarf2-frame.c b/gdb/dwarf2-frame.c
> index c4f8771..4efa7d8 100644
> --- a/gdb/dwarf2-frame.c
> +++ b/gdb/dwarf2-frame.c
> @@ -1513,16 +1513,16 @@ dwarf2_frame_cfa (struct frame_info *this_frame)
>  {
>    while (get_frame_type (this_frame) == INLINE_FRAME)
>      this_frame = get_prev_frame (this_frame);
> +  if (get_frame_unwind_stop_reason (this_frame) == UNWIND_UNAVAILABLE)
> +    throw_error (NOT_AVAILABLE_ERROR,
> +                _("can't compute CFA for this frame: "
> +                  "required registers or memory are unavailable"));
>    /* This restriction could be lifted if other unwinders are known to
>       compute the frame base in a way compatible with the DWARF
>       unwinder.  */
>    if (!frame_unwinder_is (this_frame, &dwarf2_frame_unwind)
>        && !frame_unwinder_is (this_frame, &dwarf2_tailcall_frame_unwind))
>      error (_("can't compute CFA for this frame"));
> -  if (get_frame_unwind_stop_reason (this_frame) == UNWIND_UNAVAILABLE)
> -    throw_error (NOT_AVAILABLE_ERROR,
> -		 _("can't compute CFA for this frame: "
> -		   "required registers or memory are unavailable"));
>    return get_frame_base (this_frame);
>  }

> diff --git a/gdb/frame.c b/gdb/frame.c
> index 37d780e..3f2aabb 100644
> --- a/gdb/frame.c
> +++ b/gdb/frame.c
> @@ -2507,13 +2507,10 @@ get_frame_sp (struct frame_info *this_frame)
>  enum unwind_stop_reason
>  get_frame_unwind_stop_reason (struct frame_info *frame)
>  {
> -  /* If we haven't tried to unwind past this point yet, then assume
> -     that unwinding would succeed.  */
> -  if (frame->prev_p == 0)
> -    return UNWIND_NO_REASON;
> +  /* Fill-in STOP_REASON.  */
> +  get_prev_frame_1 (frame);
> +  gdb_assert (frame->prev_p);
>  
> -  /* Otherwise, we set a reason when we succeeded (or failed) to
> -     unwind.  */
>    return frame->stop_reason;
>  }

... parts look a bit unrelated.  It'd be good to have
the rationale for this spelled out.  I can understand
why they might be necessary, but I don't see why
target vs non-unwinders unwinders would be special here?

-- 
Pedro Alves

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

* Re: [patch v8 17/24] record-btrace: provide xfer_partial target method
  2013-12-12  9:16 ` [patch v8 17/24] record-btrace: provide xfer_partial target method Markus Metzger
@ 2013-12-13 18:43   ` Pedro Alves
  2013-12-16 10:53     ` Metzger, Markus T
  0 siblings, 1 reply; 73+ messages in thread
From: Pedro Alves @ 2013-12-13 18:43 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/12/2013 09:15 AM, Markus Metzger wrote:
> Provide the xfer_partial target method for the btrace record target.
> 
> Only allow memory accesses to readonly memory while we're replaying.
> 
> 2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>
> 
> 	* record-btrace.c (record_btrace_xfer_partial): New.
> 	(init_record_btrace_ops): Initialize xfer_partial.
> 
> 
> ---
>  gdb/record-btrace.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 51 insertions(+)
> 
> diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
> index cf30e17..ec99ba2 100644
> --- a/gdb/record-btrace.c
> +++ b/gdb/record-btrace.c
> @@ -753,6 +753,56 @@ record_btrace_is_replaying (void)
>    return 0;
>  }
>  
> +/* The to_xfer_partial method of target record-btrace.  */
> +
> +static LONGEST
> +record_btrace_xfer_partial (struct target_ops *ops, enum target_object object,
> +			    const char *annex, gdb_byte *readbuf,
> +			    const gdb_byte *writebuf, ULONGEST offset,
> +			    LONGEST len)
> +{
> +  struct target_ops *t;
> +
> +  /* Filter out requests that don't make sense during replay.  */

This allows writes to pass through?

> +  if (record_btrace_is_replaying ())
> +    {
> +      switch (object)
> +	{
> +	case TARGET_OBJECT_MEMORY:
> +	case TARGET_OBJECT_RAW_MEMORY:
> +	case TARGET_OBJECT_STACK_MEMORY:

I don't think we actually ever see TARGET_OBJECT_RAW_MEMORY
and TARGET_OBJECT_STACK_MEMORY here?  Those are supposedly
handled by target.c, and translated to TARGET_OBJECT_MEMORY
before reaching the actual targets.

> +	  {
> +	    /* We allow reading readonly memory.  */
> +	    struct target_section *section;

A comment on this being an heuristic would be good.  The _current_
readonly sections might well not be the same as the ones the
target had when the trace was recorded.

> +
> +	    section = target_section_by_addr (ops, offset);
> +	    if (section != NULL)
> +	      {
> +		/* Check if the section we found is readonly.  */
> +		if ((bfd_get_section_flags (section->the_bfd_section->owner,
> +					    section->the_bfd_section)
> +		     & SEC_READONLY) != 0)
> +		  {
> +		    /* Truncate the request to fit into this section.  */
> +		    len = min (len, section->endaddr - offset);
> +		    break;
> +		  }
> +	      }
> +
> +	    return -1;

I must be missing something, but won't raw_memory_xfer_partial
keep trying in the target beneath, anyway?

> +	  }
> +	}
> +    }
> +
> +  /* Forward the request.  */
> +  for (t = ops->beneath; t != NULL; t = t->beneath)
> +    if (t->to_xfer_partial != NULL)
> +      return t->to_xfer_partial (t, object, annex, readbuf, writebuf,
> +				 offset, len);
> +
> +  return -1;
> +}
> +
>  /* The to_fetch_registers method of target record-btrace.  */
>  
>  static void
> @@ -930,6 +980,7 @@ init_record_btrace_ops (void)
>    ops->to_call_history_from = record_btrace_call_history_from;
>    ops->to_call_history_range = record_btrace_call_history_range;
>    ops->to_record_is_replaying = record_btrace_is_replaying;
> +  ops->to_xfer_partial = record_btrace_xfer_partial;
>    ops->to_fetch_registers = record_btrace_fetch_registers;
>    ops->to_store_registers = record_btrace_store_registers;
>    ops->to_prepare_to_store = record_btrace_prepare_to_store;
> 


-- 
Pedro Alves

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

* Re: [patch v8 24/24] record-btrace: add (reverse-)stepping support
  2013-12-12  9:16 ` [patch v8 24/24] record-btrace: add (reverse-)stepping support Markus Metzger
  2013-12-12 16:36   ` Eli Zaretskii
@ 2013-12-13 19:22   ` Pedro Alves
  2013-12-16 15:56     ` Metzger, Markus T
  1 sibling, 1 reply; 73+ messages in thread
From: Pedro Alves @ 2013-12-13 19:22 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches, Eli Zaretskii

On 12/12/2013 09:15 AM, Markus Metzger wrote:
> Non-stop mode is not working.  Do not allow record-btrace in non-stop mode.

Please provide a better rationale/description for the patch.
It does much more than that! :-)

> CC: Eli Zaretskii  <eliz@gnu.org>
> 
> 2013-12-12  Markus Metzger  <markus.t.metzger@intel.com>
> 
> 	* btrace.h (btrace_thread_flag): New.
> 	(struct btrace_thread_info) <flags>: New.
> 	* record-btrace.c (record_btrace_resume_thread,
> 	record_btrace_find_thread_to_move, btrace_step_no_history,
> 	btrace_step_stopped, record_btrace_start_replaying,
> 	record_btrace_step_thread,
> 	record_btrace_find_resume_thread): New.

[
The standard way to split the functions across multiple lines is
to close the line with ), and reopen the next with (.  E.g,

 	* record-btrace.c (record_btrace_resume_thread)
 	(record_btrace_find_thread_to_move, btrace_step_no_history)
        ...
]

>  
>  This recording method may not be available on all processors.
>  @end table
> diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
> index 1e8cfc1..bc23f2d 100644
> --- a/gdb/record-btrace.c
> +++ b/gdb/record-btrace.c
> @@ -167,6 +167,10 @@ record_btrace_open (char *args, int from_tty)
>    if (!target_supports_btrace ())
>      error (_("Target does not support branch tracing."));
>  
> +  if (non_stop)
> +    error (_("Record btrace can't debug inferior in non-stop mode "
> +	     "(non-stop)."));
> +
>    gdb_assert (record_btrace_thread_observer == NULL);
>  
>    disable_chain = make_cleanup (null_cleanup, NULL);
> @@ -1233,14 +1237,147 @@ const struct frame_unwind record_btrace_tailcall_frame_unwind =
>    record_btrace_frame_dealloc_cache
>  };
>  
> +/* Indicate that TP should be resumed according to FLAG.  */
> +
> +static void
> +record_btrace_resume_thread (struct thread_info *tp,
> +			     enum btrace_thread_flag flag)
> +{
> +  struct btrace_thread_info *btinfo;
> +
> +  DEBUG ("resuming %d (%s): %u", tp->num, target_pid_to_str (tp->ptid), flag);
> +
> +  btinfo = &tp->btrace;
> +
> +  if ((btinfo->flags & BTHR_MOVE) != 0)
> +    error (_("Thread already moving."));
> +
> +  /* Fetch the latest branch trace.  */
> +  btrace_fetch (tp);
> +
> +  btinfo->flags |= flag;
> +}
> +
> +/* Find the thread to resume given a PTID.  */
> +
> +static struct thread_info *
> +record_btrace_find_resume_thread (ptid_t ptid)
> +{
> +  struct thread_info *tp;
> +
> +  /* When asked to resume everything, we pick the current thread.  */
> +  if (ptid_equal (minus_one_ptid, ptid) || ptid_is_pid (ptid))
> +    ptid = inferior_ptid;
> +
> +  return find_thread_ptid (ptid);
> +}
> +
> +/* Stop replaying a thread.  */

s/Stop/Start/

> +
> +static struct btrace_insn_iterator *
> +record_btrace_start_replaying (struct thread_info *tp)
> +{
> +  volatile struct gdb_exception except;
> +  struct btrace_insn_iterator *replay;
> +  struct btrace_thread_info *btinfo;
> +  int executing;
> +
> +  btinfo = &tp->btrace;
> +  replay = NULL;
> +
> +  /* We can't start replaying without trace.  */
> +  if (btinfo->begin == NULL)
> +    return NULL;
> +
> +  /* Clear the executing flag to allow changes to the current frame.  */
> +  executing = is_executing (tp->ptid);
> +  set_executing (tp->ptid, 0);

Why is this necessary?  Is this so you can start replaying
even when the current thread is really executing?

> +
> +  TRY_CATCH (except, RETURN_MASK_ALL)
> +    {
> +      struct frame_info *frame;
> +      struct frame_id frame_id;
> +      int upd_step_frame_id, upd_step_stack_frame_id;
> +
> +      /* The current frame without replaying - computed via normal unwind.  */
> +      frame = get_current_frame ();

Then this would seem bogus.

> +      frame_id = get_frame_id (frame);
> +
> +      /* Check if we need to update any stepping-related frame id's.  */
> +      upd_step_frame_id = frame_id_eq (frame_id,
> +				       tp->control.step_frame_id);
> +      upd_step_stack_frame_id = frame_id_eq (frame_id,
> +					     tp->control.step_stack_frame_id);
> +
> +      /* We start replaying at the end of the branch trace.  This corresponds
> +	 to the current instruction.  */
> +      replay = xmalloc (sizeof (*replay));
> +      btrace_insn_end (replay, btinfo);
> +
> +      /* We're not replaying, yet.  */
> +      gdb_assert (btinfo->replay == NULL);
> +      btinfo->replay = replay;
> +
> +      /* Make sure we're not using any stale registers.  */
> +      registers_changed_ptid (tp->ptid);
> +
> +      /* The current frame with replaying - computed via btrace unwind.  */
> +      frame = get_current_frame ();
> +      frame_id = get_frame_id (frame);
> +
> +      /* Replace stepping related frames where necessary.  */
> +      if (upd_step_frame_id)
> +	tp->control.step_frame_id = frame_id;
> +      if (upd_step_stack_frame_id)
> +	tp->control.step_stack_frame_id = frame_id;

Why would this step_frame_id mucking be necessary?  Can the
thread be stepping when we get here?  How?
Some comments are missing here.

> +    }
> +
> +  /* Restore the previous execution state.  */
> +  set_executing (tp->ptid, executing);
> +
> +  if (except.reason < 0)
> +    throw_exception (except);

If something throws, things are being left
possibly in an inconsistent state, it seems to me.
Also, "replay" leaks.


>  /* The to_resume method of target record-btrace.  */
>  
>  static void
>  record_btrace_resume (struct target_ops *ops, ptid_t ptid, int step,
>  		      enum gdb_signal signal)
>  {
> +  struct thread_info *tp, *other;
> +  enum btrace_thread_flag flag;
> +
> +  DEBUG ("resume %s: %s", target_pid_to_str (ptid), step ? "step" : "cont");
> +
> +  tp = record_btrace_find_resume_thread (ptid);
> +
> +  /* Stop replaying other threads if the thread to resume is not replaying.  */
> +  if (tp != NULL && !btrace_is_replaying (tp)
> +      && execution_direction != EXEC_REVERSE)
> +    ALL_THREADS (other)
> +      record_btrace_stop_replaying (other);

Why's that?

> +
>    /* As long as we're not replaying, just forward the request.  */
> -  if (!record_btrace_is_replaying ())
> +  if (!record_btrace_is_replaying () && execution_direction != EXEC_REVERSE)
>      {
>        for (ops = ops->beneath; ops != NULL; ops = ops->beneath)
>  	if (ops->to_resume != NULL)
> @@ -1249,7 +1386,209 @@ record_btrace_resume (struct target_ops *ops, ptid_t ptid, int step,
>        error (_("Cannot find target for stepping."));
>      }
>  
> -  error (_("You can't do this from here.  Do 'record goto end', first."));
> +  /* Compute the btrace thread flag for the requested move.  */
> +  if (step == 0)
> +    flag = execution_direction == EXEC_REVERSE ? BTHR_RCONT : BTHR_CONT;
> +  else
> +    flag = execution_direction == EXEC_REVERSE ? BTHR_RSTEP : BTHR_STEP;
> +
> +  /* Find the thread to move.  */
> +  if (ptid_equal (minus_one_ptid, ptid) || ptid_is_pid (ptid))
> +    {
> +      ALL_THREADS (tp)
> +	record_btrace_resume_thread (tp, flag);

Seems like this steps all threads, when gdb only wants to
step inferior_ptid and continue others?

> +    }
> +  else if (tp == NULL)
> +    error (_("Cannot find thread to resume."));
> +  else
> +    record_btrace_resume_thread (tp, flag);
> +
> +  /* We just indicate the resume intent here.  The actual stepping happens in
> +     record_btrace_wait below.  */
> +}
> +
> +/* Find a thread to move.  */
> +
> +static struct thread_info *
> +record_btrace_find_thread_to_move (ptid_t ptid)
> +{
> +  struct thread_info *tp;
> +
> +  /* First check the parameter thread.  */
> +  tp = find_thread_ptid (ptid);
> +  if (tp != NULL && (tp->btrace.flags & BTHR_MOVE) != 0)
> +    return tp;
> +
> +  /* Next check the current thread. */
> +  tp = find_thread_ptid (inferior_ptid);
> +  if (tp != NULL && (tp->btrace.flags & BTHR_MOVE) != 0)
> +    return tp;
> +
> +  /* Otherwise, find one other thread that has been resumed.  */
> +  ALL_THREADS (tp)
> +    if ((tp->btrace.flags & BTHR_MOVE) != 0)
> +      return tp;
> +
> +  return NULL;
> +}
> +
> +/* Return a targetwait status indicating that we ran out of history.  */

"target_waitstatus"

> +
> +static struct target_waitstatus
> +btrace_step_no_history (void)
> +{
> +  struct target_waitstatus status;
> +
> +  status.kind = TARGET_WAITKIND_NO_HISTORY;
> +
> +  return status;
> +}
> +
> +/* Return a targetwait status indicating that we stopped.  */

Ditto.  "we stopped" would lead me to think this should be
GDB_SIGNAL_0.  I suggest "status indicating that a step finished".

> +
> +static struct target_waitstatus
> +btrace_step_stopped (void)
> +{
> +  struct target_waitstatus status;
> +
> +  status.kind = TARGET_WAITKIND_STOPPED;
> +  status.value.sig = GDB_SIGNAL_TRAP;
> +
> +  return status;
> +}
> +

> +/* Step a single thread.  */
> +
> +static struct target_waitstatus
> +record_btrace_step_thread (struct thread_info *tp)
> +{
...

> +      for (;;)
> +	{
> +	  const struct btrace_insn *insn;
> +
> +	  /* If we can't step any further, we're done.  */
> +	  steps = btrace_insn_prev (replay, 1);
> +	  if (steps == 0)
> +	    return btrace_step_no_history ();
> +
> +	  insn = btrace_insn_get (replay);
> +	  gdb_assert (insn);
> +
> +	  DEBUG ("stepping %d (%s): reverse~ ... %s", tp->num,

Is "reverse~" a typo?

> +		 target_pid_to_str (tp->ptid),
> +		 core_addr_to_string_nz (insn->pc));
> +
> +	  if (breakpoint_here_p (aspace, insn->pc))
> +	    return btrace_step_stopped ();

How is adjust_pc_after_break handled?

> +	}
> +    }
>  }
>  


> diff --git a/gdb/testsuite/gdb.btrace/delta.exp b/gdb/testsuite/gdb.btrace/delta.exp
> index 9ee2629..49d151e 100644
> --- a/gdb/testsuite/gdb.btrace/delta.exp
> +++ b/gdb/testsuite/gdb.btrace/delta.exp
> @@ -61,3 +61,16 @@ gdb_test "record instruction-history /f 1" "
>  1\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tmov *\\\$0x0,%eax\r" "delta, 4.2"
>  gdb_test "record function-call-history /c 1" "
>  1\tmain\r" "delta, 4.3"
> +
> +# check that we can reverse-stepi that instruction
> +gdb_test "reverse-stepi"
> +gdb_test "info record" "
> +Active record target: record-btrace\r
> +Recorded 1 instructions in 1 functions for .*\r
> +Replay in progress\.  At instruction 1\." "delta, 5.1"

I don't think assuming \n like that is a good idea.
To spell this out in separate lines, I suggest writing a
list, and then merging it with \r\n.  See
dw2-undefined-ret-addr.exp.  You may be able to do with
{} instead of list.

> diff --git a/gdb/testsuite/gdb.btrace/finish.exp b/gdb/testsuite/gdb.btrace/finish.exp
> new file mode 100644
> index 0000000..87ebfe1
> --- /dev/null
> +++ b/gdb/testsuite/gdb.btrace/finish.exp
> @@ -0,0 +1,70 @@
> +# This testcase is part of GDB, the GNU debugger.
> +#
> +# Copyright 2013 Free Software Foundation, Inc.
> +#
> +# Contributed by Intel Corp. <markus.t.metzger@intel.com>
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +# check for btrace support
> +if { [skip_btrace_tests] } { return -1 }
> +
> +# start inferior
> +standard_testfile x86-record_goto.S
> +if [prepare_for_testing finish.exp $testfile $srcfile] {
> +    return -1
> +}
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +# trace the call to the test function
> +gdb_test_no_output "record btrace"
> +gdb_test "next"
> +
> +# let's go somewhere where we can finish
> +gdb_test "record goto 32" ".*fun1\.1.*" "finish, 1.1"
> +gdb_test "info record" "
> +Active record target: record-btrace\r
> +Recorded 40 instructions in 16 functions for .*\r
> +Replay in progress\.  At instruction 32\." "finish, 1.2"
> +
> +# let's finish into fun2
> +gdb_test "finish" ".*fun2\.3.*" "finish, 2.1"
> +gdb_test "info record" "
> +Active record target: record-btrace\r
> +Recorded 40 instructions in 16 functions for .*\r
> +Replay in progress\.  At instruction 35\." "finish, 2.2"
> +
> +# now let's reverse-finish into fun3
> +gdb_test "reverse-finish" ".*fun3\.3.*" "finish, 3.1"
> +gdb_test "info record" "
> +Active record target: record-btrace\r
> +Recorded 40 instructions in 16 functions for .*\r
> +Replay in progress\.  At instruction 27\." "finish, 3.2"
> +
> +# finish again - into fun4
> +gdb_test "finish" ".*fun4\.5.*" "finish, 4.1"
> +gdb_test "info record" "
> +Active record target: record-btrace\r
> +Recorded 40 instructions in 16 functions for .*\r
> +Replay in progress\.  At instruction 39\." "finish, 4.2"
> +
> +# and reverse-finish again - into main
> +gdb_test "reverse-finish" ".*main\.2.*" "finish, 5.1"
> +gdb_test "info record" "
> +Active record target: record-btrace\r
> +Recorded 40 instructions in 16 functions for .*\r
> +Replay in progress\.  At instruction 1\." "finish, 5.2"

Man, this looks way cool.  Kudos.

> diff --git a/gdb/testsuite/gdb.btrace/multi-thread-step.exp b/gdb/testsuite/gdb.btrace/multi-thread-step.exp
> new file mode 100644
> index 0000000..d247845
> --- /dev/null
> +++ b/gdb/testsuite/gdb.btrace/multi-thread-step.exp
> @@ -0,0 +1,99 @@
> +# This testcase is part of GDB, the GNU debugger.
> +#
> +# Copyright 2013 Free Software Foundation, Inc.
> +#
> +# Contributed by Intel Corp. <markus.t.metzger@intel.com>
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +# check for btrace support
> +if { [skip_btrace_tests] } { return -1 }
> +
> +# start inferior
> +standard_testfile
> +if {[gdb_compile_pthreads "$srcdir/$subdir/$srcfile" "$binfile" executable {debug}] != "" } {
> +    return -1
> +}
> +clean_restart $testfile
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +# set up breakpoints
> +set bp_1 [gdb_get_line_number "bp.1" $srcfile]
> +set bp_2 [gdb_get_line_number "bp.2" $srcfile]
> +set bp_3 [gdb_get_line_number "bp.3" $srcfile]
> +
> +proc gdb_cont_to_line { line test } {
> +	gdb_breakpoint $line
> +	gdb_continue_to_breakpoint "$test" ".*$line\r\n.*"
> +	delete_breakpoints
> +}
> +
> +# trace the code between the two breakpoints
> +delete_breakpoints
> +gdb_cont_to_line $srcfile:$bp_1 "mts, 0.1"
> +# make sure GDB knows about the new thread
> +gdb_test "info threads" ".*" "mts, 0.2"
> +gdb_test_no_output "record btrace" "mts, 0.3"
> +gdb_cont_to_line $srcfile:$bp_2 "mts, 0.4"
> +
> +# navigate in the trace history for both threads
> +gdb_test "thread 1" ".*" "mts, 1.1"
> +gdb_test "record goto begin" ".*" "mts, 1.2"
> +gdb_test "info record" ".*Replay in progress\.  At instruction 1\." "mts, 1.3"
> +gdb_test "thread 2" ".*" "mts, 1.4"
> +gdb_test "record goto begin" ".*" "mts, 1.5"
> +gdb_test "info record" ".*Replay in progress\.  At instruction 1\." "mts, 1.6"

What does this "mts" that appears everywhere mean?
(with_test_prefix could help here to create more meaningful test names.)

> diff --git a/gdb/testsuite/gdb.btrace/rn-dl-bind.exp b/gdb/testsuite/gdb.btrace/rn-dl-bind.exp
> new file mode 100644
> index 0000000..4d803f9
> --- /dev/null
> +++ b/gdb/testsuite/gdb.btrace/rn-dl-bind.exp

What's this testing?  I can't infer it from the test name.
Please add a comment.

-- 
Pedro Alves

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

* Re: [patch v8 21/24] record-btrace: extend unwinder
  2013-12-12  9:16 ` [patch v8 21/24] record-btrace: extend unwinder Markus Metzger
@ 2013-12-13 19:45   ` Pedro Alves
  2013-12-16 12:42     ` Metzger, Markus T
  0 siblings, 1 reply; 73+ messages in thread
From: Pedro Alves @ 2013-12-13 19:45 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/12/2013 09:15 AM, Markus Metzger wrote:

> diff --git a/gdb/dwarf2-frame.c b/gdb/dwarf2-frame.c
> index 4efa7d8..9617c12 100644
> --- a/gdb/dwarf2-frame.c
> +++ b/gdb/dwarf2-frame.c
> @@ -31,6 +31,7 @@
>  #include "objfiles.h"
>  #include "regcache.h"
>  #include "value.h"
> +#include "record.h"
>  
>  #include "gdb_assert.h"
>  #include <string.h>
> @@ -1511,6 +1512,11 @@ dwarf2_frame_base_sniffer (struct frame_info *this_frame)
>  CORE_ADDR
>  dwarf2_frame_cfa (struct frame_info *this_frame)
>  {
> +  if (frame_unwinder_is (this_frame, &record_btrace_tailcall_frame_unwind)
> +      || frame_unwinder_is (this_frame, &record_btrace_frame_unwind))
> +    throw_error (NOT_AVAILABLE_ERROR,
> +		 _("cfa not available for record btrace target"));

Kind of odd abstraction boundaries broken here.  On the one hand,
we go through the target for the unwind sniffing, but OTOH, here
we refer to the unwinders directly.  Hmm.

Ah, I don't think it really matters -- if you convert
to the frame_info.stack_status == FID_STACK_UNAVAILABLE
mechanism, then this can check for that instead, and the
get_frame_unwind_stop_reason check can even be eliminated.
Perhaps it should be even get_frame_base the one that
throws.

>  /* Level of the selected frame: 0 for innermost, 1 for its caller, ...
> diff --git a/gdb/frame.h b/gdb/frame.h
> index 71f07dd..b20b69f 100644
> --- a/gdb/frame.h
> +++ b/gdb/frame.h
> @@ -222,7 +222,11 @@ enum frame_type
>    ARCH_FRAME,
>    /* Sentinel or registers frame.  This frame obtains register values
>       direct from the inferior's registers.  */
> -  SENTINEL_FRAME
> +  SENTINEL_FRAME,
> +  /* A branch tracing frame.  */
> +  BTRACE_FRAME,
> +  /* A branch tracing tail call frame.  */
> +  BTRACE_TAILCALL_FRAME
>  };

Hmm?  Why were these needed?  Missing patch rationale...  And missing
comments, I suppose.

-- 
Pedro Alves

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

* Re: [patch v8 22/24] btrace, gdbserver: read branch trace incrementally
  2013-12-12  9:16 ` [patch v8 22/24] btrace, gdbserver: read branch trace incrementally Markus Metzger
@ 2013-12-13 19:46   ` Pedro Alves
  2013-12-16 12:47     ` Metzger, Markus T
  0 siblings, 1 reply; 73+ messages in thread
From: Pedro Alves @ 2013-12-13 19:46 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/12/2013 09:15 AM, Markus Metzger wrote:
> +
> +  /* The instructions vector may become empty temporarily if this has
> +     been the only instruction in this function segment.
> +     This violates the invariant but will be remedied shortly.  */

By what?

> +  return 0;
> +}

-- 
Pedro Alves

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

* Re: [patch v8 23/24] record-btrace: show trace from enable location
  2013-12-12  9:16 ` [patch v8 23/24] record-btrace: show trace from enable location Markus Metzger
@ 2013-12-13 19:50   ` Pedro Alves
  2013-12-16 12:57     ` Metzger, Markus T
  0 siblings, 1 reply; 73+ messages in thread
From: Pedro Alves @ 2013-12-13 19:50 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/12/2013 09:15 AM, Markus Metzger wrote:
> The btrace record target shows the branch trace from the location of the first
> branch destination.  This is the first BTS records.
> 
> After adding incremental updates, we can now add a dummy record for the current
> PC when we enable tracing so we show the trace from the location where branch
> tracing has been enabled.

Ah, awesome, I was just wondering about that.  :-)

I suppose one can we still access the registers when
inspecting this dummy location, or are the btrace unwinders
preventing that?


-- 
Pedro Alves

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

* RE: [patch v8 15/24] frame, backtrace: allow targets to supply a frame unwinder
  2013-12-13 18:27   ` Pedro Alves
@ 2013-12-16  9:18     ` Metzger, Markus T
  2013-12-16 19:02       ` Pedro Alves
  0 siblings, 1 reply; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-16  9:18 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches

> -----Original Message-----
> From: gdb-patches-owner@sourceware.org [mailto:gdb-patches-
> owner@sourceware.org] On Behalf Of Pedro Alves
> Sent: Friday, December 13, 2013 7:28 PM
> To: Metzger, Markus T


> ... parts look a bit unrelated.  It'd be good to have
> the rationale for this spelled out.  I can understand
> why they might be necessary, but I don't see why
> target vs non-unwinders unwinders would be special here?

Those changes are not directly related to target vs non-target
unwinders.  They are connected in that they allow new types
of unwinders.

I spelled out the rationale for each change in the commit
message.  Should I further split this patch into three?

Regards,
Markus.

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

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

* RE: [patch v8 17/24] record-btrace: provide xfer_partial target method
  2013-12-13 18:43   ` Pedro Alves
@ 2013-12-16 10:53     ` Metzger, Markus T
  2013-12-16 19:16       ` Pedro Alves
  0 siblings, 1 reply; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-16 10:53 UTC (permalink / raw)
  To: Pedro Alves, jan.kratochvil; +Cc: gdb-patches

> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Friday, December 13, 2013 7:44 PM


> I must be missing something, but won't raw_memory_xfer_partial
> keep trying in the target beneath, anyway?

You're right.  This function did not have any effect - and that was good;
at least to some extent.  Thanks for pointing this out!

I changed the return -1 to throw_error (...) and added a check for
writebuf != NULL.  Suddenly I got tons of errors when GDB can't insert
breakpoints any longer for (reverse-)stepping.  Also stepping gets broken.

I still get the nice <unavailable> for function parameters.  This fails
already when trying to access the SP for computing the CFA.

I now get an error when trying to access a variable with static storage
duration or when trying to access memory directly via its address.
It would be nice to also get an <unavailable> in those cases.  This would
require the respective layer to catch my exception.


To avoid those errors when trying to set breakpoints, I could try
providing to_insert_breakpoint and to_remove_breakpoint methods
and maintain my own breakpoints.

Breakpoint conditions would still fail.  Not sure what else I'm missing.

Any better idea?

Thanks,
Markus.

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

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

* RE: [patch v8 21/24] record-btrace: extend unwinder
  2013-12-13 19:45   ` Pedro Alves
@ 2013-12-16 12:42     ` Metzger, Markus T
  2013-12-16 19:22       ` Pedro Alves
  0 siblings, 1 reply; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-16 12:42 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches

> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Friday, December 13, 2013 8:45 PM


> > @@ -1511,6 +1512,11 @@ dwarf2_frame_base_sniffer (struct frame_info
> *this_frame)
> >  CORE_ADDR
> >  dwarf2_frame_cfa (struct frame_info *this_frame)
> >  {
> > +  if (frame_unwinder_is (this_frame,
> &record_btrace_tailcall_frame_unwind)
> > +      || frame_unwinder_is (this_frame, &record_btrace_frame_unwind))
> > +    throw_error (NOT_AVAILABLE_ERROR,
> > +		 _("cfa not available for record btrace target"));
> 
> Kind of odd abstraction boundaries broken here.  On the one hand,
> we go through the target for the unwind sniffing, but OTOH, here
> we refer to the unwinders directly.  Hmm.
> 
> Ah, I don't think it really matters -- if you convert
> to the frame_info.stack_status == FID_STACK_UNAVAILABLE
> mechanism, then this can check for that instead, and the
> get_frame_unwind_stop_reason check can even be eliminated.
> Perhaps it should be even get_frame_base the one that
> throws.

We need that check before checking for dwarf2 unwinders.
Get_frame_base is called later but only for dwarf2 unwinders.

I will try to replace the check with a check for stack availability.


> >  /* Level of the selected frame: 0 for innermost, 1 for its caller, ...
> > diff --git a/gdb/frame.h b/gdb/frame.h
> > index 71f07dd..b20b69f 100644
> > --- a/gdb/frame.h
> > +++ b/gdb/frame.h
> > @@ -222,7 +222,11 @@ enum frame_type
> >    ARCH_FRAME,
> >    /* Sentinel or registers frame.  This frame obtains register values
> >       direct from the inferior's registers.  */
> > -  SENTINEL_FRAME
> > +  SENTINEL_FRAME,
> > +  /* A branch tracing frame.  */
> > +  BTRACE_FRAME,
> > +  /* A branch tracing tail call frame.  */
> > +  BTRACE_TAILCALL_FRAME
> >  };
> 
> Hmm?  Why were these needed?  Missing patch rationale...  And missing
> comments, I suppose.

The alternative would be to pretend that they are NORMAL_FRAME
and TAILCALL_FRAME, respectively.  I thought it cleaner to add a new
frame type.  Those frames don't have available stack.

Do you want me to use NORMAL_FRAME and TAILCALL_FRAME, instead?

Regards,
Markus.

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

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

* RE: [patch v8 22/24] btrace, gdbserver: read branch trace incrementally
  2013-12-13 19:46   ` Pedro Alves
@ 2013-12-16 12:47     ` Metzger, Markus T
  2013-12-16 19:23       ` Pedro Alves
  0 siblings, 1 reply; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-16 12:47 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches

> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Friday, December 13, 2013 8:46 PM
> To: Metzger, Markus T


> > +  /* The instructions vector may become empty temporarily if this has
> > +     been the only instruction in this function segment.
> > +     This violates the invariant but will be remedied shortly.  */
> 
> By what?

By btrace_compute_ftrace when we add the new trace.

I added the above to the comment.

Regards,
Markus.

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

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

* RE: [patch v8 23/24] record-btrace: show trace from enable location
  2013-12-13 19:50   ` Pedro Alves
@ 2013-12-16 12:57     ` Metzger, Markus T
  2013-12-16 19:41       ` Pedro Alves
  0 siblings, 1 reply; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-16 12:57 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches

> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Friday, December 13, 2013 8:50 PM


> > After adding incremental updates, we can now add a dummy record for the
> current
> > PC when we enable tracing so we show the trace from the location where
> branch
> > tracing has been enabled.
> 
> Ah, awesome, I was just wondering about that.  :-)
> 
> I suppose one can we still access the registers when
> inspecting this dummy location, or are the btrace unwinders
> preventing that?

You can not access registers when you are in the execution history.

When we add this dummy record when we enable tracing, you're still
outside the execution history, so you can access registers.  Also the last
instruction will be skipped for all record operations - it corresponds to
the current PC and the instruction has not been executed yet.

But it allows us to stitch the first trace chunk to this dummy record and
thus show the full trace from the enable location.

Internally, you're at the same PC if you are replaying and the replay
position is at the last instruction or if you are not replaying.
The different is that you will be able to access registers and memory
only if you are not replaying.  You will also have normal dward2 frames
in that case.  If you are replaying, you will have btrace frames and you
won't be able to access all registers and memory.

In record commands, we stop replaying once we reach the last instruction.

Regards,
Markus.

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

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

* RE: [patch v8 24/24] record-btrace: add (reverse-)stepping support
  2013-12-13 19:22   ` Pedro Alves
@ 2013-12-16 15:56     ` Metzger, Markus T
  2013-12-16 20:30       ` Pedro Alves
  0 siblings, 1 reply; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-16 15:56 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches, Eli Zaretskii

> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Friday, December 13, 2013 8:22 PM

Thanks for your review.


> > +  /* Clear the executing flag to allow changes to the current frame.  */
> > +  executing = is_executing (tp->ptid);
> > +  set_executing (tp->ptid, 0);
> 
> Why is this necessary?  Is this so you can start replaying
> even when the current thread is really executing?

No.  When we just start replaying with a reverse stepping command,
we need to recompute stack frames so we can compare them to
detect subroutine calls in infrun.c.

See also https://sourceware.org/ml/gdb-patches/2013-11/msg00874.html
and https://sourceware.org/ml/gdb-patches/2013-11/msg00891.html.

This function is called from to_wait to enable replaying as part of
reverse stepping.  We need to temporarily set is_executing to false
in order to be able to call get_current_frame below.

I extended the existing comments here and below to explain this in more detail.

> > +  /* Restore the previous execution state.  */
> > +  set_executing (tp->ptid, executing);
> > +
> > +  if (except.reason < 0)
> > +    throw_exception (except);
> 
> If something throws, things are being left
> possibly in an inconsistent state, it seems to me.
> Also, "replay" leaks.

Replay is stored in BTINFO.  We checked that we have trace before
the try block, so btrace_insn_end () won't throw and we're not
leaking REPLAY - we just transitioned from not replaying to replaying.

It may still be better to revert the changes and not start replaying
in case of errors.  Changed that.  I'm also calling registers_changed_ptid
and get_current_frame again to avoid leaving things behind that are
based on replaying.  This might trigger another throw.


> > +  if (tp != NULL && !btrace_is_replaying (tp)
> > +      && execution_direction != EXEC_REVERSE)
> > +    ALL_THREADS (other)
> > +      record_btrace_stop_replaying (other);
> 
> Why's that?

Imagine the user does:

(gdb) thread 1
(gdb) reverse-steop
(gdb) thread 2
(gdb) record goto 42
(gdb) thread 3
(gdb) continue

We could either error out and make him go to thread 1 and thread 2
again to stop replaying those threads.  Or we could more or less
silently stop replaying those other threads before we continue.

I chose to silently stop replaying; no warnings and no questions.
I find both somewhat annoying after some time.


> > +  /* Find the thread to move.  */
> > +  if (ptid_equal (minus_one_ptid, ptid) || ptid_is_pid (ptid))
> > +    {
> > +      ALL_THREADS (tp)
> > +	record_btrace_resume_thread (tp, flag);
> 
> Seems like this steps all threads, when gdb only wants to
> step inferior_ptid and continue others?

Only if gdb passes -1 or the ptid of the process.

In the end, we will move exactly one thread and keep all
others where they are.  This one thread will hit a breakpoint
or run out of execution history.

Are you suggesting that I should only mark inferior_ptid
in this case?  If I marked the others as continue, I would later
on need to prefer a thread that only wanted to step.

I'm preferring inferior_ptid in to_wait later on when the
threads are actually moved.  The effect should be the same,
but it might be more clear if I also did not mark the other
threads.


> > +	  if (breakpoint_here_p (aspace, insn->pc))
> > +	    return btrace_step_stopped ();
> 
> How is adjust_pc_after_break handled?

I'm not doing anything special.  The PC register is writable,
so the PC can be adjusted.  There is already code to omit
the adjustment when reverse-executing.


> > +# navigate in the trace history for both threads
> > +gdb_test "thread 1" ".*" "mts, 1.1"
> > +gdb_test "record goto begin" ".*" "mts, 1.2"
> > +gdb_test "info record" ".*Replay in progress\.  At instruction 1\." "mts,
> 1.3"
> > +gdb_test "thread 2" ".*" "mts, 1.4"
> > +gdb_test "record goto begin" ".*" "mts, 1.5"
> > +gdb_test "info record" ".*Replay in progress\.  At instruction 1\." "mts,
> 1.6"
> 
> What does this "mts" that appears everywhere mean?
> (with_test_prefix could help here to create more meaningful test names.)

It's a simple tag so I find the failing test quickly.

With_test_prefix wouldn't really help.  I could use "mts, " as test prefix, but
this would leave the raw numbers "1.1", "1.2", ... in the tests.

I don't really need the prefix since the failing .exp file is already included
in the fail message.  If it is confusing, I might as well omit it and use the numbers
only.


> > diff --git a/gdb/testsuite/gdb.btrace/rn-dl-bind.exp
> b/gdb/testsuite/gdb.btrace/rn-dl-bind.exp
> > new file mode 100644
> > index 0000000..4d803f9
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.btrace/rn-dl-bind.exp
> 
> What's this testing?  I can't infer it from the test name.
> Please add a comment.

It's testing a reverse-next over the dynamic linker's symbol lookup
code.  I added a comment to the top of the test.

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

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

* Re: [patch v8 15/24] frame, backtrace: allow targets to supply a frame unwinder
  2013-12-16  9:18     ` Metzger, Markus T
@ 2013-12-16 19:02       ` Pedro Alves
  2013-12-17  8:26         ` Metzger, Markus T
  0 siblings, 1 reply; 73+ messages in thread
From: Pedro Alves @ 2013-12-16 19:02 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches

On 12/16/2013 09:18 AM, Metzger, Markus T wrote:

>> ... parts look a bit unrelated.  It'd be good to have
>> the rationale for this spelled out.  I can understand
>> why they might be necessary, but I don't see why
>> target vs non-unwinders unwinders would be special here?
> 
> Those changes are not directly related to target vs non-target
> unwinders.  They are connected in that they allow new types
> of unwinders.

> 
> I spelled out the rationale for each change in the commit
> message.  

If I missed it, please point me at it.  E.g., I didn't see a
rationale for the get_frame_unwind_stop_reason change.
Something like "We need to do X in get_frame_unwind_stop_reason
because otherwise, when we do Y, W doesn't work."

> Should I further split this patch into three?

IMO, yes.

Thanks,
-- 
Pedro Alves

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

* Re: [patch v8 17/24] record-btrace: provide xfer_partial target method
  2013-12-16 10:53     ` Metzger, Markus T
@ 2013-12-16 19:16       ` Pedro Alves
  2013-12-17 11:58         ` Metzger, Markus T
  0 siblings, 1 reply; 73+ messages in thread
From: Pedro Alves @ 2013-12-16 19:16 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches

On 12/16/2013 10:53 AM, Metzger, Markus T wrote:
>> -----Original Message-----
>> From: Pedro Alves [mailto:palves@redhat.com]
>> Sent: Friday, December 13, 2013 7:44 PM
> 
> 
>> I must be missing something, but won't raw_memory_xfer_partial
>> keep trying in the target beneath, anyway?
> 
> You're right.  This function did not have any effect - and that was good;
> at least to some extent.  Thanks for pointing this out!
> 
> I changed the return -1 to throw_error (...) and added a check for
> writebuf != NULL.  Suddenly I got tons of errors when GDB can't insert
> breakpoints any longer for (reverse-)stepping.

This is why precord keeps track of breakpoints itself too:

/* Behavior is conditional on RECORD_FULL_IS_REPLAY.  We will not actually
   insert or remove breakpoints in the real target when replaying, nor
   when recording.  */

static int
record_full_insert_breakpoint (struct gdbarch *gdbarch,
			       struct bp_target_info *bp_tgt)
{




>   Also stepping gets broken.

I can't immediately why that would be.

> 
> I still get the nice <unavailable> for function parameters.  This fails
> already when trying to access the SP for computing the CFA.
> 
> I now get an error when trying to access a variable with static storage
> duration or when trying to access memory directly via its address.
> It would be nice to also get an <unavailable> in those cases.  This would
> require the respective layer to catch my exception.

Please try returning TARGET_XFER_E_UNAVAILABLE instead.

For traceframes what we do is GDB knows what memory is available
or isn't available before requesting it, through traceframe_available_memory.

See also:
  https://sourceware.org/ml/gdb-patches/2013-10/msg00761.html
  (the discussion continued a little in the following month)

> To avoid those errors when trying to set breakpoints, I could try
> providing to_insert_breakpoint and to_remove_breakpoint methods
> and maintain my own breakpoints.

Right.

> Breakpoint conditions would still fail.  Not sure what else I'm missing.

-- 
Pedro Alves

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

* Re: [patch v8 21/24] record-btrace: extend unwinder
  2013-12-16 12:42     ` Metzger, Markus T
@ 2013-12-16 19:22       ` Pedro Alves
  2013-12-17 12:56         ` Metzger, Markus T
  0 siblings, 1 reply; 73+ messages in thread
From: Pedro Alves @ 2013-12-16 19:22 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches

On 12/16/2013 12:42 PM, Metzger, Markus T wrote:

>>> @@ -222,7 +222,11 @@ enum frame_type
>>>    ARCH_FRAME,
>>>    /* Sentinel or registers frame.  This frame obtains register values
>>>       direct from the inferior's registers.  */
>>> -  SENTINEL_FRAME
>>> +  SENTINEL_FRAME,
>>> +  /* A branch tracing frame.  */
>>> +  BTRACE_FRAME,
>>> +  /* A branch tracing tail call frame.  */
>>> +  BTRACE_TAILCALL_FRAME
>>>  };
>>
>> Hmm?  Why were these needed?  Missing patch rationale...  And missing
>> comments, I suppose.
> 
> The alternative would be to pretend that they are NORMAL_FRAME
> and TAILCALL_FRAME, respectively.  I thought it cleaner to add a new
> frame type.  Those frames don't have available stack.
> 
> Do you want me to use NORMAL_FRAME and TAILCALL_FRAME, instead?

If they behave example like those, then yes.  I wouldn't call
it "pretend" then.  This is IMO just like we don't have other
frames types for the many different implementations of
NORMAL_FRAMEs or SIGTRAP_FRAME unwinders, etc., and likewise for
when inspecting tracepoint traceframes with unavailable stack.
IOW, is there any place in the common code that needs to
distinguish NORMAL_FRAME vs BTRACE_FRAME and TAILCALL_FRAME
vs BTRACE_TAILCALL_FRAME?

-- 
Pedro Alves

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

* Re: [patch v8 22/24] btrace, gdbserver: read branch trace incrementally
  2013-12-16 12:47     ` Metzger, Markus T
@ 2013-12-16 19:23       ` Pedro Alves
  0 siblings, 0 replies; 73+ messages in thread
From: Pedro Alves @ 2013-12-16 19:23 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches

On 12/16/2013 12:45 PM, Metzger, Markus T wrote:
>> -----Original Message-----
>> From: Pedro Alves [mailto:palves@redhat.com]
>> Sent: Friday, December 13, 2013 8:46 PM
>> To: Metzger, Markus T
> 
> 
>>> +  /* The instructions vector may become empty temporarily if this has
>>> +     been the only instruction in this function segment.
>>> +     This violates the invariant but will be remedied shortly.  */
>>
>> By what?
> 
> By btrace_compute_ftrace when we add the new trace.
> 
> I added the above to the comment.

Excellent, thanks.

-- 
Pedro Alves

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

* Re: [patch v8 23/24] record-btrace: show trace from enable location
  2013-12-16 12:57     ` Metzger, Markus T
@ 2013-12-16 19:41       ` Pedro Alves
  2013-12-17 13:20         ` Metzger, Markus T
  0 siblings, 1 reply; 73+ messages in thread
From: Pedro Alves @ 2013-12-16 19:41 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches

On 12/16/2013 12:56 PM, Metzger, Markus T wrote:
>> -----Original Message-----
>> From: Pedro Alves [mailto:palves@redhat.com]
>> Sent: Friday, December 13, 2013 8:50 PM
> 
> 
>>> After adding incremental updates, we can now add a dummy record for the
>> current
>>> PC when we enable tracing so we show the trace from the location where
>> branch
>>> tracing has been enabled.
>>
>> Ah, awesome, I was just wondering about that.  :-)
>>
>> I suppose one can we still access the registers when
>> inspecting this dummy location, or are the btrace unwinders
>> preventing that?
> 
> You can not access registers when you are in the execution history.
> 
> When we add this dummy record when we enable tracing, you're still
> outside the execution history, so you can access registers.  Also the last
> instruction will be skipped for all record operations - it corresponds to
> the current PC and the instruction has not been executed yet.
> 
> But it allows us to stitch the first trace chunk to this dummy record and
> thus show the full trace from the enable location.
> 
> Internally, you're at the same PC if you are replaying and the replay
> position is at the last instruction or if you are not replaying.
> The different is that you will be able to access registers and memory
> only if you are not replaying.  

OK, the question was then, would it be wrong to allow
accessing registers and memory of the live program, if replaying, and
at that position?  Just OOC and for my education, I'm not saying
it'd be a requirement.

Sorry if the question sounds dumb.  I can't actually try out this
stuff due to broken btrace on my machine, if you'll recall.

> You will also have normal dward2 frames
> in that case.  If you are replaying, you will have btrace frames and you
> won't be able to access all registers and memory.
> 
> In record commands, we stop replaying once we reach the last instruction.

-- 
Pedro Alves

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

* Re: [patch v8 24/24] record-btrace: add (reverse-)stepping support
  2013-12-16 15:56     ` Metzger, Markus T
@ 2013-12-16 20:30       ` Pedro Alves
  2013-12-17 14:14         ` Metzger, Markus T
  0 siblings, 1 reply; 73+ messages in thread
From: Pedro Alves @ 2013-12-16 20:30 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches, Eli Zaretskii

On 12/16/2013 03:54 PM, Metzger, Markus T wrote:
>> -----Original Message-----
>> From: Pedro Alves [mailto:palves@redhat.com]
>> Sent: Friday, December 13, 2013 8:22 PM
> 
> Thanks for your review.
> 
> 
>>> +  /* Clear the executing flag to allow changes to the current frame.  */
>>> +  executing = is_executing (tp->ptid);
>>> +  set_executing (tp->ptid, 0);
>>
>> Why is this necessary?  Is this so you can start replaying
>> even when the current thread is really executing?
> 
> No.  When we just start replaying with a reverse stepping command,
> we need to recompute stack frames so we can compare them to
> detect subroutine calls in infrun.c.
> 
> See also https://sourceware.org/ml/gdb-patches/2013-11/msg00874.html
> and https://sourceware.org/ml/gdb-patches/2013-11/msg00891.html.

I guess this goes in a similar direction of what I was asking
in the other thread.  When we enable replaying, is still sitting
at the same location the live inferior is, it seems like if
you allow reading registers/memory from the live inferior,
then you can also use the regular dwarf unwinder for that frame,
and then you wouldn't need to frob step_frame_id, etc.

> 
> This function is called from to_wait to enable replaying as part of
> reverse stepping.  We need to temporarily set is_executing to false
> in order to be able to call get_current_frame below.

Ah, because target_set called set_executing (..., 1), right?
I forgot record full does something like that too.

> 
> I extended the existing comments here and below to explain this in more detail.
> 
>>> +  /* Restore the previous execution state.  */
>>> +  set_executing (tp->ptid, executing);
>>> +
>>> +  if (except.reason < 0)
>>> +    throw_exception (except);
>>
>> If something throws, things are being left
>> possibly in an inconsistent state, it seems to me.
>> Also, "replay" leaks.
> 
> Replay is stored in BTINFO.  We checked that we have trace before
> the try block, so btrace_insn_end () won't throw and we're not
> leaking REPLAY - we just transitioned from not replaying to replaying.
> 
> It may still be better to revert the changes and not start replaying
> in case of errors.  Changed that.  I'm also calling registers_changed_ptid
> and get_current_frame again to avoid leaving things behind that are
> based on replaying.  This might trigger another throw.

Sounds good, thanks.

>>> +  if (tp != NULL && !btrace_is_replaying (tp)
>>> +      && execution_direction != EXEC_REVERSE)
>>> +    ALL_THREADS (other)
>>> +      record_btrace_stop_replaying (other);
>>
>> Why's that?
> 
> Imagine the user does:
> 
> (gdb) thread 1
> (gdb) reverse-steop
> (gdb) thread 2
> (gdb) record goto 42
> (gdb) thread 3
> (gdb) continue
> 
> We could either error out and make him go to thread 1 and thread 2
> again to stop replaying those threads.  Or we could more or less
> silently stop replaying those other threads before we continue.
> 
> I chose to silently stop replaying; no warnings and no questions.
> I find both somewhat annoying after some time.

Hmm.  I see.  Please consider scheduler-locking though.  That is,

(gdb) set scheduler-locking on
(gdb) thread 1
(gdb) reverse-steop
(gdb) thread 2
(gdb) record goto 42
(gdb) thread 3
(gdb) continue

The last continue doesn't apply to threads 1 and 2, so not
clear what it should do to them then.  My knee jerk would
be to leave them alone.

> 
> 
>>> +  /* Find the thread to move.  */
>>> +  if (ptid_equal (minus_one_ptid, ptid) || ptid_is_pid (ptid))
>>> +    {
>>> +      ALL_THREADS (tp)
>>> +	record_btrace_resume_thread (tp, flag);
>>
>> Seems like this steps all threads, when gdb only wants to
>> step inferior_ptid and continue others?
> 
> Only if gdb passes -1 or the ptid of the process.

Yes, but -1 or "ptid of the process" means "step inferior_ptid
and let others run free".

> In the end, we will move exactly one thread and keep all
> others where they are.  This one thread will hit a breakpoint
> or run out of execution history.

Some of the other threads can hit a breakpoint before the
current thread hits one too.

> Are you suggesting that I should only mark inferior_ptid
> in this case?  If I marked the others as continue, I would later
> on need to prefer a thread that only wanted to step.
> 
> I'm preferring inferior_ptid in to_wait later on when the
> threads are actually moved.  The effect should be the same,
> but it might be more clear if I also did not mark the other
> threads.

It's better to not rely on inferior_ptid in to_wait at all.
E.g., in non-stop/async (I now you don't support it now, but
still), there's no connection between inferior_ptid and
the thread that was stepped when you get to target_wait.
It really can't --- in principle, how could core GDB
know which thread would get the event _before_ the target
reported it to the core exactly with target_wait ?

> 
> 
>>> +	  if (breakpoint_here_p (aspace, insn->pc))
>>> +	    return btrace_step_stopped ();
>>
>> How is adjust_pc_after_break handled?
> 
> I'm not doing anything special.  The PC register is writable,
> so the PC can be adjusted.

Eh, it's writable even when forward executing through history?

I see this in patch 14:

> +static void
> +record_btrace_store_registers (struct target_ops *ops,
> +			       struct regcache *regcache, int regno)
> +{
> +  struct target_ops *t;
> +
> +  if (record_btrace_is_replaying ())
> +    error (_("This record target does not allow writing registers."));
> +

Was it changed in later patches?

>>> +# navigate in the trace history for both threads
>>> +gdb_test "thread 1" ".*" "mts, 1.1"
>>> +gdb_test "record goto begin" ".*" "mts, 1.2"
>>> +gdb_test "info record" ".*Replay in progress\.  At instruction 1\." "mts,
>> 1.3"
>>> +gdb_test "thread 2" ".*" "mts, 1.4"
>>> +gdb_test "record goto begin" ".*" "mts, 1.5"
>>> +gdb_test "info record" ".*Replay in progress\.  At instruction 1\." "mts,
>> 1.6"
>>
>> What does this "mts" that appears everywhere mean?
>> (with_test_prefix could help here to create more meaningful test names.)
> 
> It's a simple tag so I find the failing test quickly.

Right, I was just wondering what is stood
for --  "Markus T ...", I guess?

> 
> With_test_prefix wouldn't really help.  I could use "mts, " as test prefix, but
> this would leave the raw numbers "1.1", "1.2", ... in the tests.

> I don't really need the prefix since the failing .exp file is already included
> in the fail message.  If it is confusing, I might as well omit it and use the numbers
> only.

I meant things like:

# navigate in the trace history for both threads
with_test_prefix "nativ-hist-both-thrds" {
  gdb_test "thread 1" ".*"
  with_test_prefix "thrd1" {
    gdb_test "record goto begin" ".*"
    gdb_test "info record" ".*Replay in progress\.  At instruction 1\."
  }
  gdb_test "thread 2" ".*"
  with_test_prefix "thrd2" {
    gdb_test "record goto begin" ".*"
    gdb_test "info record" ".*Replay in progress\.  At instruction 1\."
  }
}

This way you don't need numbers at all, and gdb.sum shows
something meaningful (if not specified, the gdb.sum out defaults
to the gdb command passed to gdb_test).  Some of the old gdb.trace/
tests also used numbers like this, and over time, was we
add/remove bits from the tests, we end up with outdated numbers,
holes in the numeric sequence, etc.  Sometimes you need to update
the numbers of the following tests to make room for one other tests.
Etc.

-- 
Pedro Alves

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

* RE: [patch v8 15/24] frame, backtrace: allow targets to supply a frame unwinder
  2013-12-16 19:02       ` Pedro Alves
@ 2013-12-17  8:26         ` Metzger, Markus T
  2013-12-17  9:48           ` Pedro Alves
  0 siblings, 1 reply; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-17  8:26 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches

> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Monday, December 16, 2013 8:02 PM


> >> ... parts look a bit unrelated.  It'd be good to have
> >> the rationale for this spelled out.  I can understand
> >> why they might be necessary, but I don't see why
> >> target vs non-unwinders unwinders would be special here?
> >
> > Those changes are not directly related to target vs non-target
> > unwinders.  They are connected in that they allow new types
> > of unwinders.
> 
> >
> > I spelled out the rationale for each change in the commit
> > message.
> 
> If I missed it, please point me at it.  E.g., I didn't see a
> rationale for the get_frame_unwind_stop_reason change.
> Something like "We need to do X in get_frame_unwind_stop_reason
> because otherwise, when we do Y, W doesn't work."

I meant that based on your review, I now spell out the rationale
for each change in the commit message.  I have not sent the updated
patch, yet.


> > Should I further split this patch into three?
> 
> IMO, yes.

OK.  I'll do that, as well.

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

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

* Re: [patch v8 15/24] frame, backtrace: allow targets to supply a frame unwinder
  2013-12-17  8:26         ` Metzger, Markus T
@ 2013-12-17  9:48           ` Pedro Alves
  0 siblings, 0 replies; 73+ messages in thread
From: Pedro Alves @ 2013-12-17  9:48 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches

On 12/17/2013 08:26 AM, Metzger, Markus T wrote:

>>> I spelled out the rationale for each change in the commit
>>> message.
>>
>> If I missed it, please point me at it.  E.g., I didn't see a
>> rationale for the get_frame_unwind_stop_reason change.
>> Something like "We need to do X in get_frame_unwind_stop_reason
>> because otherwise, when we do Y, W doesn't work."
> 
> I meant that based on your review, I now spell out the rationale
> for each change in the commit message.  I have not sent the updated
> patch, yet.

Ah, thanks.  Sorry for misunderstanding.

-- 
Pedro Alves

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

* RE: [patch v8 17/24] record-btrace: provide xfer_partial target method
  2013-12-16 19:16       ` Pedro Alves
@ 2013-12-17 11:58         ` Metzger, Markus T
  2013-12-17 16:56           ` Pedro Alves
  0 siblings, 1 reply; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-17 11:58 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches

> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Monday, December 16, 2013 8:16 PM


> > I changed the return -1 to throw_error (...) and added a check for
> > writebuf != NULL.  Suddenly I got tons of errors when GDB can't insert
> > breakpoints any longer for (reverse-)stepping.
> 
> This is why precord keeps track of breakpoints itself too:
[...] 
> >   Also stepping gets broken.
> 
> I can't immediately why that would be.

Because we can't set temporary breakpoints.


> > I now get an error when trying to access a variable with static storage
> > duration or when trying to access memory directly via its address.
> > It would be nice to also get an <unavailable> in those cases.  This would
> > require the respective layer to catch my exception.
> 
> Please try returning TARGET_XFER_E_UNAVAILABLE instead.

That is ignored just like the -1 I returned earlier.  I nevertheless changed
the default return to that since it is more descriptive.


> > To avoid those errors when trying to set breakpoints, I could try
> > providing to_insert_breakpoint and to_remove_breakpoint methods
> > and maintain my own breakpoints.
> 
> Right.

I have something to temporarily disable the xfer checks during
to_insert_breakpoint and to_remove_breakpoint.

Not sure whether this is considered too hacky or what else I'm missing.
My tests all pass.  Any idea where else GDB would need to access
target memory in order to function correctly?


Here's the patch.  I omit a preparation patch to pass target_ops to
to_insert_breakpoint and to_remove_breakpoint so that the request
can be forwarded to the target beneath.

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 00a056d..0536071 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -42,6 +42,9 @@ static struct target_ops record_btrace_ops;
 /* A new thread observer enabling branch tracing for the new thread.  */
 static struct observer *record_btrace_thread_observer;
 
+/* Temporarily allow memory accesses.  */
+static int record_btrace_allow_memory_access;
+
 /* Print a record-btrace debug message.  Use do ... while (0) to avoid
    ambiguities when used in if statements.  */
 
@@ -805,7 +808,7 @@ record_btrace_xfer_partial (struct target_ops *ops, enum target_object object,
   struct target_ops *t;
 
   /* Filter out requests that don't make sense during replay.  */
-  if (record_btrace_is_replaying ())
+  if (record_btrace_allow_memory_access == 0 && record_btrace_is_replaying ())
     {
       switch (object)
 	{
@@ -850,6 +853,58 @@ record_btrace_xfer_partial (struct target_ops *ops, enum target_object object,
   return TARGET_XFER_E_UNAVAILABLE;
 }
 
+/* The to_insert_breakpoint method of target record-btrace.  */
+
+static int
+record_btrace_insert_breakpoint (struct target_ops *ops,
+				 struct gdbarch *gdbarch,
+				 struct bp_target_info *bp_tgt)
+{
+  volatile struct gdb_exception except;
+  int old, ret;
+
+  /* Inserting breakpoints requires accessing memory.  Allow it for the
+     duration of this function.  */
+  old = record_btrace_allow_memory_access;
+  record_btrace_allow_memory_access = 1;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    ret = forward_target_insert_breakpoint (ops->beneath, gdbarch, bp_tgt);
+
+  record_btrace_allow_memory_access = old;
+
+  if (except.reason < 0)
+    throw_exception (except);
+
+  return ret;
+}
+
+/* The to_remove_breakpoint method of target record-btrace.  */
+
+static int
+record_btrace_remove_breakpoint (struct target_ops *ops,
+				 struct gdbarch *gdbarch,
+				 struct bp_target_info *bp_tgt)
+{
+  volatile struct gdb_exception except;
+  int old, ret;
+
+  /* Removing breakpoints requires accessing memory.  Allow it for the
+     duration of this function.  */
+  old = record_btrace_allow_memory_access;
+  record_btrace_allow_memory_access = 1;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    ret = forward_target_remove_breakpoint (ops->beneath, gdbarch, bp_tgt);
+
+  record_btrace_allow_memory_access = old;
+
+  if (except.reason < 0)
+    throw_exception (except);
+
+  return ret;
+}
+
 /* The to_fetch_registers method of target record-btrace.  */
 
 static void
@@ -1804,6 +1859,8 @@ init_record_btrace_ops (void)
   ops->to_call_history_range = record_btrace_call_history_range;
   ops->to_record_is_replaying = record_btrace_is_replaying;
   ops->to_xfer_partial = record_btrace_xfer_partial;
+  ops->to_remove_breakpoint = record_btrace_remove_breakpoint;
+  ops->to_insert_breakpoint = record_btrace_insert_breakpoint;
   ops->to_fetch_registers = record_btrace_fetch_registers;
   ops->to_store_registers = record_btrace_store_registers;
   ops->to_prepare_to_store = record_btrace_prepare_to_store;

Regards,
Markus.

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

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

* RE: [patch v8 21/24] record-btrace: extend unwinder
  2013-12-16 19:22       ` Pedro Alves
@ 2013-12-17 12:56         ` Metzger, Markus T
  0 siblings, 0 replies; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-17 12:56 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches

> -----Original Message-----
> From: gdb-patches-owner@sourceware.org [mailto:gdb-patches-
> owner@sourceware.org] On Behalf Of Pedro Alves
> Sent: Monday, December 16, 2013 8:23 PM


> >>> @@ -222,7 +222,11 @@ enum frame_type
> >>>    ARCH_FRAME,
> >>>    /* Sentinel or registers frame.  This frame obtains register values
> >>>       direct from the inferior's registers.  */
> >>> -  SENTINEL_FRAME
> >>> +  SENTINEL_FRAME,
> >>> +  /* A branch tracing frame.  */
> >>> +  BTRACE_FRAME,
> >>> +  /* A branch tracing tail call frame.  */
> >>> +  BTRACE_TAILCALL_FRAME
> >>>  };
> >>
> >> Hmm?  Why were these needed?  Missing patch rationale...  And missing
> >> comments, I suppose.
> >
> > The alternative would be to pretend that they are NORMAL_FRAME
> > and TAILCALL_FRAME, respectively.  I thought it cleaner to add a new
> > frame type.  Those frames don't have available stack.
> >
> > Do you want me to use NORMAL_FRAME and TAILCALL_FRAME, instead?
> 
> If they behave example like those, then yes.  I wouldn't call
> it "pretend" then.  This is IMO just like we don't have other
> frames types for the many different implementations of
> NORMAL_FRAMEs or SIGTRAP_FRAME unwinders, etc., and likewise for
> when inspecting tracepoint traceframes with unavailable stack.
> IOW, is there any place in the common code that needs to
> distinguish NORMAL_FRAME vs BTRACE_FRAME and TAILCALL_FRAME
> vs BTRACE_TAILCALL_FRAME?

They behave the same.  I'll remove the new frame types.

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

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

* RE: [patch v8 23/24] record-btrace: show trace from enable location
  2013-12-16 19:41       ` Pedro Alves
@ 2013-12-17 13:20         ` Metzger, Markus T
  2013-12-17 16:59           ` Pedro Alves
  0 siblings, 1 reply; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-17 13:20 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches

> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Monday, December 16, 2013 8:41 PM


> >>> After adding incremental updates, we can now add a dummy record for
> the
> >> current
> >>> PC when we enable tracing so we show the trace from the location
> where
> >> branch
> >>> tracing has been enabled.
> >>
> >> Ah, awesome, I was just wondering about that.  :-)
> >>
> >> I suppose one can we still access the registers when
> >> inspecting this dummy location, or are the btrace unwinders
> >> preventing that?
> >
> > You can not access registers when you are in the execution history.
> >
> > When we add this dummy record when we enable tracing, you're still
> > outside the execution history, so you can access registers.  Also the last
> > instruction will be skipped for all record operations - it corresponds to
> > the current PC and the instruction has not been executed yet.
> >
> > But it allows us to stitch the first trace chunk to this dummy record and
> > thus show the full trace from the enable location.
> >
> > Internally, you're at the same PC if you are replaying and the replay
> > position is at the last instruction or if you are not replaying.
> > The different is that you will be able to access registers and memory
> > only if you are not replaying.
> 
> OK, the question was then, would it be wrong to allow
> accessing registers and memory of the live program, if replaying, and
> at that position?  Just OOC and for my education, I'm not saying
> it'd be a requirement.

It would be conceptually wrong.

And it would not be necessary, either, since we will never be in
this position when the user gets the prompt.  We will only pass
through it internally.  When the user gets the prompt, we are
either replaying and we're somewhere in the execution history.
Or we're not replaying and we're at the current location.


> Sorry if the question sounds dumb.  I can't actually try out this
> stuff due to broken btrace on my machine, if you'll recall.

That's sad, indeed.  For one, this is a really nice feature.  And also
it prevents you from finding (and reporting) bugs that I don't find.

Regards,
Markus.


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

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

* RE: [patch v8 24/24] record-btrace: add (reverse-)stepping support
  2013-12-16 20:30       ` Pedro Alves
@ 2013-12-17 14:14         ` Metzger, Markus T
  2013-12-17 15:07           ` Metzger, Markus T
  2013-12-17 20:07           ` Pedro Alves
  0 siblings, 2 replies; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-17 14:14 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches, Eli Zaretskii

> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Monday, December 16, 2013 9:30 PM
> To: Metzger, Markus T


> > No.  When we just start replaying with a reverse stepping command,
> > we need to recompute stack frames so we can compare them to
> > detect subroutine calls in infrun.c.
> >
> > See also https://sourceware.org/ml/gdb-patches/2013-11/msg00874.html
> > and https://sourceware.org/ml/gdb-patches/2013-11/msg00891.html.
> 
> I guess this goes in a similar direction of what I was asking
> in the other thread.  When we enable replaying, is still sitting
> at the same location the live inferior is, it seems like if
> you allow reading registers/memory from the live inferior,
> then you can also use the regular dwarf unwinder for that frame,
> and then you wouldn't need to frob step_frame_id, etc.

I can't.  Because after the first reverse-single-step, I will be somewhere
in the execution history and suddenly, the frame_id of my caller does
no longer compare equal to the frame_id we started stepping - assuming
I stepped into a subroutine.

I'm using the fact that I'm actually at the same position as the live
target, yet I'm already replaying, so I get the btrace frame_id's I need
later on.


> > This function is called from to_wait to enable replaying as part of
> > reverse stepping.  We need to temporarily set is_executing to false
> > in order to be able to call get_current_frame below.
> 
> Ah, because target_set called set_executing (..., 1), right?
> I forgot record full does something like that too.

I don't know who does it but it is set when I am called.


> >>> +  if (tp != NULL && !btrace_is_replaying (tp)
> >>> +      && execution_direction != EXEC_REVERSE)
> >>> +    ALL_THREADS (other)
> >>> +      record_btrace_stop_replaying (other);
> >>
> >> Why's that?
> >
> > Imagine the user does:
> >
> > (gdb) thread 1
> > (gdb) reverse-steop
> > (gdb) thread 2
> > (gdb) record goto 42
> > (gdb) thread 3
> > (gdb) continue
> >
> > We could either error out and make him go to thread 1 and thread 2
> > again to stop replaying those threads.  Or we could more or less
> > silently stop replaying those other threads before we continue.
> >
> > I chose to silently stop replaying; no warnings and no questions.
> > I find both somewhat annoying after some time.
> 
> Hmm.  I see.  Please consider scheduler-locking though.  That is,
> 
> (gdb) set scheduler-locking on
> (gdb) thread 1
> (gdb) reverse-steop
> (gdb) thread 2
> (gdb) record goto 42
> (gdb) thread 3
> (gdb) continue
> 
> The last continue doesn't apply to threads 1 and 2, so not
> clear what it should do to them then.  My knee jerk would
> be to leave them alone.

Does scheduler locking set inferior_ptid?
If it does, we should already get the desired behaviour.
If it doesn't, how would I know which thread to step?


> >>> +  /* Find the thread to move.  */
> >>> +  if (ptid_equal (minus_one_ptid, ptid) || ptid_is_pid (ptid))
> >>> +    {
> >>> +      ALL_THREADS (tp)
> >>> +	record_btrace_resume_thread (tp, flag);
> >>
> >> Seems like this steps all threads, when gdb only wants to
> >> step inferior_ptid and continue others?
> >
> > Only if gdb passes -1 or the ptid of the process.
> 
> Yes, but -1 or "ptid of the process" means "step inferior_ptid
> and let others run free".

See below.


> > In the end, we will move exactly one thread and keep all
> > others where they are.  This one thread will hit a breakpoint
> > or run out of execution history.
> 
> Some of the other threads can hit a breakpoint before the
> current thread hits one too.

That depends on thread interleaving.  There is no guarantee that
the other threads make progress.  The way it is currently
implemented, we step the chosen thread until the next event.
This event is either a breakpoint, step completed, or out of
execution history.

We're effectively only stepping a single thread.  I believe this
is also how the s/w record target works.

The alternative would be to lock-step all threads, i.e. single-step
each thread until the first thread produces an event.  We could
switch between those two based on scheduler-locking.


> > Are you suggesting that I should only mark inferior_ptid
> > in this case?  If I marked the others as continue, I would later
> > on need to prefer a thread that only wanted to step.
> >
> > I'm preferring inferior_ptid in to_wait later on when the
> > threads are actually moved.  The effect should be the same,
> > but it might be more clear if I also did not mark the other
> > threads.
> 
> It's better to not rely on inferior_ptid in to_wait at all.
> E.g., in non-stop/async (I now you don't support it now, but
> still), there's no connection between inferior_ptid and
> the thread that was stepped when you get to target_wait.
> It really can't --- in principle, how could core GDB
> know which thread would get the event _before_ the target
> reported it to the core exactly with target_wait ?

OK.  So in to_wait I only step marked threads.  We can still
extend it to lock-stepping later on by marking more than one
thread.

But this means that I would rely on inferior_ptid in to_resume
if -1 or the process id is passed.


> >>> +	  if (breakpoint_here_p (aspace, insn->pc))
> >>> +	    return btrace_step_stopped ();
> >>
> >> How is adjust_pc_after_break handled?
> >
> > I'm not doing anything special.  The PC register is writable,
> > so the PC can be adjusted.
> 
> Eh, it's writable even when forward executing through history?
> 
> I see this in patch 14:
> 
> > +static void
> > +record_btrace_store_registers (struct target_ops *ops,
> > +			       struct regcache *regcache, int regno)
> > +{
> > +  struct target_ops *t;
> > +
> > +  if (record_btrace_is_replaying ())
> > +    error (_("This record target does not allow writing registers."));
> > +
> 
> Was it changed in later patches?

Oops.  You are right.  It is read-only.

After some playing around, I also managed to trigger the error
when adjust_pc_after_break tries to write the adjusted PC.

I'm inclined to simply allow it.  The adjusted PC will be ignored for
stepping, of course.  We just follow the recorded execution.

Concerns?


> I meant things like:
> 
> # navigate in the trace history for both threads
> with_test_prefix "nativ-hist-both-thrds" {
>   gdb_test "thread 1" ".*"
>   with_test_prefix "thrd1" {
>     gdb_test "record goto begin" ".*"
>     gdb_test "info record" ".*Replay in progress\.  At instruction 1\."
>   }
>   gdb_test "thread 2" ".*"
>   with_test_prefix "thrd2" {
>     gdb_test "record goto begin" ".*"
>     gdb_test "info record" ".*Replay in progress\.  At instruction 1\."
>   }
> }
> 
> This way you don't need numbers at all, and gdb.sum shows
> something meaningful (if not specified, the gdb.sum out defaults
> to the gdb command passed to gdb_test).  Some of the old gdb.trace/
> tests also used numbers like this, and over time, was we
> add/remove bits from the tests, we end up with outdated numbers,
> holes in the numeric sequence, etc.  Sometimes you need to update
> the numbers of the following tests to make room for one other tests.
> Etc.

I already spent some time renumbering...

OK, I'll use test prefixes for the different test groups.

Regards,
Markus.

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

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

* RE: [patch v8 24/24] record-btrace: add (reverse-)stepping support
  2013-12-17 14:14         ` Metzger, Markus T
@ 2013-12-17 15:07           ` Metzger, Markus T
  2013-12-17 15:48             ` Metzger, Markus T
  2013-12-17 20:34             ` Pedro Alves
  2013-12-17 20:07           ` Pedro Alves
  1 sibling, 2 replies; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-17 15:07 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches, Eli Zaretskii

> -----Original Message-----
> From: Metzger, Markus T
> Sent: Tuesday, December 17, 2013 3:15 PM
> To: Pedro Alves


> > >>> +	  if (breakpoint_here_p (aspace, insn->pc))
> > >>> +	    return btrace_step_stopped ();
> > >>
> > >> How is adjust_pc_after_break handled?
> > >
> > > I'm not doing anything special.  The PC register is writable,
> > > so the PC can be adjusted.
> >
> > Eh, it's writable even when forward executing through history?
> >
> > I see this in patch 14:
> >
> > > +static void
> > > +record_btrace_store_registers (struct target_ops *ops,
> > > +			       struct regcache *regcache, int regno)
> > > +{
> > > +  struct target_ops *t;
> > > +
> > > +  if (record_btrace_is_replaying ())
> > > +    error (_("This record target does not allow writing registers."));
> > > +
> >
> > Was it changed in later patches?
> 
> Oops.  You are right.  It is read-only.
> 
> After some playing around, I also managed to trigger the error
> when adjust_pc_after_break tries to write the adjusted PC.
> 
> I'm inclined to simply allow it.  The adjusted PC will be ignored for
> stepping, of course.  We just follow the recorded execution.
> 
> Concerns?

Besides RIP, we also write orig_rax, so we still fail.

I'm beginning to wonder if we should actually adjust the PC for
btrace replay.  The way I interpret the function, we got a SIGTRAP
for executing the breakpoint instruction.  The PC is after the breakpoint
instruction, so it is one byte after the actual breakpoint location.  We're
now trying to undo the execution of the breakpoint instruction.
Is this correct?

In that case, we shouldn't adjust the PC since we did not execute
the breakpoint instruction in the trace.  We just asked whether there
is a breakpoint at the current location.

This seems to be different for s/w record.  This is the first time, I need
to distinguish between the different record targets.

I would add a new target method to_omit_pc_after_break_adjustment
that would default to false if not present.  The btrace record target will
provide it and return true if we're replaying.

OK with that?


Regards,
Markus.

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

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

* RE: [patch v8 24/24] record-btrace: add (reverse-)stepping support
  2013-12-17 15:07           ` Metzger, Markus T
@ 2013-12-17 15:48             ` Metzger, Markus T
  2013-12-17 20:41               ` Pedro Alves
  2013-12-17 20:34             ` Pedro Alves
  1 sibling, 1 reply; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-17 15:48 UTC (permalink / raw)
  To: Pedro Alves, jan.kratochvil; +Cc: gdb-patches, Eli Zaretskii

> -----Original Message-----
> From: Metzger, Markus T
> Sent: Tuesday, December 17, 2013 4:06 PM
> To: Pedro Alves


> I'm beginning to wonder if we should actually adjust the PC for
> btrace replay.  The way I interpret the function, we got a SIGTRAP
> for executing the breakpoint instruction.  The PC is after the breakpoint
> instruction, so it is one byte after the actual breakpoint location.  We're
> now trying to undo the execution of the breakpoint instruction.
> Is this correct?
> 
> In that case, we shouldn't adjust the PC since we did not execute
> the breakpoint instruction in the trace.  We just asked whether there
> is a breakpoint at the current location.
> 
> This seems to be different for s/w record.  This is the first time, I need
> to distinguish between the different record targets.

The s/w record target also does not execute the breakpoint instruction.
It increments the PC by gdbarch_decr_pc_after_break () when it finds a
s/w breakpoint at the current location.

I wonder why they are doing that.  Is this a workaround that was considered
less intrusive than what I am proposing below?  Or are there more places
in GDB that expect the PC to be past the breakpoint instruction?

I could do the same, of course.  Not sure which version is more desirable.


> I would add a new target method to_omit_pc_after_break_adjustment
> that would default to false if not present.  The btrace record target will
> provide it and return true if we're replaying.
> 
> OK with that?

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

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

* Re: [patch v8 17/24] record-btrace: provide xfer_partial target method
  2013-12-17 11:58         ` Metzger, Markus T
@ 2013-12-17 16:56           ` Pedro Alves
  2013-12-18  9:26             ` Metzger, Markus T
  0 siblings, 1 reply; 73+ messages in thread
From: Pedro Alves @ 2013-12-17 16:56 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches

On 12/17/2013 11:57 AM, Metzger, Markus T wrote:
>> -----Original Message-----
>> From: Pedro Alves [mailto:palves@redhat.com]
>> Sent: Monday, December 16, 2013 8:16 PM
> 
> 
>>> I changed the return -1 to throw_error (...) and added a check for
>>> writebuf != NULL.  Suddenly I got tons of errors when GDB can't insert
>>> breakpoints any longer for (reverse-)stepping.
>>
>> This is why precord keeps track of breakpoints itself too:
> [...] 
>>>   Also stepping gets broken.
>>
>> I can't immediately why that would be.
> 
> Because we can't set temporary breakpoints.
> 
> 
>>> I now get an error when trying to access a variable with static storage
>>> duration or when trying to access memory directly via its address.
>>> It would be nice to also get an <unavailable> in those cases.  This would
>>> require the respective layer to catch my exception.
>>
>> Please try returning TARGET_XFER_E_UNAVAILABLE instead.
> 
> That is ignored just like the -1 I returned earlier.  I nevertheless changed
> the default return to that since it is more descriptive.

Thanks.  Hmm, yes, looks like raw_memory_xfer_partial carries on
looking at the target beneath, and then when that fails, we'll
lose TARGET_XFER_E_UNAVAILABLE, and return TARGET_XFER_E_IO/-1,
losing the better TARGET_XFER_E_UNAVAILABLE.

>>> To avoid those errors when trying to set breakpoints, I could try
>>> providing to_insert_breakpoint and to_remove_breakpoint methods
>>> and maintain my own breakpoints.
>>
>> Right.
> 
> I have something to temporarily disable the xfer checks during
> to_insert_breakpoint and to_remove_breakpoint.
> 
> Not sure whether this is considered too hacky or what else I'm missing.

It's hacky as the breakpoints in memory will never actually
trigger/execute.  If you want to assume that the inferior's current
read only sections match exactly the read only sections the program
had when the trace was taken, I won't insist.  The assumption will
fail across tracing e.g., dlopen/dlclose/mmap/unmmap, as breakpoints
will fail to insert on unmapped sections.

> My tests all pass.  Any idea where else GDB would need to access
> target memory in order to function correctly?

Can't think of anything.

> Here's the patch.  I omit a preparation patch to pass target_ops to
> to_insert_breakpoint and to_remove_breakpoint so that the request
> can be forwarded to the target beneath.
> 
> diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
> index 00a056d..0536071 100644
> --- a/gdb/record-btrace.c
> +++ b/gdb/record-btrace.c
> @@ -42,6 +42,9 @@ static struct target_ops record_btrace_ops;
>  /* A new thread observer enabling branch tracing for the new thread.  */
>  static struct observer *record_btrace_thread_observer;
>  
> +/* Temporarily allow memory accesses.  */
> +static int record_btrace_allow_memory_access;
> +
>  /* Print a record-btrace debug message.  Use do ... while (0) to avoid
>     ambiguities when used in if statements.  */
>  
> @@ -805,7 +808,7 @@ record_btrace_xfer_partial (struct target_ops *ops, enum target_object object,
>    struct target_ops *t;
>  
>    /* Filter out requests that don't make sense during replay.  */
> -  if (record_btrace_is_replaying ())
> +  if (record_btrace_allow_memory_access == 0 && record_btrace_is_replaying ())

We use ! for boolean ints, so write:

  if (!record_btrace_allow_memory_access && record_btrace_is_replaying ())

-- 
Pedro Alves

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

* Re: [patch v8 23/24] record-btrace: show trace from enable location
  2013-12-17 13:20         ` Metzger, Markus T
@ 2013-12-17 16:59           ` Pedro Alves
  0 siblings, 0 replies; 73+ messages in thread
From: Pedro Alves @ 2013-12-17 16:59 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches

On 12/17/2013 01:20 PM, Metzger, Markus T wrote:

>> OK, the question was then, would it be wrong to allow
>> accessing registers and memory of the live program, if replaying, and
>> at that position?  Just OOC and for my education, I'm not saying
>> it'd be a requirement.
> 
> It would be conceptually wrong.
> 
> And it would not be necessary, either, since we will never be in
> this position when the user gets the prompt.  We will only pass
> through it internally.  When the user gets the prompt, we are
> either replaying and we're somewhere in the execution history.
> Or we're not replaying and we're at the current location.

OK, thanks.

-- 
Pedro Alves

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

* Re: [patch v8 24/24] record-btrace: add (reverse-)stepping support
  2013-12-17 14:14         ` Metzger, Markus T
  2013-12-17 15:07           ` Metzger, Markus T
@ 2013-12-17 20:07           ` Pedro Alves
  2013-12-18  9:44             ` Metzger, Markus T
  1 sibling, 1 reply; 73+ messages in thread
From: Pedro Alves @ 2013-12-17 20:07 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches, Eli Zaretskii

On 12/17/2013 02:14 PM, Metzger, Markus T wrote:

> Does scheduler locking set inferior_ptid?
> If it does, we should already get the desired behaviour.

It does, but only by chance.  The thread to resume will
be in ptid passed to target_resume.  But I confirm
the code already does the right thing.


> 
> 
>>>>> +  /* Find the thread to move.  */
>>>>> +  if (ptid_equal (minus_one_ptid, ptid) || ptid_is_pid (ptid))
>>>>> +    {
>>>>> +      ALL_THREADS (tp)
>>>>> +	record_btrace_resume_thread (tp, flag);
>>>>
>>>> Seems like this steps all threads, when gdb only wants to
>>>> step inferior_ptid and continue others?
>>>
>>> Only if gdb passes -1 or the ptid of the process.
>>
>> Yes, but -1 or "ptid of the process" means "step inferior_ptid
>> and let others run free".
> 
> See below.
> 
> 
>>> In the end, we will move exactly one thread and keep all
>>> others where they are.  This one thread will hit a breakpoint
>>> or run out of execution history.
>>
>> Some of the other threads can hit a breakpoint before the
>> current thread hits one too.
> 
> That depends on thread interleaving.  There is no guarantee that
> the other threads make progress.  The way it is currently
> implemented, we step the chosen thread until the next event.
> This event is either a breakpoint, step completed, or out of
> execution history.
> 
> We're effectively only stepping a single thread.  I believe this
> is also how the s/w record target works.

I'd understand that if the code looked obviously like but,
how the quoted code above seems to be resuming/stepping all
threads?

  /* Find the thread to move.  */
  if (ptid_equal (minus_one_ptid, ptid) || ptid_is_pid (ptid))
    {
      ALL_THREADS (tp)
	record_btrace_resume_thread (tp, flag);

> But this means that I would rely on inferior_ptid in to_resume
> if -1 or the process id is passed.

That's fine.  target_resume is telling the target "resume _this_",
albeit getting what "this" is has a weird interface.  It's
target_wait that's the problem.  The user can have e.g., thread 3
selected (meaning inferior_ptid points at thread 3), while
target_wait returns an event for some other thread.  There's
really no connection here.

-- 
Pedro Alves

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

* Re: [patch v8 24/24] record-btrace: add (reverse-)stepping support
  2013-12-17 15:07           ` Metzger, Markus T
  2013-12-17 15:48             ` Metzger, Markus T
@ 2013-12-17 20:34             ` Pedro Alves
  1 sibling, 0 replies; 73+ messages in thread
From: Pedro Alves @ 2013-12-17 20:34 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches, Eli Zaretskii

On 12/17/2013 03:06 PM, Metzger, Markus T wrote:
>> -----Original Message-----
>> From: Metzger, Markus T
>> Sent: Tuesday, December 17, 2013 3:15 PM
>> To: Pedro Alves
> 
> 
>>>>>> +	  if (breakpoint_here_p (aspace, insn->pc))
>>>>>> +	    return btrace_step_stopped ();
>>>>>
>>>>> How is adjust_pc_after_break handled?
>>>>
>>>> I'm not doing anything special.  The PC register is writable,
>>>> so the PC can be adjusted.
>>>
>>> Eh, it's writable even when forward executing through history?
>>>
>>> I see this in patch 14:
>>>
>>>> +static void
>>>> +record_btrace_store_registers (struct target_ops *ops,
>>>> +			       struct regcache *regcache, int regno)
>>>> +{
>>>> +  struct target_ops *t;
>>>> +
>>>> +  if (record_btrace_is_replaying ())
>>>> +    error (_("This record target does not allow writing registers."));
>>>> +
>>>
>>> Was it changed in later patches?
>>
>> Oops.  You are right.  It is read-only.
>>
>> After some playing around, I also managed to trigger the error
>> when adjust_pc_after_break tries to write the adjusted PC.
>>
>> I'm inclined to simply allow it.  The adjusted PC will be ignored for
>> stepping, of course.  We just follow the recorded execution.
>>
>> Concerns?
> 
> Besides RIP, we also write orig_rax, so we still fail.
> 
> I'm beginning to wonder if we should actually adjust the PC for
> btrace replay.  The way I interpret the function, we got a SIGTRAP
> for executing the breakpoint instruction.  The PC is after the breakpoint
> instruction, so it is one byte after the actual breakpoint location.  We're
> now trying to undo the execution of the breakpoint instruction.
> Is this correct?
> 
> In that case, we shouldn't adjust the PC since we did not execute
> the breakpoint instruction in the trace.  We just asked whether there
> is a breakpoint at the current location.
> 
> This seems to be different for s/w record.  This is the first time, I need
> to distinguish between the different record targets.
> 
> I would add a new target method to_omit_pc_after_break_adjustment
> that would default to false if not present.  The btrace record target will
> provide it and return true if we're replaying.

My view here (and this is the same thing with most simulators or
some kind that implement breakpoints on their own, instead of having
the program execute trap instructions) is that gdb actually should
have known that it inserted a hardware breakpoint, not a
software breakpoint (and perhaps get a different TARGET_WAITKIND_FOO
for those).  But that'd be quite an overhaul.

I think better even would be to provide a to_decr_pc_after_break
target method whose default would be to return gdbarch_decr_pc_after_break.
Then, make btrace record override this target method when replaying.

-- 
Pedro Alves

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

* Re: [patch v8 24/24] record-btrace: add (reverse-)stepping support
  2013-12-17 15:48             ` Metzger, Markus T
@ 2013-12-17 20:41               ` Pedro Alves
  0 siblings, 0 replies; 73+ messages in thread
From: Pedro Alves @ 2013-12-17 20:41 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches, Eli Zaretskii

On 12/17/2013 03:47 PM, Metzger, Markus T wrote:
>> -----Original Message-----
>> From: Metzger, Markus T
>> Sent: Tuesday, December 17, 2013 4:06 PM
>> To: Pedro Alves
> 
> 
>> I'm beginning to wonder if we should actually adjust the PC for
>> btrace replay.  The way I interpret the function, we got a SIGTRAP
>> for executing the breakpoint instruction.  The PC is after the breakpoint
>> instruction, so it is one byte after the actual breakpoint location.  We're
>> now trying to undo the execution of the breakpoint instruction.
>> Is this correct?
>>
>> In that case, we shouldn't adjust the PC since we did not execute
>> the breakpoint instruction in the trace.  We just asked whether there
>> is a breakpoint at the current location.
>>
>> This seems to be different for s/w record.  This is the first time, I need
>> to distinguish between the different record targets.
> 
> The s/w record target also does not execute the breakpoint instruction.
> It increments the PC by gdbarch_decr_pc_after_break () when it finds a
> s/w breakpoint at the current location.
> 
> I wonder why they are doing that.  Is this a workaround that was considered
> less intrusive than what I am proposing below?  

It just seemed better at the time.  When it was originally submitted
the precord target was a lot more invasive in the core side, and
I pushed for redoing it as a target (new stratum), and make it
as transparent as possible.  The current decr_pc_after_break
handling was a result of that mindset.  I'm not seeing a reason
your suggested direction couldn't work for the s/w record target
too.

-- 
Pedro Alves

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

* RE: [patch v8 17/24] record-btrace: provide xfer_partial target method
  2013-12-17 16:56           ` Pedro Alves
@ 2013-12-18  9:26             ` Metzger, Markus T
  2013-12-18 10:39               ` Pedro Alves
  0 siblings, 1 reply; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-18  9:26 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches

> -----Original Message-----
> From: gdb-patches-owner@sourceware.org [mailto:gdb-patches-
> owner@sourceware.org] On Behalf Of Pedro Alves


> > I have something to temporarily disable the xfer checks during
> > to_insert_breakpoint and to_remove_breakpoint.
> >
> > Not sure whether this is considered too hacky or what else I'm missing.
> 
> It's hacky as the breakpoints in memory will never actually
> trigger/execute.  If you want to assume that the inferior's current
> read only sections match exactly the read only sections the program
> had when the trace was taken, I won't insist.  The assumption will
> fail across tracing e.g., dlopen/dlclose/mmap/unmmap, as breakpoints
> will fail to insert on unmapped sections.

Right.  I might as well just ignore insert and remove breakpoints.

When I do that, however, I might leak breakpoint instructions for
breakpoints that have been deleted while replaying.  Or I might end
up with breakpoints that have not been properly inserted.

The easiest choice seems to be to actually insert and remove breakpoint
instructions into the target memory.

This will fail if the current process image does not match the process
image at the time of the recorded execution.  This is currently not
supported and will fail for various other reasons, as well.  For one,
I won't be able to disassemble instructions that are no longer there
or might have been replaced meanwhile.

Regards,
Markus.

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

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

* RE: [patch v8 24/24] record-btrace: add (reverse-)stepping support
  2013-12-17 20:07           ` Pedro Alves
@ 2013-12-18  9:44             ` Metzger, Markus T
  0 siblings, 0 replies; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-18  9:44 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches, Eli Zaretskii

> -----Original Message-----
> From: gdb-patches-owner@sourceware.org [mailto:gdb-patches-
> owner@sourceware.org] On Behalf Of Pedro Alves


> I'd understand that if the code looked obviously like but,
> how the quoted code above seems to be resuming/stepping all
> threads?
> 
>   /* Find the thread to move.  */
>   if (ptid_equal (minus_one_ptid, ptid) || ptid_is_pid (ptid))
>     {
>       ALL_THREADS (tp)
> 	record_btrace_resume_thread (tp, flag);

I removed this.
This case is handled in record_btrace_find_resume_thread, where
we use inferior_ptid to find the thread to resume.

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

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

* Re: [patch v8 17/24] record-btrace: provide xfer_partial target method
  2013-12-18  9:26             ` Metzger, Markus T
@ 2013-12-18 10:39               ` Pedro Alves
  0 siblings, 0 replies; 73+ messages in thread
From: Pedro Alves @ 2013-12-18 10:39 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches

On 12/18/2013 09:24 AM, Metzger, Markus T wrote:

> The easiest choice seems to be to actually insert and remove breakpoint
> instructions into the target memory.

Right.  Let's do that.

-- 
Pedro Alves

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

* RE: [patch v8 05/24] frame: artificial frame id's
  2013-12-13 17:51               ` Pedro Alves
@ 2013-12-18 13:30                 ` Metzger, Markus T
  0 siblings, 0 replies; 73+ messages in thread
From: Metzger, Markus T @ 2013-12-18 13:30 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Jan Kratochvil, gdb-patches

> -----Original Message-----
> From: gdb-patches-owner@sourceware.org [mailto:gdb-patches-
> owner@sourceware.org] On Behalf Of Pedro Alves
> Sent: Friday, December 13, 2013 6:51 PM
> Cc: Metzger, Markus T; Jan Kratochvil; gdb-patches@sourceware.org
> Subject: Re: [patch v8 05/24] frame: artificial frame id's
> 
> On 12/13/2013 03:44 PM, Pedro Alves wrote:
> > Yeah.  Let me clean up that patch a little (and write a test
> > for it), and you should then able to build such frames, and
> > I don't think you'll need to change frame_id_eq then.
> 
> https://sourceware.org/ml/gdb-patches/2013-12/msg00535.html

Thanks.  I'm using this instead of my own frame_id changes and it
works fine.

I will need some more time for those test changes you requested;
they require a fair amount of editing.  Once they are done, I will
send an update of the btrace series - which grew to 29 patches.

Regards,
Markus.

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

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

end of thread, other threads:[~2013-12-18 13:30 UTC | newest]

Thread overview: 73+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-12-12  9:15 [patch v8 00/24] record-btrace: reverse Markus Metzger
2013-12-12  9:15 ` [patch v8 03/24] gdbarch: add instruction predicate methods Markus Metzger
2013-12-12  9:15 ` [patch v8 01/24] btrace, linux: fix memory leak when reading branch trace Markus Metzger
2013-12-12  9:15 ` [patch v8 14/24] record-btrace: supply register target methods Markus Metzger
2013-12-12  9:15 ` [patch v8 16/24] record-btrace, frame: supply target-specific unwinder Markus Metzger
2013-12-12  9:16 ` [patch v8 06/24] btrace: change branch trace data structure Markus Metzger
2013-12-12  9:16 ` [patch v8 18/24] record-btrace: add to_wait and to_resume target methods Markus Metzger
2013-12-12  9:16 ` [patch v8 10/24] record-btrace: optionally indent function call history Markus Metzger
2013-12-12  9:16 ` [patch v8 02/24] btrace: uppercase btrace_read_type Markus Metzger
2013-12-12  9:16 ` [patch v8 09/24] btrace: increase buffer size Markus Metzger
2013-12-12  9:16 ` [patch v8 08/24] record-btrace: start counting at one Markus Metzger
2013-12-12  9:16 ` [patch v8 21/24] record-btrace: extend unwinder Markus Metzger
2013-12-13 19:45   ` Pedro Alves
2013-12-16 12:42     ` Metzger, Markus T
2013-12-16 19:22       ` Pedro Alves
2013-12-17 12:56         ` Metzger, Markus T
2013-12-12  9:16 ` [patch v8 20/24] record-btrace: add record goto target methods Markus Metzger
2013-12-12  9:16 ` [patch v8 12/24] btrace: add replay position to btrace thread info Markus Metzger
2013-12-12  9:16 ` [patch v8 24/24] record-btrace: add (reverse-)stepping support Markus Metzger
2013-12-12 16:36   ` Eli Zaretskii
2013-12-13 19:22   ` Pedro Alves
2013-12-16 15:56     ` Metzger, Markus T
2013-12-16 20:30       ` Pedro Alves
2013-12-17 14:14         ` Metzger, Markus T
2013-12-17 15:07           ` Metzger, Markus T
2013-12-17 15:48             ` Metzger, Markus T
2013-12-17 20:41               ` Pedro Alves
2013-12-17 20:34             ` Pedro Alves
2013-12-17 20:07           ` Pedro Alves
2013-12-18  9:44             ` Metzger, Markus T
2013-12-12  9:16 ` [patch v8 13/24] target: add ops parameter to to_prepare_to_store method Markus Metzger
2013-12-12  9:16 ` [patch v8 15/24] frame, backtrace: allow targets to supply a frame unwinder Markus Metzger
2013-12-13 18:27   ` Pedro Alves
2013-12-16  9:18     ` Metzger, Markus T
2013-12-16 19:02       ` Pedro Alves
2013-12-17  8:26         ` Metzger, Markus T
2013-12-17  9:48           ` Pedro Alves
2013-12-12  9:16 ` [patch v8 07/24] record-btrace: fix insn range in function call history Markus Metzger
2013-12-12  9:16 ` [patch v8 23/24] record-btrace: show trace from enable location Markus Metzger
2013-12-13 19:50   ` Pedro Alves
2013-12-16 12:57     ` Metzger, Markus T
2013-12-16 19:41       ` Pedro Alves
2013-12-17 13:20         ` Metzger, Markus T
2013-12-17 16:59           ` Pedro Alves
2013-12-12  9:16 ` [patch v8 22/24] btrace, gdbserver: read branch trace incrementally Markus Metzger
2013-12-13 19:46   ` Pedro Alves
2013-12-16 12:47     ` Metzger, Markus T
2013-12-16 19:23       ` Pedro Alves
2013-12-12  9:16 ` [patch v8 17/24] record-btrace: provide xfer_partial target method Markus Metzger
2013-12-13 18:43   ` Pedro Alves
2013-12-16 10:53     ` Metzger, Markus T
2013-12-16 19:16       ` Pedro Alves
2013-12-17 11:58         ` Metzger, Markus T
2013-12-17 16:56           ` Pedro Alves
2013-12-18  9:26             ` Metzger, Markus T
2013-12-18 10:39               ` Pedro Alves
2013-12-12  9:16 ` [patch v8 19/24] record-btrace: provide target_find_new_threads method Markus Metzger
2013-12-12  9:16 ` [patch v8 04/24] frame: add frame_is_tailcall function Markus Metzger
2013-12-12  9:16 ` [patch v8 11/24] record-btrace: make ranges include begin and end Markus Metzger
2013-12-13 17:56   ` Pedro Alves
2013-12-12  9:16 ` [patch v8 05/24] frame: artificial frame id's Markus Metzger
2013-12-12 19:39   ` Pedro Alves
2013-12-12 19:55     ` Jan Kratochvil
2013-12-13  8:04       ` Metzger, Markus T
2013-12-13 11:27         ` Jan Kratochvil
2013-12-13 11:42           ` Metzger, Markus T
2013-12-13 12:09         ` Pedro Alves
2013-12-13 13:01           ` Metzger, Markus T
2013-12-13 15:44             ` Pedro Alves
2013-12-13 17:51               ` Pedro Alves
2013-12-18 13:30                 ` Metzger, Markus T
2013-12-12 14:07 ` [patch v8 00/24] record-btrace: reverse Jan Kratochvil
2013-12-12 14:19   ` Metzger, Markus T

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