public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [patch v6 02/21] gdbarch: add instruction predicate methods
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (2 preceding siblings ...)
  2013-09-20 11:30 ` [patch v6 14/21] record-btrace: provide xfer_partial target method Markus Metzger
@ 2013-09-20 11:30 ` Markus Metzger
  2013-09-20 11:30 ` [patch v6 10/21] target: add ops parameter to to_prepare_to_store method Markus Metzger
                   ` (18 subsequent siblings)
  22 siblings, 0 replies; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:30 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

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

2013-09-20  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(+), 0 deletions(-)

diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 3ab74f0..ceec696 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -1364,6 +1364,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)
 {
@@ -1435,6 +1453,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_memory (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.  */
 
@@ -2968,6 +3032,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 459fd88..913f94d 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 1f3380e..b96b3b9 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 5d20ea6..f400905 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -1258,6 +1258,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 5e46788..08e11c8 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -983,6 +983,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 b159b49..7201cdc 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -472,6 +472,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)
 {
@@ -543,6 +559,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_memory (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_memory (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_memory (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.  */
 
@@ -7773,6 +7828,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.7.1

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

* [patch v6 15/21] record-btrace: add to_wait and to_resume target methods.
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
@ 2013-09-20 11:30 ` Markus Metzger
  2013-09-20 11:30 ` [patch v6 12/21] frame, backtrace: allow targets to supply a frame unwinder Markus Metzger
                   ` (21 subsequent siblings)
  22 siblings, 0 replies; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:30 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-09-20  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 files changed, 41 insertions(+), 0 deletions(-)

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 6799aeb..faf4e32 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -954,6 +954,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
@@ -986,6 +1025,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.7.1

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

* [patch v6 14/21] record-btrace: provide xfer_partial target method
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
  2013-09-20 11:30 ` [patch v6 15/21] record-btrace: add to_wait and to_resume target methods Markus Metzger
  2013-09-20 11:30 ` [patch v6 12/21] frame, backtrace: allow targets to supply a frame unwinder Markus Metzger
@ 2013-09-20 11:30 ` Markus Metzger
  2013-09-20 11:30 ` [patch v6 02/21] gdbarch: add instruction predicate methods Markus Metzger
                   ` (19 subsequent siblings)
  22 siblings, 0 replies; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:30 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-09-20  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 files changed, 51 insertions(+), 0 deletions(-)

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 8cde642..6799aeb 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
@@ -931,6 +981,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.7.1

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

* [patch v6 00/21] record-btrace: reverse
@ 2013-09-20 11:30 Markus Metzger
  2013-09-20 11:30 ` [patch v6 15/21] record-btrace: add to_wait and to_resume target methods Markus Metzger
                   ` (22 more replies)
  0 siblings, 23 replies; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:30 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

This is a small update of v5 that fixes the test fail when using "finish"
in a tail call frame.  The fix only changes "record-btrace: extend unwinder"
and resolves a resulting conflict in "record-btrace: add (reverse-)stepping
support".

Here's the v5 text again (without mentioning the fail):

This is an update of the record-btrace series to add reverse support
incorporating Jan's feedback.

The series can be split roughly in two parts.  The first half
improves the existing record btrace commands and fixes PR/15240.
The "record function-call-history" command can now show the
call relationship like this:

(gdb) record function-call-history /cli
12          fib inst 101,111    at src/fib.c:3,7
13            fib       inst 112,124    at src/fib.c:3,8
14          fib inst 125,129    at src/fib.c:7
15            fib       inst 130,142    at src/fib.c:3,8
16          fib inst 143,147    at src/fib.c:7,8
17        fib   inst 148,152    at src/fib.c:7,8
18      fib     inst 153,157    at src/fib.c:7
19        fib   inst 158,168    at src/fib.c:3,7
20          fib inst 169,179    at src/fib.c:3,7
21            fib       inst 180,185    at src/fib.c:3,4

The second half adds support for reverse execution.


Markus Metzger (21):
  btrace, linux: fix memory leak when reading branch trace
  gdbarch: add instruction predicate methods
  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-tdep.c                                   |   67 +
 gdb/arch-utils.c                                   |   15 +
 gdb/arch-utils.h                                   |    4 +
 gdb/btrace.c                                       | 1338 ++++++++++++++++---
 gdb/btrace.h                                       |  269 ++++-
 gdb/common/btrace-common.h                         |   10 +-
 gdb/common/linux-btrace.c                          |  115 ++-
 gdb/common/linux-btrace.h                          |   10 +-
 gdb/doc/gdb.texinfo                                |   30 +-
 gdb/dwarf2-frame-tailcall.c                        |    4 +-
 gdb/dwarf2-frame.c                                 |    8 +-
 gdb/frame-unwind.c                                 |   86 +-
 gdb/frame.c                                        |   54 +-
 gdb/frame.h                                        |   21 +-
 gdb/gdbarch.c                                      |   75 +
 gdb/gdbarch.h                                      |   18 +
 gdb/gdbarch.sh                                     |    9 +
 gdb/gdbserver/linux-low.c                          |   22 +-
 gdb/gdbserver/server.c                             |   11 +-
 gdb/gdbserver/target.h                             |    6 +-
 gdb/i386-tdep.c                                    |   59 +
 gdb/inf-child.c                                    |    2 +-
 gdb/infcmd.c                                       |    2 +-
 gdb/monitor.c                                      |    2 +-
 gdb/ravenscar-thread.c                             |    7 +-
 gdb/record-btrace.c                                | 1454 +++++++++++++++++---
 gdb/record-full.c                                  |    3 +-
 gdb/record.c                                       |    4 +
 gdb/record.h                                       |    3 +
 gdb/remote-m32r-sdi.c                              |    2 +-
 gdb/remote-mips.c                                  |    5 +-
 gdb/remote.c                                       |   28 +-
 gdb/stack.c                                        |    2 +-
 gdb/target.c                                       |   65 +-
 gdb/target.h                                       |   34 +-
 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 +
 59 files changed, 5566 insertions(+), 725 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

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

* [patch v6 12/21] frame, backtrace: allow targets to supply a frame unwinder
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
  2013-09-20 11:30 ` [patch v6 15/21] record-btrace: add to_wait and to_resume target methods Markus Metzger
@ 2013-09-20 11:30 ` Markus Metzger
  2013-09-20 11:30 ` [patch v6 14/21] record-btrace: provide xfer_partial target method Markus Metzger
                   ` (20 subsequent siblings)
  22 siblings, 0 replies; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:30 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 5c88b03..2aff23e 100644
--- a/gdb/dwarf2-frame.c
+++ b/gdb/dwarf2-frame.c
@@ -1497,16 +1497,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 ce2f6da..e163e73 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 d52c26a..5c080eb 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -2426,13 +2426,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 24511b3..c97dcfb 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4462,6 +4462,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 909ddf6..b86ea4e 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -886,6 +886,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?
      */
@@ -1767,6 +1772,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.7.1

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

* [patch v6 06/21] btrace: increase buffer size
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (5 preceding siblings ...)
  2013-09-20 11:30 ` [patch v6 16/21] record-btrace: provide target_find_new_threads method Markus Metzger
@ 2013-09-20 11:30 ` Markus Metzger
  2013-09-20 11:31 ` [patch v6 03/21] btrace: change branch trace data structure Markus Metzger
                   ` (15 subsequent siblings)
  22 siblings, 0 replies; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:30 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-09-20  Markus Metzger  <markus.t.metzger@intel.com>

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


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

diff --git a/gdb/common/linux-btrace.c b/gdb/common/linux-btrace.c
index 9fd2dc5..20e43e9 100644
--- a/gdb/common/linux-btrace.c
+++ b/gdb/common/linux-btrace.c
@@ -420,7 +420,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;
@@ -448,17 +448,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.7.1

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

* [patch v6 16/21] record-btrace: provide target_find_new_threads method
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (4 preceding siblings ...)
  2013-09-20 11:30 ` [patch v6 10/21] target: add ops parameter to to_prepare_to_store method Markus Metzger
@ 2013-09-20 11:30 ` Markus Metzger
  2013-09-20 11:30 ` [patch v6 06/21] btrace: increase buffer size Markus Metzger
                   ` (16 subsequent siblings)
  22 siblings, 0 replies; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:30 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-09-20  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 files changed, 19 insertions(+), 0 deletions(-)

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index faf4e32..1a753cc 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -993,6 +993,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
@@ -1027,6 +1045,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.7.1

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

* [patch v6 10/21] target: add ops parameter to to_prepare_to_store method
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (3 preceding siblings ...)
  2013-09-20 11:30 ` [patch v6 02/21] gdbarch: add instruction predicate methods Markus Metzger
@ 2013-09-20 11:30 ` Markus Metzger
  2013-09-20 11:30 ` [patch v6 16/21] record-btrace: provide target_find_new_threads method Markus Metzger
                   ` (17 subsequent siblings)
  22 siblings, 0 replies; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:30 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-09-20  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 1878272..e32e684 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 d0c9866..cbe4534 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 26b8171..18f7796 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 81fea53..9e4d238 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 bf6cce5..6880490 100644
--- a/gdb/remote-mips.c
+++ b/gdb/remote-mips.c
@@ -92,7 +92,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);
 
@@ -2066,7 +2067,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 2e116d9..33ca9f3 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);
 
@@ -6369,7 +6370,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 d55712d..24511b3 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -90,8 +90,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 *,
@@ -626,7 +624,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);
@@ -760,9 +758,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 *))
@@ -3995,6 +3990,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)
 {
@@ -4447,14 +4462,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,
@@ -4906,7 +4913,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 905c387..909ddf6 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -365,7 +365,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
@@ -996,8 +996,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.7.1

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

* [patch v6 17/21] record-btrace: add record goto target methods
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (18 preceding siblings ...)
  2013-09-20 11:31 ` [patch v6 01/21] btrace, linux: fix memory leak when reading branch trace Markus Metzger
@ 2013-09-20 11:31 ` Markus Metzger
  2013-10-06 19:48   ` Jan Kratochvil
  2013-09-20 11:31 ` [patch v6 11/21] record-btrace: supply register " Markus Metzger
                   ` (2 subsequent siblings)
  22 siblings, 1 reply; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:31 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches, Christian Himpel

Reviewed-by: Eli Zaretskii  <eliz@gnu.org>
CC: Christian Himpel  <christian.himpel@intel.com>
2013-09-20  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 8bb1781..5c41a09 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -25,6 +25,8 @@ Nios II ELF 			nios2*-*-elf
 Nios II GNU/Linux		nios2*-*-linux
 Texas Instruments MSP430	msp430*-*-elf
 
+* The btrace record target supports the 'record goto' command.
+
 * 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.
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 1a753cc..14a3ff5 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.  */
@@ -1011,6 +1027,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
@@ -1046,6 +1159,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..9e9c4ef
--- /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: "/users/mmetzger/gdb/gerrit/git/gdb/testsuite/gdb.btrace"
+	.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	"/users/mmetzger/gdb/gerrit/git/gdb/testsuite/gdb.btrace"
+.LASF2:
+	.string	"fun3"
+	.ident	"GCC: (GNU) 4.4.4 20100726 (Red Hat 4.4.4-13)"
+	.section	.note.GNU-stack,"",@progbits
-- 
1.7.1

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

* [patch v6 03/21] btrace: change branch trace data structure
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (6 preceding siblings ...)
  2013-09-20 11:30 ` [patch v6 06/21] btrace: increase buffer size Markus Metzger
@ 2013-09-20 11:31 ` Markus Metzger
  2013-10-06 19:46   ` Jan Kratochvil
  2013-09-20 11:31 ` [patch v6 13/21] record-btrace, frame: supply target-specific unwinder Markus Metzger
                   ` (14 subsequent siblings)
  22 siblings, 1 reply; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:31 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-09-20  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                                       | 1148 ++++++++++++++++----
 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, 1371 insertions(+), 398 deletions(-)

diff --git a/gdb/btrace.c b/gdb/btrace.c
index 3230a3e..f2a0d1f 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;
+
+      bfun->number = prev->number + 1;
+      bfun->insn_offset = (prev->insn_offset
+			   + VEC_length (btrace_insn_s, prev->insn));
+    }
 
-  return (filename_cmp (bfile, filename) != 0);
+  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;
+
+  bfun = ftrace_new_function (caller, mfun, fun);
+  bfun->up = caller;
+  bfun->level = caller->level + 1;
+  bfun->flags |= BFUN_UP_LINKS_TO_TAILCALL;
+
+  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;
 
-  ftrace = NULL;
-  bfun = NULL;
+      /* This is the function segment we're looking for.  */
+      break;
+    }
 
-  for (idx = 0; VEC_iterate (btrace_inst_s, itrace, idx, binst); ++idx)
+  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;
+
+      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;
 
-      /* 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);
+      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;
+
+	  /* 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);
 
-      /* If we're switching functions, we start over.  */
-      if (ftrace_function_switched (bfun, mfun.minsym, fun))
+	  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;
 
-      /* Update the instruction range.  */
-      bfun->iend = idx;
-      ftrace_debug (bfun, "update insns");
+  /* This is an unexplained function switch.  The call stack will likely
+     be wrong at this point.  */
+  bfun = ftrace_new_function (prev, mfun, fun);
 
-      /* Let's see if we have source correlation, as well.  */
-      sal = find_pc_line (pc, 0);
-      if (sal.symtab == NULL || sal.line == 0)
+  /* We keep the function level.  */
+  bfun->level = prev->level;
+
+  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 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))
+  /* 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));
+
+      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;
+
+      xfree (trash);
+    }
+
+  btinfo->begin = NULL;
+  btinfo->end = NULL;
 
-  btinfo->btrace = NULL;
-  btinfo->itrace = NULL;
-  btinfo->ftrace = NULL;
+  xfree (btinfo->insn_history);
+  xfree (btinfo->call_history);
+
+  btinfo->insn_history = NULL;
+  btinfo->call_history = NULL;
 }
 
 /* See btrace.h.  */
@@ -541,3 +874,448 @@ 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;
+
+  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..24d8806 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 not 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 s 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.7.1

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

* [patch v6 13/21] record-btrace, frame: supply target-specific unwinder
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (7 preceding siblings ...)
  2013-09-20 11:31 ` [patch v6 03/21] btrace: change branch trace data structure Markus Metzger
@ 2013-09-20 11:31 ` Markus Metzger
  2013-09-20 11:31 ` [patch v6 04/21] record-btrace: fix insn range in function call history Markus Metzger
                   ` (13 subsequent siblings)
  22 siblings, 0 replies; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:31 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 files changed, 64 insertions(+), 0 deletions(-)

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 88d7cbe..8cde642 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;
@@ -841,6 +842,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
@@ -871,6 +934,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.7.1

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

* [patch v6 21/21] record-btrace: add (reverse-)stepping support
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (14 preceding siblings ...)
  2013-09-20 11:31 ` [patch v6 09/21] btrace: add replay position to btrace thread info Markus Metzger
@ 2013-09-20 11:31 ` Markus Metzger
  2013-10-06 19:52   ` Jan Kratochvil
  2013-09-20 11:31 ` [patch v6 18/21] record-btrace: extend unwinder Markus Metzger
                   ` (6 subsequent siblings)
  22 siblings, 1 reply; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:31 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

There's an open regarding frame unwinding.  When I start stepping, the frame
cache will still be based on normal unwinding as will the frame cached in the
thread's stepping context.  This will prevent me from detecting that i stepped
into a subroutine.

To overcome that, I'm resetting the frame cache and setting the thread's
stepping cache based on the current frame - which is now computed using branch
tracing unwind.  I had to split get_current_frame to avoid checks that would
prevent me from doing this.

I also need to call registers_changed when I return from to_wait.  Otherwise,
the PC is not updated and the current location is shown incorrectly.  Not sure
whether this is intended or whether I'm unintentionally working around
something, here.

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

Reviewed-by: Eli Zaretskii  <eliz@gnu.org>
2013-09-20  Markus Metzger  <markus.t.metzger@intel.com>

	* btrace.h (btrace_thread_flag): New.
	(struct btrace_thread_info)<flags>: New.
	* frame.c (get_current_frame_nocheck): New.
	(get_current_frame): Call get_current_frame_nocheck.
	* frame.h (get_current_frame_nocheck): 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                            |    4 +-
 gdb/frame.c                                    |   37 ++-
 gdb/frame.h                                    |    4 +
 gdb/record-btrace.c                            |  381 ++++++++++++++++++++++--
 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 ++
 18 files changed, 1183 insertions(+), 54 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 6005b89..acac3ab 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -25,6 +25,10 @@ Nios II ELF 			nios2*-*-elf
 Nios II GNU/Linux		nios2*-*-linux
 Texas Instruments MSP430	msp430*-*-elf
 
+* The btrace record target supports limited replay and reverse
+  execution.  The target does not record data and does therefore
+  not allow reading memory or registers.
+
 * The btrace record target 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.
diff --git a/gdb/btrace.h b/gdb/btrace.h
index 36e12cb..34476b0 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 1098b60..50e7206 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6192,8 +6192,8 @@ 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.  It allows limited replay and reverse execution.
 
 This recording method may not be available on all processors.
 @end table
diff --git a/gdb/frame.c b/gdb/frame.c
index 0fd98ff..5a6f107 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1367,6 +1367,29 @@ unwind_to_current_frame (struct ui_out *ui_out, void *args)
   return 0;
 }
 
+/* See frame.h.  */
+
+struct frame_info *get_current_frame_nocheck (void)
+{
+  if (current_frame == NULL)
+    {
+      struct frame_info *sentinel_frame =
+	create_sentinel_frame (current_program_space, get_current_regcache ());
+
+      if (catch_exceptions (current_uiout, unwind_to_current_frame,
+			    sentinel_frame, RETURN_MASK_ERROR) != 0)
+	{
+	  /* Oops! Fake a current frame?  Is this useful?  It has a PC
+             of zero, for instance.  */
+	  current_frame = sentinel_frame;
+	}
+    }
+
+  return current_frame;
+}
+
+/* See frame.h.  */
+
 struct frame_info *
 get_current_frame (void)
 {
@@ -1392,19 +1415,7 @@ get_current_frame (void)
 	error (_("Target is executing."));
     }
 
-  if (current_frame == NULL)
-    {
-      struct frame_info *sentinel_frame =
-	create_sentinel_frame (current_program_space, get_current_regcache ());
-      if (catch_exceptions (current_uiout, unwind_to_current_frame,
-			    sentinel_frame, RETURN_MASK_ERROR) != 0)
-	{
-	  /* Oops! Fake a current frame?  Is this useful?  It has a PC
-             of zero, for instance.  */
-	  current_frame = sentinel_frame;
-	}
-    }
-  return current_frame;
+  return get_current_frame_nocheck ();
 }
 
 /* The "selected" stack frame is used by default for local and arg
diff --git a/gdb/frame.h b/gdb/frame.h
index f0da19e..cd2033d 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -242,6 +242,10 @@ enum frame_type
    error.  */
 extern struct frame_info *get_current_frame (void);
 
+/* Similar to get_current_frame except that we omit all checks.  May
+   return NULL if unwinding fails.  */
+extern struct frame_info *get_current_frame_nocheck (void);
+
 /* Does the current target interface have enough state to be able to
    query the current inferior for frame info, and is the inferior in a
    state where that is possible?  */
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 1620700..d634712 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);
@@ -1235,14 +1239,117 @@ static 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)
+{
+  struct btrace_thread_info *btinfo;
+  struct btrace_insn_iterator *replay;
+  const struct btrace_insn *insn;
+  struct symtab_and_line sal;
+  struct frame_info *frame;
+
+  btinfo = &tp->btrace;
+
+  /* We can't start replaying without trace.  */
+  if (btinfo->begin == NULL)
+    return NULL;
+
+  /* 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);
+
+  /* We just started replaying.  The frame id cached for stepping is based
+     on unwinding, not on branch tracing.  Recompute it.  */
+  frame = get_current_frame_nocheck ();
+  insn = btrace_insn_get (replay);
+  sal = find_pc_line (insn->pc, 0);
+  set_step_info (frame, sal);
+
+  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)
@@ -1251,7 +1358,213 @@ 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."));
+  /* We can't pass signals when replaying.  */
+  if (signal != GDB_SIGNAL_0)
+    error (_("You can't resume with signal from here."));
+
+  /* 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.  */
@@ -1260,8 +1573,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)
@@ -1270,7 +1587,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.  */
@@ -1303,32 +1653,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.  */
@@ -1427,6 +1765,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.7.1

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

* [patch v6 20/21] record-btrace: show trace from enable location
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (11 preceding siblings ...)
  2013-09-20 11:31 ` [patch v6 07/21] record-btrace: optionally indent function call history Markus Metzger
@ 2013-09-20 11:31 ` Markus Metzger
  2013-09-20 11:31 ` [patch v6 08/21] record-btrace: make ranges include begin and end Markus Metzger
                   ` (9 subsequent siblings)
  22 siblings, 0 replies; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:31 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-09-20  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 82cb134..0e9fa05 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.  */
@@ -1452,3 +1484,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 66b5967..36e12cb 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 14a99aa..1620700 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 cc21574..3283d5a 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.7.1

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

* [patch v6 08/21] record-btrace: make ranges include begin and end
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (12 preceding siblings ...)
  2013-09-20 11:31 ` [patch v6 20/21] record-btrace: show trace from enable location Markus Metzger
@ 2013-09-20 11:31 ` Markus Metzger
  2013-09-20 11:31 ` [patch v6 09/21] btrace: add replay position to btrace thread info Markus Metzger
                   ` (8 subsequent siblings)
  22 siblings, 0 replies; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:31 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches, Christian Himpel

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  <eliz@gnu.org>
CC: Christian Himpel  <christian.himpel@intel.com>
2013-09-20  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 1b18548..3449d52 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6393,8 +6393,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.
@@ -6464,8 +6463,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 7622465..905c387 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -864,7 +864,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.
@@ -879,7 +879,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.7.1

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

* [patch v6 04/21] record-btrace: fix insn range in function call history
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (8 preceding siblings ...)
  2013-09-20 11:31 ` [patch v6 13/21] record-btrace, frame: supply target-specific unwinder Markus Metzger
@ 2013-09-20 11:31 ` Markus Metzger
  2013-09-20 11:31 ` [patch v6 19/21] btrace, gdbserver: read branch trace incrementally Markus Metzger
                   ` (12 subsequent siblings)
  22 siblings, 0 replies; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:31 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-09-20  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 files 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.7.1

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

* [patch v6 09/21] btrace: add replay position to btrace thread info
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (13 preceding siblings ...)
  2013-09-20 11:31 ` [patch v6 08/21] record-btrace: make ranges include begin and end Markus Metzger
@ 2013-09-20 11:31 ` Markus Metzger
  2013-09-20 11:31 ` [patch v6 21/21] record-btrace: add (reverse-)stepping support Markus Metzger
                   ` (7 subsequent siblings)
  22 siblings, 0 replies; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:31 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-09-20  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 cdb9064..8f401f9 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.  */
@@ -1325,3 +1327,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 24d8806..66b5967 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.7.1

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

* [patch v6 19/21] btrace, gdbserver: read branch trace incrementally
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (9 preceding siblings ...)
  2013-09-20 11:31 ` [patch v6 04/21] record-btrace: fix insn range in function call history Markus Metzger
@ 2013-09-20 11:31 ` Markus Metzger
  2013-10-06 19:51   ` Jan Kratochvil
  2013-09-20 11:31 ` [patch v6 07/21] record-btrace: optionally indent function call history Markus Metzger
                   ` (11 subsequent siblings)
  22 siblings, 1 reply; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:31 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  <eliz@gnu.org>
CC: Pedro Alves  <palves@redhat.com>
2013-09-20  Markus Metzger  <markus.t.metzger@intel.com>

	* common/linux-btrace.c (perf_event_read_bts, linux_read_btrace):
	Support delta reads.
	* common/linux-btrace.h (linux_read_btrace): Change parameters
	and return type to allow error reporting.
	* common/btrace-common.h (btrace_read_type)<btrace_read_delta>:
	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.


---
 gdb/NEWS                   |    4 +
 gdb/btrace.c               |  147 ++++++++++++++++++++++++++++++++++++++-----
 gdb/common/btrace-common.h |   10 +++-
 gdb/common/linux-btrace.c  |   89 +++++++++++++++++++--------
 gdb/common/linux-btrace.h  |   10 ++-
 gdb/doc/gdb.texinfo        |    8 +++
 gdb/gdbserver/linux-low.c  |   22 +++++--
 gdb/gdbserver/server.c     |   11 +++-
 gdb/gdbserver/target.h     |    6 +-
 gdb/remote.c               |   23 ++++---
 gdb/target.c               |    9 ++-
 gdb/target.h               |   14 +++--
 12 files changed, 278 insertions(+), 75 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 4b41e87..6005b89 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -149,6 +149,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/btrace.c b/gdb/btrace.c
index 7029034..82cb134 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,149 @@ btrace_teardown (struct thread_info *tp)
   btrace_clear (tp);
 }
 
+/* Adjust the block trace in order to stitch old and new trace together.
+   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 *end;
+  struct btrace_insn *insn;
+  btrace_block_s *block;
+
+  /* If we don't have trace, there's nothing to do.  */
+  if (VEC_empty (btrace_block_s, *btrace))
+    return 0;
+
+  end = btinfo->end;
+  gdb_assert (end != NULL);
+
+  block = VEC_last (btrace_block_s, *btrace);
+  insn = VEC_last (btrace_insn_s, end->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 (block->end == 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 (insn),
+	 core_addr_to_string_nz (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 (block->end < 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 (block->begin == 0);
+  block->begin = 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 (insn));
+
+  VEC_pop (btrace_insn_s, end->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 +894,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 +986,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 b157c7c..2a9f46f 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,11 @@ 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
 };
 
 #endif /* BTRACE_COMMON_H */
diff --git a/gdb/common/linux-btrace.c b/gdb/common/linux-btrace.c
index 20e43e9..353869d 100644
--- a/gdb/common/linux-btrace.c
+++ b/gdb/common/linux-btrace.c
@@ -169,11 +169,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;
 
@@ -249,6 +249,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;
 }
 
@@ -501,21 +508,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,
+int
+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 0;
 
   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--)
@@ -523,23 +533,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 -EOVERFLOW;
+
+	  /* If the buffer is smaller than the trace delta, we overflowed.  */
+	  size = data_head - data_tail;
+	  if (buffer_size < size)
+	    return -EOVERFLOW;
+	}
+      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.
@@ -551,7 +583,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 0;
 }
 
 #else /* !HAVE_LINUX_PERF_EVENT_H */
@@ -577,16 +615,17 @@ linux_enable_btrace (ptid_t ptid)
 int
 linux_disable_btrace (struct btrace_target_info *tinfo)
 {
-  return ENOSYS;
+  return -ENOSYS;
 }
 
 /* See linux-btrace.h.  */
 
-VEC (btrace_block_s) *
-linux_read_btrace (struct btrace_target_info *tinfo,
+int
+linux_read_btrace (VEC (btrace_block_s) **btrace,
+		   struct btrace_target_info *tinfo,
 		   enum btrace_read_type type)
 {
-  return NULL;
+  return -ENOSYS;
 }
 
 #endif /* !HAVE_LINUX_PERF_EVENT_H */
diff --git a/gdb/common/linux-btrace.h b/gdb/common/linux-btrace.h
index d4e8402..ea86319 100644
--- a/gdb/common/linux-btrace.h
+++ b/gdb/common/linux-btrace.h
@@ -70,8 +70,12 @@ 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);
 
-/* Read branch trace data.  */
-extern VEC (btrace_block_s) *linux_read_btrace (struct btrace_target_info *,
-						enum btrace_read_type);
+/* Read branch trace data for the thread indicated by BTINFO into BTRACE
+   using the TYPE read method.
+   The branch trace will start with the most recent block and continue
+   towards older blocks.  */
+extern int 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 3449d52..1098b60 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -39260,6 +39260,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 799fcc9..273cfdf 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -5722,17 +5722,27 @@ linux_low_enable_btrace (ptid_t ptid)
   return tinfo;
 }
 
-/* Read branch trace data as btrace xml document.  */
+/* The read_btrace target method.  */
 
-static void
+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;
-  int i;
+  int i, errcode;
+
+  btrace = NULL;
+  errcode = linux_read_btrace (&btrace, tinfo, type);
+  if (errcode != 0)
+    {
+      if (errcode == -EOVERFLOW)
+	buffer_grow_str0 (buffer, "E.Overflow.");
+      else
+	buffer_grow_str0 (buffer, "E.Generic Error.");
 
-  btrace = linux_read_btrace (tinfo, type);
+      return -1;
+    }
 
   buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
   buffer_grow_str (buffer, "<btrace version=\"1.0\">\n");
@@ -5741,9 +5751,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 */
 
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 4de20d5..ce84a61 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..0fa83fc 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -360,8 +360,10 @@ struct target_ops
   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/remote.c b/gdb/remote.c
index 33ca9f3..69289a7 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -11365,13 +11365,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 int
+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;
 
@@ -11390,6 +11391,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."),
@@ -11398,15 +11402,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 -1;
 
-      btrace = parse_xml_btrace (xml);
-      do_cleanups (cleanup);
-    }
+  cleanup = make_cleanup (xfree, xml);
+  *btrace = parse_xml_btrace (xml);
+  do_cleanups (cleanup);
 
-  return btrace;
+  return 0;
 }
 
 static int
diff --git a/gdb/target.c b/gdb/target.c
index c97dcfb..06fb617 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4199,18 +4199,19 @@ target_teardown_btrace (struct btrace_target_info *btinfo)
 
 /* See target.h.  */
 
-VEC (btrace_block_s) *
-target_read_btrace (struct btrace_target_info *btinfo,
+int
+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 ENOSYS;
 }
 
 /* See target.h.  */
diff --git a/gdb/target.h b/gdb/target.h
index b86ea4e..28fcf57 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -823,9 +823,12 @@ 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 into DATA.  The vector is cleared before any
+       new data is added.
+       Returns 0 on success, a negative errno code otherwise.  */
+    int (*to_read_btrace) (VEC (btrace_block_s) **data,
+			   struct btrace_target_info *,
+			   enum btrace_read_type);
 
     /* Stop trace recording.  */
     void (*to_stop_recording) (void);
@@ -1955,8 +1958,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 int 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.7.1

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

* [patch v6 05/21] record-btrace: start counting at one
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (16 preceding siblings ...)
  2013-09-20 11:31 ` [patch v6 18/21] record-btrace: extend unwinder Markus Metzger
@ 2013-09-20 11:31 ` Markus Metzger
  2013-09-20 11:31 ` [patch v6 01/21] btrace, linux: fix memory leak when reading branch trace Markus Metzger
                   ` (4 subsequent siblings)
  22 siblings, 0 replies; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:31 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-09-20  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 f2a0d1f..cdb9064 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.7.1

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

* [patch v6 18/21] record-btrace: extend unwinder
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (15 preceding siblings ...)
  2013-09-20 11:31 ` [patch v6 21/21] record-btrace: add (reverse-)stepping support Markus Metzger
@ 2013-09-20 11:31 ` Markus Metzger
  2013-10-06 19:49   ` Jan Kratochvil
  2013-09-20 11:31 ` [patch v6 05/21] record-btrace: start counting at one Markus Metzger
                   ` (5 subsequent siblings)
  22 siblings, 1 reply; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:31 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.

There's an open point:

An assertion in get_frame_id at frame.c:340 requires that a frame provides a
stack address.  The record-btrace unwinder can't provide this since the trace
does not contain data.  I incorrectly set stack_addr_p to 1 to avoid the
assertion.

Reviewed-by: Eli Zaretskii  <eliz@gnu.org>
2013-04-24  Markus Metzger  <markus.t.metzger@intel.com>

	* frame.h (enum frame_type) <BTRACE_FRAME>: New.
	<BTRACE_TAILCALL_FRAME>: New.
	(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.
	* 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.
	(_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-tailcall.c              |    4 +-
 gdb/frame.c                              |    8 +-
 gdb/frame.h                              |   17 ++-
 gdb/infcmd.c                             |    2 +-
 gdb/record-btrace.c                      |  290 ++++++++++++++++++++++++++++-
 gdb/stack.c                              |    2 +-
 gdb/testsuite/gdb.btrace/record_goto.exp |   13 ++
 gdb/testsuite/gdb.btrace/tailcall.exp    |   17 ++
 10 files changed, 340 insertions(+), 19 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 5c41a09..4b41e87 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -26,6 +26,8 @@ Nios II GNU/Linux		nios2*-*-linux
 Texas Instruments MSP430	msp430*-*-elf
 
 * The btrace record target 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.
 
 * The command 'record function-call-history' supports a new modifier '/c' to
   indent the function names based on their call stack depth.
diff --git a/gdb/btrace.c b/gdb/btrace.c
index 8f401f9..7029034 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-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 5c080eb..0fd98ff 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -380,7 +380,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;
@@ -886,7 +886,7 @@ frame_pop (struct frame_info *this_frame)
 
   /* Ignore TAILCALL_FRAME type frames, they were executed already before
      entering THISFRAME.  */
-  while (get_frame_type (prev_frame) == TAILCALL_FRAME)
+  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.
@@ -2126,9 +2126,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;
 
diff --git a/gdb/frame.h b/gdb/frame.h
index a5e1629..f0da19e 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -216,7 +216,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
@@ -773,4 +777,15 @@ 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 non-zero if FRAME is a tailcall frame, return zero otherwise.  */
+
+static inline int
+frame_is_tailcall (struct frame_info *frame)
+{
+  enum frame_type type;
+
+  type = get_frame_type (frame);
+  return (type == TAILCALL_FRAME || type == BTRACE_TAILCALL_FRAME);
+}
+
 #endif /* !defined (FRAME_H)  */
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index e29dcde..37cc917 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -1774,7 +1774,7 @@ finish_command (char *arg, int from_tty)
 
   /* Ignore TAILCALL_FRAME type frames, they were executed already before
      entering THISFRAME.  */
-  while (get_frame_type (frame) == TAILCALL_FRAME)
+  while (frame_is_tailcall (frame))
     frame = get_prev_frame (frame);
 
   /* Find the function we will return from.  */
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 14a3ff5..14a99aa 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);
@@ -908,13 +931,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.  */
@@ -923,7 +1033,28 @@ 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 stack, code, special;
+
+  cache = *this_cache;
+
+  bfun = cache->bfun;
+  gdb_assert (bfun != NULL);
+
+  while (bfun->segment.prev != NULL)
+    bfun = bfun->segment.prev;
+
+  stack = 0;
+  code = get_frame_func (this_frame);
+  special = (CORE_ADDR) bfun;
+
+  *this_id = frame_id_build_special (stack, 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.  */
@@ -933,8 +1064,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.  */
@@ -944,15 +1114,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
@@ -963,12 +1217,24 @@ record_btrace_frame_sniffer (const struct frame_unwind *self,
 
 static 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
+};
+
+static 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.  */
@@ -1156,6 +1422,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;
@@ -1191,4 +1458,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/stack.c b/gdb/stack.c
index cd4ac7a..66c6bad 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -1509,7 +1509,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",
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.7.1

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

* [patch v6 11/21] record-btrace: supply register target methods
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (19 preceding siblings ...)
  2013-09-20 11:31 ` [patch v6 17/21] record-btrace: add record goto target methods Markus Metzger
@ 2013-09-20 11:31 ` Markus Metzger
  2013-09-26 19:16 ` v6 crash bugreport [Re: [patch v6 00/21] record-btrace: reverse] Jan Kratochvil
  2013-10-06 19:59 ` [+rfc] Re: [patch v6 00/21] record-btrace: reverse Jan Kratochvil
  22 siblings, 0 replies; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:31 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

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

2013-09-20  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 |   93 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 93 insertions(+), 0 deletions(-)

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index fc692aa..88d7cbe 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,95 @@ 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."));
+
+  if (may_write_registers == 0)
+    error (_("Writing to registers is not allowed (regno %d)"), regno);
+
+  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 +868,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.7.1

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

* [patch v6 07/21] record-btrace: optionally indent function call history
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (10 preceding siblings ...)
  2013-09-20 11:31 ` [patch v6 19/21] btrace, gdbserver: read branch trace incrementally Markus Metzger
@ 2013-09-20 11:31 ` Markus Metzger
  2013-10-06 19:47   ` Jan Kratochvil
  2013-09-20 11:31 ` [patch v6 20/21] record-btrace: show trace from enable location Markus Metzger
                   ` (10 subsequent siblings)
  22 siblings, 1 reply; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:31 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches, Christian Himpel

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  <eliz@gnu.org>
CC: Christian Himpel  <christian.himpel@intel.com>
2013-09-20  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 af06a21..8bb1781 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -25,6 +25,12 @@ Nios II ELF 			nios2*-*-elf
 Nios II GNU/Linux		nios2*-*-linux
 Texas Instruments MSP430	msp430*-*-elf
 
+* 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 commands:
 catch rethrow
   Like "catch throw", but catches a re-thrown exception.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 60d2877..1b18548 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6419,7 +6419,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}
@@ -6433,10 +6435,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 07b1b97..ffe9810 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 65d508f..9acc7de 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..cc21574
--- /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 thw 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..25510ce
--- /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: "/users/mmetzger/gdb/gerrit/git/gdb/testsuite/gdb.btrace"
+	.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	"/users/mmetzger/gdb/gerrit/git/gdb/testsuite/gdb.btrace"
+.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.7.1

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

* [patch v6 01/21] btrace, linux: fix memory leak when reading branch trace
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (17 preceding siblings ...)
  2013-09-20 11:31 ` [patch v6 05/21] record-btrace: start counting at one Markus Metzger
@ 2013-09-20 11:31 ` Markus Metzger
  2013-09-20 11:31 ` [patch v6 17/21] record-btrace: add record goto target methods Markus Metzger
                   ` (3 subsequent siblings)
  22 siblings, 0 replies; 41+ messages in thread
From: Markus Metzger @ 2013-09-20 11:31 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-09-20  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 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/gdb/common/linux-btrace.c b/gdb/common/linux-btrace.c
index b874c84..9fd2dc5 100644
--- a/gdb/common/linux-btrace.c
+++ b/gdb/common/linux-btrace.c
@@ -517,6 +517,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.7.1

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

* v6 crash bugreport  [Re: [patch v6 00/21] record-btrace: reverse]
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (20 preceding siblings ...)
  2013-09-20 11:31 ` [patch v6 11/21] record-btrace: supply register " Markus Metzger
@ 2013-09-26 19:16 ` Jan Kratochvil
  2013-09-27  6:37   ` Metzger, Markus T
  2013-10-06 19:59 ` [+rfc] Re: [patch v6 00/21] record-btrace: reverse Jan Kratochvil
  22 siblings, 1 reply; 41+ messages in thread
From: Jan Kratochvil @ 2013-09-26 19:16 UTC (permalink / raw)
  To: Markus Metzger; +Cc: gdb-patches

Hi Markus,

./gdb ./gdb -ex start -ex 'record btrace' -ex stepi -ex 'record instruction-history 0'
Segmentation fault

Program received signal SIGSEGV, Segmentation fault.
0x000000000089dd86 in btrace_find_insn_by_number (it=0x7fffffffd4c0, btinfo=0x291fc98, number=0) at btrace.c:1243
1243	  end = bfun->insn_offset + VEC_length (btrace_insn_s, bfun->insn);
(gdb) p bfun
$1 = (const struct btrace_function *) 0x0

(I am aware I was asking to reduce the number of NULL checks there.)


Thanks,
Jan

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

* RE: v6 crash bugreport  [Re: [patch v6 00/21] record-btrace: reverse]
  2013-09-26 19:16 ` v6 crash bugreport [Re: [patch v6 00/21] record-btrace: reverse] Jan Kratochvil
@ 2013-09-27  6:37   ` Metzger, Markus T
  0 siblings, 0 replies; 41+ messages in thread
From: Metzger, Markus T @ 2013-09-27  6:37 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

> -----Original Message-----
> From: Jan Kratochvil [mailto:jan.kratochvil@redhat.com]
> Sent: Thursday, September 26, 2013 9:16 PM


> ./gdb ./gdb -ex start -ex 'record btrace' -ex stepi -ex 'record instruction-
> history 0'
> Segmentation fault
> 
> Program received signal SIGSEGV, Segmentation fault.
> 0x000000000089dd86 in btrace_find_insn_by_number (it=0x7fffffffd4c0,
> btinfo=0x291fc98, number=0) at btrace.c:1243
> 1243	  end = bfun->insn_offset + VEC_length (btrace_insn_s, bfun->insn);
> (gdb) p bfun
> $1 = (const struct btrace_function *) 0x0

Thanks for reporting this!

I'll look into it and provide a fix in v7.

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] 41+ messages in thread

* Re: [patch v6 03/21] btrace: change branch trace data structure
  2013-09-20 11:31 ` [patch v6 03/21] btrace: change branch trace data structure Markus Metzger
@ 2013-10-06 19:46   ` Jan Kratochvil
  0 siblings, 0 replies; 41+ messages in thread
From: Jan Kratochvil @ 2013-10-06 19:46 UTC (permalink / raw)
  To: Markus Metzger; +Cc: gdb-patches, Christian Himpel

On Fri, 20 Sep 2013 13:30:21 +0200, Markus Metzger wrote:
> --- a/gdb/btrace.h
> +++ b/gdb/btrace.h
[...]
> @@ -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 not branch trace available.  If there is

I do not think the wording is right.  Either:
        Both will be NULL if branch trace is not available.
or
        Both will be NULL if there is no branch trace available.


> +     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;
>  
[...]
> +/* 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 s by which the call

Typo, excessive " s ".


> +   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);


Thanks,
Jan

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

* Re: [patch v6 07/21] record-btrace: optionally indent function call history
  2013-09-20 11:31 ` [patch v6 07/21] record-btrace: optionally indent function call history Markus Metzger
@ 2013-10-06 19:47   ` Jan Kratochvil
  0 siblings, 0 replies; 41+ messages in thread
From: Jan Kratochvil @ 2013-10-06 19:47 UTC (permalink / raw)
  To: Markus Metzger; +Cc: gdb-patches, Christian Himpel

On Fri, 20 Sep 2013 13:30:25 +0200, Markus Metzger wrote:
> --- /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 thw two breakpoints

s/thw/the/


> +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.*"
[...]
> --- /dev/null
> +++ b/gdb/testsuite/gdb.btrace/x86-tailcall.S
> @@ -0,0 +1,279 @@
[...]
> +.LASF2:
> +	.string	"/users/mmetzger/gdb/gerrit/git/gdb/testsuite/gdb.btrace"

You could replace the directory for example by (that is IIRC not correct
anyway for out-of-src-tree builds but it at least finds the source for
in-src-tree builds)::
        .string ""


> +.LASF1:
> +	.string	"x86-tailcall.c"
> +	.ident	"GCC: (GNU) 4.4.4 20100726 (Red Hat 4.4.4-13)"
> +	.section	.note.GNU-stack,"",@progbits


Jan

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

* Re: [patch v6 17/21] record-btrace: add record goto target methods
  2013-09-20 11:31 ` [patch v6 17/21] record-btrace: add record goto target methods Markus Metzger
@ 2013-10-06 19:48   ` Jan Kratochvil
  0 siblings, 0 replies; 41+ messages in thread
From: Jan Kratochvil @ 2013-10-06 19:48 UTC (permalink / raw)
  To: Markus Metzger; +Cc: gdb-patches, Christian Himpel

On Fri, 20 Sep 2013 13:30:35 +0200, Markus Metzger wrote:
[...]
> --- /dev/null
> +++ b/gdb/testsuite/gdb.btrace/x86-record_goto.S
> @@ -0,0 +1,355 @@
[...]
> +.LASF6:
> +	.string	"/users/mmetzger/gdb/gerrit/git/gdb/testsuite/gdb.btrace"

You could replace the directory for example by (that is IIRC not correct
anyway for out-of-src-tree builds but it at least finds the source for
in-src-tree builds)::
        .string ""



Jan

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

* Re: [patch v6 18/21] record-btrace: extend unwinder
  2013-09-20 11:31 ` [patch v6 18/21] record-btrace: extend unwinder Markus Metzger
@ 2013-10-06 19:49   ` Jan Kratochvil
  2013-11-06 13:45     ` Metzger, Markus T
  0 siblings, 1 reply; 41+ messages in thread
From: Jan Kratochvil @ 2013-10-06 19:49 UTC (permalink / raw)
  To: Markus Metzger; +Cc: gdb-patches

On Fri, 20 Sep 2013 13:30:36 +0200, Markus Metzger wrote:
[...]
> --- a/gdb/frame.c
> +++ b/gdb/frame.c
[...]
> @@ -886,7 +886,7 @@ frame_pop (struct frame_info *this_frame)
>  
>    /* Ignore TAILCALL_FRAME type frames, they were executed already before
>       entering THISFRAME.  */
> -  while (get_frame_type (prev_frame) == TAILCALL_FRAME)
> +  while (frame_is_tailcall (prev_frame))

Such kind of refactorization should be in a standalone patch.


>      prev_frame = get_prev_frame (prev_frame);
>  
>    /* Make a copy of all the register values unwound from this frame.
> @@ -2126,9 +2126,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;
>  
> diff --git a/gdb/frame.h b/gdb/frame.h
> index a5e1629..f0da19e 100644
> --- a/gdb/frame.h
> +++ b/gdb/frame.h
> @@ -216,7 +216,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

One should update also fprint_frame_type, otherwise:
(gdb) set debug frame 1
{ get_prev_frame_1 (this_frame=2) -> {level=3,type=<unknown type>,unwind=0x104aaa0,pc=0x40051f,id=<unknown>,func=<unknown>} // cached 
                                                   ^^^^^^^^^^^^^^

(I have fixed it now for TAILCALL_FRAME which I also forgot to add to
fprint_frame_type.)


>  };
>  
>  /* For every stopped thread, GDB tracks two frames: current and
> @@ -773,4 +777,15 @@ 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 non-zero if FRAME is a tailcall frame, return zero otherwise.  */
> +
> +static inline int
> +frame_is_tailcall (struct frame_info *frame)
> +{
> +  enum frame_type type;
> +
> +  type = get_frame_type (frame);
> +  return (type == TAILCALL_FRAME || type == BTRACE_TAILCALL_FRAME);

Excessive parentheses.

This function does not need to be / should not be inlined in .h file, this
code is not a hot performance code path I think and moreover such
optimizations should be left for gcc -flto.

And I think when there are already two such unwinders there could be reather
a new field in struct frame_unwind:

 struct frame_unwind
 {
   /* The frame's type.  Should this instead be a collection of
      predicates that test the frame for various attributes?  */
   enum frame_type type;
+  /* Is this unwinder for tail calls?  Call + return replaced by jump
+     at the end of a function is a tail call.  */
+  unsigned tail_call : 1;

This unfortunately needs a simple change for all existing unwinders...


> +}
> +
>  #endif /* !defined (FRAME_H)  */
[...]
> --- a/gdb/record-btrace.c
> +++ b/gdb/record-btrace.c
[...]
> @@ -923,7 +1033,28 @@ 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 stack, code, special;
> +
> +  cache = *this_cache;
> +
> +  bfun = cache->bfun;
> +  gdb_assert (bfun != NULL);
> +

/* Get unique identifier of the function - frame independent on specific PC in
   the function.  */


> +  while (bfun->segment.prev != NULL)
> +    bfun = bfun->segment.prev;
> +
> +  stack = 0;

As discussed elsewhere I do not find correct to set stack_p = 1 && stack = 0.
I think frame_id_p() can return true even if special_p == 1 but stack_p == 0.
You sure need some new function similar to frame_id_build_special().


> +  code = get_frame_func (this_frame);
> +  special = (CORE_ADDR) bfun;

This is not safe, GDB host may be 64-bit and target 32-bit and in such case
without --enable-64-bit-bfd there will be sizeof (CORE_ADDR) == 4,
therefore different BFUNs may get the same SPECIAL.
bfun->insn_offset or bfun->insn_offset should serve better I think.

Also there could be a comment like (it still applies if one uses any of bfun,
bfun->insn_offset or bfun->insn_offset):
  /* The btrace_function structures can be rebuilt but only after inferior has
     run.  In such case all btrace frame have been deleted and there remain no
     stale uses of BFUN addresses.  */


I was trying to find countercase but I haven't found any so hopefully it will
work.  Clearly btrace_function *


> +
> +  *this_id = frame_id_build_special (stack, 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.  */


Thanks,
Jan

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

* Re: [patch v6 19/21] btrace, gdbserver: read branch trace incrementally
  2013-09-20 11:31 ` [patch v6 19/21] btrace, gdbserver: read branch trace incrementally Markus Metzger
@ 2013-10-06 19:51   ` Jan Kratochvil
  0 siblings, 0 replies; 41+ messages in thread
From: Jan Kratochvil @ 2013-10-06 19:51 UTC (permalink / raw)
  To: Markus Metzger; +Cc: gdb-patches, Pedro Alves

On Fri, 20 Sep 2013 13:30:37 +0200, Markus Metzger wrote:
> --- a/gdb/btrace.c
> +++ b/gdb/btrace.c
[...]
> @@ -717,27 +717,149 @@ btrace_teardown (struct thread_info *tp)
>    btrace_clear (tp);
>  }
>  
> +/* Adjust the block trace in order to stitch old and new trace together.
> +   Return 0 on success, -1 otherwise.  */

Document parameters (what is input, what is output).


> +
> +static int
> +btrace_stitch_trace (VEC (btrace_block_s) **btrace,
> +		     const struct btrace_thread_info *btinfo)
> +{
> +  struct btrace_function *end;
> +  struct btrace_insn *insn;
> +  btrace_block_s *block;
> +
> +  /* If we don't have trace, there's nothing to do.  */
> +  if (VEC_empty (btrace_block_s, *btrace))
> +    return 0;
> +
> +  end = btinfo->end;
> +  gdb_assert (end != NULL);
> +
> +  block = VEC_last (btrace_block_s, *btrace);
> +  insn = VEC_last (btrace_insn_s, end->insn);

At least call block and insn somehow specific from where they come from.
Maybe btrace_block and btinfo_end.  Also end should be called btinfo_end (if
the extra variable still makes sense in such case).

I would even call it new_btrace and old_btinfo with variables old_end etc.


> +
> +  /* 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 (block->end == 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 (insn),
> +	 core_addr_to_string_nz (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 (block->end < 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 (block->begin == 0);
> +  block->begin = 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 (insn));
> +
> +  VEC_pop (btrace_insn_s, end->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;
> +}
[...]
> --- a/gdb/common/linux-btrace.h
> +++ b/gdb/common/linux-btrace.h
> @@ -70,8 +70,12 @@ 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);
>  
> -/* Read branch trace data.  */
> -extern VEC (btrace_block_s) *linux_read_btrace (struct btrace_target_info *,
> -						enum btrace_read_type);
> +/* Read branch trace data for the thread indicated by BTINFO into BTRACE
> +   using the TYPE read method.
> +   The branch trace will start with the most recent block and continue
> +   towards older blocks.  */

It could also state from to_read_btrace these two things:
	The vector is cleared before any new data is added.
	Returns 0 on success; a negative error code, otherwise.

And in fact the comment can be merged to to_read_btrace and here to have only:
  /* See to_read_btrace.  */


> +extern int linux_read_btrace (VEC (btrace_block_s) **btrace,
> +			      struct btrace_target_info *btinfo,
> +			      enum btrace_read_type type);
>  
>  #endif /* LINUX_BTRACE_H */
[...]
> --- a/gdb/gdbserver/linux-low.c
> +++ b/gdb/gdbserver/linux-low.c
> @@ -5722,17 +5722,27 @@ linux_low_enable_btrace (ptid_t ptid)
>    return tinfo;
>  }
>  
> -/* Read branch trace data as btrace xml document.  */
> +/* The read_btrace target method.  */

/* See to_read_btrace target method.  */

(so that ctags and other tools work; I made a "typo" in the review)


>  
> -static void
> +static int
>  linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
>  		       int type)
>  {
[...]
> --- a/gdb/target.c
> +++ b/gdb/target.c
> @@ -4199,18 +4199,19 @@ target_teardown_btrace (struct btrace_target_info *btinfo)
>  
>  /* See target.h.  */
>  
> -VEC (btrace_block_s) *
> -target_read_btrace (struct btrace_target_info *btinfo,
> +int
> +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 ENOSYS;

This should be "return -ENOSYS".


>  }
>  
>  /* See target.h.  */
[...]


Thanks,
Jan

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

* Re: [patch v6 21/21] record-btrace: add (reverse-)stepping support
  2013-09-20 11:31 ` [patch v6 21/21] record-btrace: add (reverse-)stepping support Markus Metzger
@ 2013-10-06 19:52   ` Jan Kratochvil
  2013-11-06 15:06     ` Metzger, Markus T
  0 siblings, 1 reply; 41+ messages in thread
From: Jan Kratochvil @ 2013-10-06 19:52 UTC (permalink / raw)
  To: Markus Metzger; +Cc: gdb-patches

On Fri, 20 Sep 2013 13:30:39 +0200, Markus Metzger wrote:
[...]
> --- 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)
> +};

The content should be indented by two spaces, not by four.


> +
>  /* Branch trace information per thread.
>  
>     This represents the branch trace configuration as well as the entry point
[...]
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 1098b60..50e7206 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -6192,8 +6192,8 @@ 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.  It allows limited replay and reverse execution.

As discussed in
	Message-ID: <A78C989F6D9628469189715575E55B230A9D4934@IRSMSX104.ger.corp.intel.com>

the documentation should mention the full history is usually not kept as the
btrace buffer commonly overflows.  Otherwise user may be curious why the
btrace history is always relatively short.

I would see it worth a comment even in
	(gdb) help record btrace 
	Start branch trace recording.


>  This recording method may not be available on all processors.
>  @end table


Thanks,
Jan

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

* [+rfc] Re: [patch v6 00/21] record-btrace: reverse
  2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
                   ` (21 preceding siblings ...)
  2013-09-26 19:16 ` v6 crash bugreport [Re: [patch v6 00/21] record-btrace: reverse] Jan Kratochvil
@ 2013-10-06 19:59 ` Jan Kratochvil
  2013-11-07 15:44   ` Metzger, Markus T
  22 siblings, 1 reply; 41+ messages in thread
From: Jan Kratochvil @ 2013-10-06 19:59 UTC (permalink / raw)
  To: Markus Metzger; +Cc: gdb-patches

On Fri, 20 Sep 2013 13:30:18 +0200, Markus Metzger wrote:
> This is a small update of v5 that fixes the test fail when using "finish"
> in a tail call frame.  The fix only changes "record-btrace: extend unwinder"
> and resolves a resulting conflict in "record-btrace: add (reverse-)stepping
> support".

Besides some nitpicks there remains the larger request
	Re: [patch v4 23/24] record-btrace: add (reverse-)stepping support
	https://sourceware.org/ml/gdb-patches/2013-09/msg01004.html
	Message-ID: <20130930102533.GA29665@host2.jankratochvil.net>


to always use to_resume + to_wait for any change of history position.  This
also means the "hacks" like
	+record_btrace_goto_end (void)
	+  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
can be removed as normal GDB inferior stop will print the frame.

Other comments are welcome if it is not a too strict requirement from me but
I think we could face some forgotten resetting of future caches avoiding the
normal resume+wait path this way.  Or ... we would not face them but it would
violate localized of cache resets in to_resume+to_wait code path.


Thanks,
Jan

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

* RE: [patch v6 18/21] record-btrace: extend unwinder
  2013-10-06 19:49   ` Jan Kratochvil
@ 2013-11-06 13:45     ` Metzger, Markus T
  2013-11-25 21:11       ` Jan Kratochvil
  0 siblings, 1 reply; 41+ messages in thread
From: Metzger, Markus T @ 2013-11-06 13:45 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

> -----Original Message-----
> From: gdb-patches-owner@sourceware.org [mailto:gdb-patches-
> owner@sourceware.org] On Behalf Of Jan Kratochvil
> Sent: Sunday, October 06, 2013 9:50 PM

Hello Jan,

Thanks for your feedback.  I've been busy otherwise the last few weeks;
now I'm switching back to the btrace series.


> > +  while (bfun->segment.prev != NULL)
> > +    bfun = bfun->segment.prev;
> > +
> > +  stack = 0;
> 
> As discussed elsewhere I do not find correct to set stack_p = 1 && stack = 0.
> I think frame_id_p() can return true even if special_p == 1 but stack_p == 0.
> You sure need some new function similar to frame_id_build_special().

Besides frame_id_p I also had to change frame_id_eq.  Here's how it look
like.  Are you OK with such changes?  Should I put the frame.[hc] changes into
a separate patch, as well?

I had to make the first test in frame_id_eq stricter than it was since
otherwise all artificial frames would have been equal to each other.
I ran the full test suite without regressions (native on 64bit IA FC19), but I'm
Not exactly sure what has been tested.  The report lists 34 untested and 133
unsupported for both runs.


diff --git a/gdb/frame.c b/gdb/frame.c
index 3157167..e8dd029 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -491,6 +491,19 @@ frame_id_build_wild (CORE_ADDR stack_addr)
   return id;
 }
 
+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)
 {
@@ -501,6 +514,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=");
@@ -524,13 +540,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 7109dbc..9fd278c 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 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.  */
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 02b5e3d..63f09c6 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -1036,7 +1036,7 @@ record_btrace_frame_this_id (struct frame_info *this_frame, void **this_cache,
 {
   const struct btrace_frame_cache *cache;
   const struct btrace_function *bfun;
-  CORE_ADDR stack, code, special;
+  CORE_ADDR code, special;
 
   cache = *this_cache;
 
@@ -1046,11 +1046,10 @@ record_btrace_frame_this_id (struct frame_info *this_frame, void **this_cache,
   while (bfun->segment.prev != NULL)
     bfun = bfun->segment.prev;
 
-  stack = 0;
   code = get_frame_func (this_frame);
   special = bfun->number;
 
-  *this_id = frame_id_build_special (stack, code, special);
+  *this_id = frame_id_build_artificial (code, special);
 
   DEBUG ("[frame] %s id: (!stack, pc=%s, special=%s)",
 	 btrace_get_bfun_name (cache->bfun),


> > +  code = get_frame_func (this_frame);
> > +  special = (CORE_ADDR) bfun;
> 
> This is not safe, GDB host may be 64-bit and target 32-bit and in such case
> without --enable-64-bit-bfd there will be sizeof (CORE_ADDR) == 4,
> therefore different BFUNs may get the same SPECIAL.
> bfun->insn_offset or bfun->insn_offset should serve better I think.

I changed this to use bfun->number, instead.


> Also there could be a comment like (it still applies if one uses any of bfun,
> bfun->insn_offset or bfun->insn_offset):
>   /* The btrace_function structures can be rebuilt but only after inferior has
>      run.  In such case all btrace frame have been deleted and there remain no
>      stale uses of BFUN addresses.  */

Doesn't gdb keep frame id's alive during stepping?  I thought they were used
to detect steps into subroutines.

We could end up with the same frame id but a different frame if we
recomputed the entire trace (i.e. the trace buffer overflows and we can't
stitch traces) and we happened to have the same function at the same
position in the trace.

But this might also happen for all other frame id's.


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] 41+ messages in thread

* RE: [patch v6 21/21] record-btrace: add (reverse-)stepping support
  2013-10-06 19:52   ` Jan Kratochvil
@ 2013-11-06 15:06     ` Metzger, Markus T
  2013-11-26 13:48       ` Jan Kratochvil
  0 siblings, 1 reply; 41+ messages in thread
From: Metzger, Markus T @ 2013-11-06 15:06 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

> -----Original Message-----
> From: gdb-patches-owner@sourceware.org [mailto:gdb-patches-
> owner@sourceware.org] On Behalf Of Jan Kratochvil
> Sent: Sunday, October 06, 2013 9:52 PM


> >  @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.  It allows limited replay and reverse execution.
> 
> As discussed in
> 	Message-ID:
> <A78C989F6D9628469189715575E55B230A9D4934@IRSMSX104.ger.corp.intel.
> com>
> 
> the documentation should mention the full history is usually not kept as the
> btrace buffer commonly overflows.  Otherwise user may be curious why the
> btrace history is always relatively short.

I added a sentence to describe this in the documentation.


> I would see it worth a comment even in
> 	(gdb) help record btrace
> 	Start branch trace recording.

I don't think that this is necessary or helpful.  If we start documenting the
trace size here, we should also say that trace is collected per thread with
no correlation between threads and that tracing induces a run-time overhead
and that it may also affect thread interleaving which may cause bug symptoms
to disappear, and ....


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] 41+ messages in thread

* RE: [+rfc] Re: [patch v6 00/21] record-btrace: reverse
  2013-10-06 19:59 ` [+rfc] Re: [patch v6 00/21] record-btrace: reverse Jan Kratochvil
@ 2013-11-07 15:44   ` Metzger, Markus T
  2013-11-27 20:35     ` Jan Kratochvil
  0 siblings, 1 reply; 41+ messages in thread
From: Metzger, Markus T @ 2013-11-07 15:44 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

> -----Original Message-----
> From: Jan Kratochvil [mailto:jan.kratochvil@redhat.com]
> Sent: Sunday, October 06, 2013 9:59 PM

Hello Jan,

> Besides some nitpicks there remains the larger request
> 	Re: [patch v4 23/24] record-btrace: add (reverse-)stepping support
> 	https://sourceware.org/ml/gdb-patches/2013-09/msg01004.html
> 	Message-ID: <20130930102533.GA29665@host2.jankratochvil.net>
> 
> 
> to always use to_resume + to_wait for any change of history position.  This
> also means the "hacks" like
> 	+record_btrace_goto_end (void)
> 	+  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC,
> 1);
> can be removed as normal GDB inferior stop will print the frame.
> 
> Other comments are welcome if it is not a too strict requirement from me
> but
> I think we could face some forgotten resetting of future caches avoiding the
> normal resume+wait path this way.  Or ... we would not face them but it
> would
> violate localized of cache resets in to_resume+to_wait code path.

I hacked a first prototype of this (see below).  It passes most tests but
results in three fails in the record_goto suite.

One thing that it shows, though, is that it only removes the 'mostly harmless'
hack in the various goto functions shown above.

The more serious hacks in record_btrace_start_replaying

	  /* Make sure we're not using any stale registers.  */
	  registers_changed_ptid (tp->ptid);

	  /* We just started replaying.  The frame id cached for stepping is based
	     on unwinding, not on branch tracing.  Recompute it.  */
	  frame = get_current_frame_nocheck ();
	  insn = btrace_insn_get (replay);
	  sal = find_pc_line (insn->pc, 0);
	  set_step_info (frame, sal);

and record_btrace_stop_replaying

	  /* Make sure we're not leaving any stale registers.  */
	  registers_changed_ptid (tp->ptid);

however, are not removed by this.

They are required when reverse-stepping the first time or when
stepping past the end of the execution trace.

I don't think that there's a big benefit in pursuing this.  Plus the patch has the
potential of messing things up pretty badly if somehow (maybe due to some
unexpected error () somewhere in proceed ()) the record goto command does
not complete and BTHR_GOTO remains set.


Regards,
Markus.


diff --git a/gdb/btrace.h b/gdb/btrace.h
index 5b52ec6..c1a5d14 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -169,7 +169,10 @@ enum btrace_thread_flag
   BTHR_RCONT = (1 << 3),
 
   /* The thread is to be moved.  */
-  BTHR_MOVE = (BTHR_STEP | BTHR_RSTEP | BTHR_CONT | BTHR_RCONT)
+  BTHR_MOVE = (BTHR_STEP | BTHR_RSTEP | BTHR_CONT | BTHR_RCONT),
+
+  /* The thread is to go to GOTO_TARGET in its BTINFO.  */
+  BTHR_GOTO = (1 << 4)
 };
 
 /* Branch trace information per thread.
@@ -212,6 +215,9 @@ struct btrace_thread_info
 
   /* The current replay position.  NULL if not replaying.  */
   struct btrace_insn_iterator *replay;
+
+  /* The goto target replay position.  NULL if not active.  */
+  struct btrace_insn_iterator *goto_target;
 };
 
 /* Enable branch tracing for a thread.  */
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index f32f1fd..c559327 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -1459,6 +1459,17 @@ record_btrace_step_thread (struct thread_info *tp)
 
   DEBUG ("stepping %d (%s): %u", tp->num, target_pid_to_str (tp->ptid), flags);
 
+  if ((btinfo->flags & BTHR_GOTO) != 0)
+    {
+      btinfo->replay = btinfo->goto_target;
+      btinfo->goto_target = NULL;
+      btinfo->flags &= ~BTHR_GOTO;
+
+      xfree (replay);
+
+      return btrace_step_stopped ();
+    }
+
   switch (flags)
     {
     default:
@@ -1635,32 +1646,57 @@ record_btrace_find_new_threads (struct target_ops *ops)
       }
 }
 
-/* Set the replay branch trace instruction iterator.  If IT is NULL, replay
+/* Move to the IT in the branch trace of TP.  If IT is NULL, replay
    is stopped.  */
 
 static void
-record_btrace_set_replay (struct thread_info *tp,
-			  const struct btrace_insn_iterator *it)
+record_btrace_goto_target (struct thread_info *tp,
+			   const struct btrace_insn_iterator *it)
 {
+  struct btrace_insn_iterator *goto_target;
   struct btrace_thread_info *btinfo;
+  struct target_waitstatus ws;
 
   btinfo = &tp->btrace;
 
-  if (it == NULL || it->function == NULL)
-    record_btrace_stop_replaying (tp);
+  if (btinfo->goto_target != NULL || (btinfo->flags & BTHR_GOTO) != 0)
+    error (_("Record goto already in progress."));
+
+  if ((btinfo->flags & BTHR_MOVE) != 0)
+    error (_("Thread is already moving."));
+
+  /* If we're not replaying, to_resume might misinterpret our request. */
+  if (btinfo->replay == NULL)
+    record_btrace_start_replaying (tp);
+
+  if (it == NULL)
+    goto_target = NULL;
   else
     {
-      if (btinfo->replay == NULL)
-	record_btrace_start_replaying (tp);
-      else if (btrace_insn_cmp (btinfo->replay, it) == 0)
-	return;
-
-      *btinfo->replay = *it;
-      registers_changed_ptid (tp->ptid);
+      goto_target = xmalloc (sizeof(*goto_target));
+      *goto_target = *it;
     }
 
-  /* Start anew from the new replay position.  */
-  record_btrace_clear_histories (btinfo);
+  btinfo->flags |= BTHR_GOTO;
+  btinfo->goto_target = goto_target;
+
+#if 0
+  if (goto_target != NULL)
+    tp->control.exception_resume_breakpoint =
+      set_momentary_breakpoint_at_pc (target_gdbarch (),
+				      btrace_insn_get (goto_target)->pc,
+				      bp_until);
+#endif
+#if 0
+  target_resume (tp->ptid, 0, GDB_SIGNAL_0);
+  target_wait (tp->ptid, &ws, 0);
+#else
+  clear_proceed_status ();
+  proceed ((CORE_ADDR) -1, GDB_SIGNAL_0, 0);
+#endif
+
+  if (btinfo->goto_target != NULL || (btinfo->flags & BTHR_GOTO) != 0)
+    error (_("Record goto failed."));
 }
 
 /* The to_goto_record_begin method of target record-btrace.  */
@@ -1674,9 +1710,8 @@ record_btrace_goto_begin (void)
   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);
+  record_btrace_goto_target (tp, &begin);
 }
 
 /* The to_goto_record_end method of target record-btrace.  */
@@ -1688,9 +1723,7 @@ record_btrace_goto_end (void)
 
   tp = require_btrace_thread ();
 
-  record_btrace_set_replay (tp, NULL);
-
-  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
+  record_btrace_goto_target (tp, NULL);
 }
 
 /* The to_goto_record method of target record-btrace.  */
@@ -1715,9 +1748,7 @@ record_btrace_goto (ULONGEST insn)
   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);
+  record_btrace_goto_target (tp, &it);
 }
 
 /* Initialize the record-btrace target ops.  */

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] 41+ messages in thread

* Re: [patch v6 18/21] record-btrace: extend unwinder
  2013-11-06 13:45     ` Metzger, Markus T
@ 2013-11-25 21:11       ` Jan Kratochvil
  0 siblings, 0 replies; 41+ messages in thread
From: Jan Kratochvil @ 2013-11-25 21:11 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: gdb-patches

On Wed, 06 Nov 2013 14:03:04 +0100, Metzger, Markus T wrote:
> Besides frame_id_p I also had to change frame_id_eq.  Here's how it look
> like.  Are you OK with such changes?  Should I put the frame.[hc] changes into
> a separate patch, as well?
> 
> I had to make the first test in frame_id_eq stricter than it was since
> otherwise all artificial frames would have been equal to each other.
> I ran the full test suite without regressions (native on 64bit IA FC19), but I'm
> Not exactly sure what has been tested.  The report lists 34 untested and 133
> unsupported for both runs.

That is the patch part below.  I find the patch correct, thanks.

From what I checked the condition '!l.stack_addr_p && l.special_addr_p' in
current GDB code base can happen only for outer_frame_id.  Also the comment
there assumes it is outer_frame_id.  So I find your change correct.

There is only a bit fragile memcmp for frame_id, as there may be uninitialized
inter-field alignment gaps.  But specifically frame_id is always initialized
from a copy of null_frame_id so it should be safe.  Also memcmp is already
used in frame_id_p().



> diff --git a/gdb/frame.c b/gdb/frame.c
> index 3157167..e8dd029 100644
> --- a/gdb/frame.c
> +++ b/gdb/frame.c
[...]
> @@ -524,13 +540,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.



> > > +  code = get_frame_func (this_frame);
> > > +  special = (CORE_ADDR) bfun;
> > 
> > This is not safe, GDB host may be 64-bit and target 32-bit and in such case
> > without --enable-64-bit-bfd there will be sizeof (CORE_ADDR) == 4,
> > therefore different BFUNs may get the same SPECIAL.
> > bfun->insn_offset or bfun->insn_offset should serve better I think.
> 
> I changed this to use bfun->number, instead.

OK.


> > Also there could be a comment like (it still applies if one uses any of bfun,
> > bfun->insn_offset or bfun->insn_offset):
> >   /* The btrace_function structures can be rebuilt but only after inferior has
> >      run.  In such case all btrace frame have been deleted and there remain no
> >      stale uses of BFUN addresses.  */
> 
> Doesn't gdb keep frame id's alive during stepping?  I thought they were used
> to detect steps into subroutines.

Yes.


> We could end up with the same frame id but a different frame if we
> recomputed the entire trace (i.e. the trace buffer overflows and we can't
> stitch traces) and we happened to have the same function at the same
> position in the trace.

OK, good catch.  But I do not think it can happen for a user, there may be
some way but I do not have an idea how to reproduce it, that is to find
a caller with set_momentary_breakpoint (), such as from "finish" between two
btrace frames.  If there is breakpoint->frame_id from btrace the real
execution cannot suddenly resume.  Inferior calls or gnu-ifunc resolution all
cannot happen in btrace.  "finish" into btrace parent cannot resume inferior.
etc.

There could be a sanity cleanup when btrace resumes real execution that no
stale btrace frame_id remains in breakpoints, something like in
check_longjmp_breakpoint_for_call_dummy.  I would call error() although it may
be also rather even internal_error().


> But this might also happen for all other frame id's.

It does not, or at least it should not.  As you said GDB uses frame_ids only
for small moves like step.  In other cases - like "finish" or return address
from inferior call (see pop_dummy_frame_bpt) GDB takes care to remove any
breakpoints having stale frame_id.

breakpoint->frame_id is never present for too long.


Thanks,
Jan

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

* Re: [patch v6 21/21] record-btrace: add (reverse-)stepping support
  2013-11-06 15:06     ` Metzger, Markus T
@ 2013-11-26 13:48       ` Jan Kratochvil
  0 siblings, 0 replies; 41+ messages in thread
From: Jan Kratochvil @ 2013-11-26 13:48 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: gdb-patches

On Wed, 06 Nov 2013 14:48:29 +0100, Metzger, Markus T wrote:
> > I would see it worth a comment even in
> > 	(gdb) help record btrace
> > 	Start branch trace recording.
> 
> I don't think that this is necessary or helpful.  If we start documenting the
> trace size here, we should also say that trace is collected per thread with
> no correlation between threads and that tracing induces a run-time overhead
> and that it may also affect thread interleaving which may cause bug symptoms
> to disappear, and ....

Tom for example recently added a TODO item to fix it all in general:
	https://sourceware.org/gdb/wiki/ProjectIdeas?action=diff&rev1=57&rev2=58

More text is better than less text.  But I do not insist on that.


Thanks,
Jan

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

* Re: [+rfc] Re: [patch v6 00/21] record-btrace: reverse
  2013-11-07 15:44   ` Metzger, Markus T
@ 2013-11-27 20:35     ` Jan Kratochvil
  2013-11-28 10:54       ` Metzger, Markus T
  0 siblings, 1 reply; 41+ messages in thread
From: Jan Kratochvil @ 2013-11-27 20:35 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 1677 bytes --]

On Thu, 07 Nov 2013 16:41:40 +0100, Metzger, Markus T wrote:
> I hacked a first prototype of this (see below).  It passes most tests but
> results in three fails in the record_goto suite.
> 
> One thing that it shows, though, is that it only removes the 'mostly harmless'
> hack in the various goto functions shown above.
> 
> The more serious hacks in record_btrace_start_replaying
> 
> 	  /* Make sure we're not using any stale registers.  */
> 	  registers_changed_ptid (tp->ptid);
> 
> 	  /* We just started replaying.  The frame id cached for stepping is based
> 	     on unwinding, not on branch tracing.  Recompute it.  */
> 	  frame = get_current_frame_nocheck ();
> 	  insn = btrace_insn_get (replay);
> 	  sal = find_pc_line (insn->pc, 0);
> 	  set_step_info (frame, sal);
> 
> and record_btrace_stop_replaying
> 
> 	  /* Make sure we're not leaving any stale registers.  */
> 	  registers_changed_ptid (tp->ptid);
> 
> however, are not removed by this.

In such case it is not finished.  These hacks should not be needed.


> They are required when reverse-stepping the first time or when
> stepping past the end of the execution trace.

I have patched what you describe as the problem.  But as I do not have a box
with reliably working BTS so it is difficult for me to say whether it works or
not.  I can look at other problems if you describe them from a reliable box.


> Plus the patch has the potential of messing things up pretty badly if
> somehow (maybe due to some unexpected error () somewhere in proceed ()) the
> record goto command does not complete and BTHR_GOTO remains set.

It can be cleaned up as I did there, thanks for catching it.


Thanks,
Jan

[-- Attachment #2: 1 --]
[-- Type: text/plain, Size: 2862 bytes --]

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index c50e11b..9fbff15 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -1300,16 +1300,6 @@ record_btrace_start_replaying (struct thread_info *tp)
   gdb_assert (btinfo->replay == NULL);
   btinfo->replay = replay;
 
-  /* Make sure we're not using any stale registers.  */
-  registers_changed_ptid (tp->ptid);
-
-  /* We just started replaying.  The frame id cached for stepping is based
-     on unwinding, not on branch tracing.  Recompute it.  */
-  frame = get_current_frame_nocheck ();
-  insn = btrace_insn_get (replay);
-  sal = find_pc_line (insn->pc, 0);
-  set_step_info (frame, sal);
-
   return replay;
 }
 
@@ -1324,9 +1314,6 @@ record_btrace_stop_replaying (struct thread_info *tp)
 
   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.  */
@@ -1619,10 +1606,6 @@ record_btrace_wait (struct target_ops *ops, ptid_t ptid,
   /* 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;
 }
 
@@ -1662,6 +1645,8 @@ record_btrace_goto_target (struct thread_info *tp,
   struct btrace_insn_iterator *goto_target;
   struct btrace_thread_info *btinfo;
   struct target_waitstatus ws;
+  struct btrace_insn_iterator target_it;
+  volatile struct gdb_exception exception;
 
   btinfo = &tp->btrace;
 
@@ -1686,11 +1671,17 @@ record_btrace_goto_target (struct thread_info *tp,
   btinfo->flags |= BTHR_GOTO;
   btinfo->goto_target = goto_target;
 
-#if 0
+  TRY_CATCH (exception, RETURN_MASK_ALL)
+    {
+
+#if 1
   if (goto_target != NULL)
+    target_it = *goto_target;
+  else
+    btrace_insn_end (&target_it, btinfo);
     tp->control.exception_resume_breakpoint =
       set_momentary_breakpoint_at_pc (target_gdbarch (),
-				      btrace_insn_get (goto_target)->pc,
+				      btrace_insn_get (&target_it)->pc,
 				      bp_until);
 #endif
 #if 0
@@ -1701,8 +1692,18 @@ record_btrace_goto_target (struct thread_info *tp,
   proceed ((CORE_ADDR) -1, GDB_SIGNAL_0, 0);
 #endif
 
-  if (btinfo->goto_target != NULL || (btinfo->flags & BTHR_GOTO) != 0)
+  // It will need a fix if reverse mode supports target-async mode.
+  if ((btinfo->flags & BTHR_GOTO) != 0)
     error (_("Record goto failed."));
+  gdb_assert (btinfo->goto_target == NULL);
+
+    }
+  if (exception.reason < 0)
+    {
+      xfree (btinfo->goto_target);
+      btinfo->goto_target = NULL;
+      btinfo->flags &= ~BTHR_GOTO;
+    }
 }
 
 /* The to_goto_record_begin method of target record-btrace.  */

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

* RE: [+rfc] Re: [patch v6 00/21] record-btrace: reverse
  2013-11-27 20:35     ` Jan Kratochvil
@ 2013-11-28 10:54       ` Metzger, Markus T
  2013-11-28 22:35         ` Jan Kratochvil
  0 siblings, 1 reply; 41+ messages in thread
From: Metzger, Markus T @ 2013-11-28 10:54 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

> -----Original Message-----
> From: Jan Kratochvil [mailto:jan.kratochvil@redhat.com]
> Sent: Wednesday, November 27, 2013 7:57 PM
> To: Metzger, Markus T
> Cc: gdb-patches@sourceware.org
> Subject: Re: [+rfc] Re: [patch v6 00/21] record-btrace: reverse
> 
> On Thu, 07 Nov 2013 16:41:40 +0100, Metzger, Markus T wrote:
> > I hacked a first prototype of this (see below).  It passes most tests but
> > results in three fails in the record_goto suite.
> >
> > One thing that it shows, though, is that it only removes the 'mostly
> harmless'
> > hack in the various goto functions shown above.
> >
> > The more serious hacks in record_btrace_start_replaying
> >
> > 	  /* Make sure we're not using any stale registers.  */
> > 	  registers_changed_ptid (tp->ptid);
> >
> > 	  /* We just started replaying.  The frame id cached for stepping is
> based
> > 	     on unwinding, not on branch tracing.  Recompute it.  */
> > 	  frame = get_current_frame_nocheck ();
> > 	  insn = btrace_insn_get (replay);
> > 	  sal = find_pc_line (insn->pc, 0);
> > 	  set_step_info (frame, sal);
> >
> > and record_btrace_stop_replaying
> >
> > 	  /* Make sure we're not leaving any stale registers.  */
> > 	  registers_changed_ptid (tp->ptid);
> >
> > however, are not removed by this.
> 
> In such case it is not finished.  These hacks should not be needed.

See below.


> > They are required when reverse-stepping the first time or when
> > stepping past the end of the execution trace.
> 
> I have patched what you describe as the problem.  But as I do not have a box
> with reliably working BTS so it is difficult for me to say whether it works or
> not.  I can look at other problems if you describe them from a reliable box.

Those hacks are not related to "record goto" and are thus also not affected
by the patch to implement "record goto" via wait/resume.

Let me try to describe the problem.  It is also exposed by the next.exp test.

Assume we enable btrace and next over a function call.  We will end up
right after the call instruction.

(gdb) record btrace 
(gdb) n
50        return 0;     /* main.3 */
(gdb) record instruction-history -
31         0x0000000000400590 <fun1+0>: push   %rbp
32         0x0000000000400591 <fun1+1>: mov    %rsp,%rbp
33         0x0000000000400594 <fun1+4>: leaveq 
34         0x0000000000400595 <fun1+5>: retq   
35         0x000000000040059f <fun2+9>: leaveq 
36         0x00000000004005a0 <fun2+10>:        retq   
37         0x00000000004005af <fun3+14>:        leaveq 
38         0x00000000004005b0 <fun3+15>:        retq   
39         0x00000000004005c4 <fun4+19>:        leaveq 
40         0x00000000004005c5 <fun4+20>:        retq   
(gdb) disas
Dump of assembler code for function main:
   0x00000000004005c6 <+0>:     push   %rbp
   0x00000000004005c7 <+1>:     mov    %rsp,%rbp
   0x00000000004005ca <+4>:     callq  0x4005b1 <fun4>
=> 0x00000000004005cf <+9>:     mov    $0x0,%eax
   0x00000000004005d4 <+14>:    leaveq 
   0x00000000004005d5 <+15>:    retq   
End of assembler dump.
(gdb)

If we now do a reverse-next, we end up inside the function
we were supposed to step over.

(gdb) reverse-next
fun4 () at record_goto.c:44
44      }               /* fun4.5 */
(gdb) record instruction-history -
30         0x000000000040059a <fun2+4>: callq  0x400590 <fun1>
31         0x0000000000400590 <fun1+0>: push   %rbp
32         0x0000000000400591 <fun1+1>: mov    %rsp,%rbp
33         0x0000000000400594 <fun1+4>: leaveq 
34         0x0000000000400595 <fun1+5>: retq   
35         0x000000000040059f <fun2+9>: leaveq 
36         0x00000000004005a0 <fun2+10>:        retq   
37         0x00000000004005af <fun3+14>:        leaveq 
38         0x00000000004005b0 <fun3+15>:        retq   
39      => 0x00000000004005c4 <fun4+19>:        leaveq 
(gdb) disas
Dump of assembler code for function fun4:
   0x00000000004005b1 <+0>:     push   %rbp
   0x00000000004005b2 <+1>:     mov    %rsp,%rbp
   0x00000000004005b5 <+4>:     callq  0x400590 <fun1>
   0x00000000004005ba <+9>:     callq  0x400596 <fun2>
   0x00000000004005bf <+14>:    callq  0x4005a1 <fun3>
=> 0x00000000004005c4 <+19>:    leaveq 
   0x00000000004005c5 <+20>:    retq   
End of assembler dump.
(gdb)

The reason is the way how GDB implements next/reverse-next.

We store the frame_id of the current frame and do a single-step.  
Then we try to detect stepping into a subroutine by unwinding
the stack frames and comparing the frame_id's with our stored
frame_id.

The stored frame_id has been computed using dwarf2 frame
unwind.
After single-stepping, we're replaying the recorded execution.
The frame_id's are now computed using btrace frame unwind.

Our parent's frame_id does not compare equal to the stored
frame_id.  We fail to detect that we just reverse-stepped into
a subroutine.

The s/w record implementation does not suffer from this problem
because it traces data and is hence able to use the dwarf2 frame
unwinder also when replacing.

The way I tried to overcome this is to recompute all frame_id's
when we start replaying.  This will cause us to store a btrace
frame_id in the stepping algorithm.  Now we are able to detect
that we reverse-stepped into a subroutine.

Do you have a better idea?

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] 41+ messages in thread

* Re: [+rfc] Re: [patch v6 00/21] record-btrace: reverse
  2013-11-28 10:54       ` Metzger, Markus T
@ 2013-11-28 22:35         ` Jan Kratochvil
  2013-11-29 14:27           ` Metzger, Markus T
  0 siblings, 1 reply; 41+ messages in thread
From: Jan Kratochvil @ 2013-11-28 22:35 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 1329 bytes --]

On Thu, 28 Nov 2013 09:42:16 +0100, Metzger, Markus T wrote:
> We store the frame_id of the current frame and do a single-step.  
> Then we try to detect stepping into a subroutine by unwinding
> the stack frames and comparing the frame_id's with our stored
> frame_id.

OK, understood now.

In fact the time you change btinfo->replay you also change register contents.
Therefore the registers_changed_ptid() call is there right.

For the frame_id change one could also provide mapping of old frame_id to new
frame_id in frame_id_eq() but that is worse than just re-setting it.


> Do you have a better idea?

I agree in general.  Just:

Rather than get_current_frame_nocheck I find safer to just temporarily switch
off the executing flag.  There are many other checks which make sense which
were omitted.

Calling set_step_info seems needlessly intrusive to me, there is no need to
re-set tp->current_symtab + tp->current_line.  Despite in the moment of
record_btrace_start_replaying() I see step_frame_id should be the current
frame id it does not have to be.  Such as when we reverse-continue, it will be
null_frame_id.

The attached patch(es) is on top of yours.

I still believe inferior should resume + wait if it changes its PC.

I am again not sure if the patch passes without FAILs due to my BTS hardware.


Thanks,
Jan

[-- Attachment #2: 1 --]
[-- Type: text/plain, Size: 2747 bytes --]

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index c50e11b..4a57b51 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -1281,9 +1281,8 @@ record_btrace_start_replaying (struct thread_info *tp)
 {
   struct btrace_thread_info *btinfo;
   struct btrace_insn_iterator *replay;
-  const struct btrace_insn *insn;
-  struct symtab_and_line sal;
-  struct frame_info *frame;
+  volatile struct gdb_exception except;
+  int executing;
 
   btinfo = &tp->btrace;
 
@@ -1291,24 +1290,50 @@ record_btrace_start_replaying (struct thread_info *tp)
   if (btinfo->begin == NULL)
     return NULL;
 
-  /* 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);
+  executing = is_executing (tp->ptid);
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      int update_step_frame_id, update_step_stack_frame_id;
+      struct frame_id frame_id;
 
-  /* We're not replaying, yet.  */
-  gdb_assert (btinfo->replay == NULL);
-  btinfo->replay = replay;
+      /* 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);
 
-  /* Make sure we're not using any stale registers.  */
-  registers_changed_ptid (tp->ptid);
+      if (executing)
+	{
+	  /* get_current_frame would error out otherwise.  */
+	  set_executing (tp->ptid, 0);
+	}
+
+      frame_id = get_frame_id (get_current_frame ());
 
-  /* We just started replaying.  The frame id cached for stepping is based
-     on unwinding, not on branch tracing.  Recompute it.  */
-  frame = get_current_frame_nocheck ();
-  insn = btrace_insn_get (replay);
-  sal = find_pc_line (insn->pc, 0);
-  set_step_info (frame, sal);
+      update_step_frame_id = frame_id_eq (frame_id, tp->control.step_frame_id);
+      update_step_stack_frame_id = frame_id_eq (frame_id,
+					       tp->control.step_stack_frame_id);
+
+      /* 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);
+
+      /* We just started replaying.  The frame id cached for stepping is based
+	 on unwinding, not on branch tracing.  Recompute it.  */
+
+      frame_id = get_frame_id (get_current_frame ());
+
+      if (update_step_frame_id)
+	tp->control.step_frame_id = frame_id;
+      if (update_step_stack_frame_id)
+	tp->control.step_stack_frame_id = frame_id;
+    }
+  if (executing)
+    set_executing (tp->ptid, 1);
+  if (except.reason < 0)
+    throw_exception (except);
 
   return replay;
 }

[-- Attachment #3: 2 --]
[-- Type: text/plain, Size: 2047 bytes --]

diff --git a/gdb/frame.c b/gdb/frame.c
index 5a6f107..0fd98ff 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1367,29 +1367,6 @@ unwind_to_current_frame (struct ui_out *ui_out, void *args)
   return 0;
 }
 
-/* See frame.h.  */
-
-struct frame_info *get_current_frame_nocheck (void)
-{
-  if (current_frame == NULL)
-    {
-      struct frame_info *sentinel_frame =
-	create_sentinel_frame (current_program_space, get_current_regcache ());
-
-      if (catch_exceptions (current_uiout, unwind_to_current_frame,
-			    sentinel_frame, RETURN_MASK_ERROR) != 0)
-	{
-	  /* Oops! Fake a current frame?  Is this useful?  It has a PC
-             of zero, for instance.  */
-	  current_frame = sentinel_frame;
-	}
-    }
-
-  return current_frame;
-}
-
-/* See frame.h.  */
-
 struct frame_info *
 get_current_frame (void)
 {
@@ -1415,7 +1392,19 @@ get_current_frame (void)
 	error (_("Target is executing."));
     }
 
-  return get_current_frame_nocheck ();
+  if (current_frame == NULL)
+    {
+      struct frame_info *sentinel_frame =
+	create_sentinel_frame (current_program_space, get_current_regcache ());
+      if (catch_exceptions (current_uiout, unwind_to_current_frame,
+			    sentinel_frame, RETURN_MASK_ERROR) != 0)
+	{
+	  /* Oops! Fake a current frame?  Is this useful?  It has a PC
+             of zero, for instance.  */
+	  current_frame = sentinel_frame;
+	}
+    }
+  return current_frame;
 }
 
 /* The "selected" stack frame is used by default for local and arg
diff --git a/gdb/frame.h b/gdb/frame.h
index cd2033d..f0da19e 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -242,10 +242,6 @@ enum frame_type
    error.  */
 extern struct frame_info *get_current_frame (void);
 
-/* Similar to get_current_frame except that we omit all checks.  May
-   return NULL if unwinding fails.  */
-extern struct frame_info *get_current_frame_nocheck (void);
-
 /* Does the current target interface have enough state to be able to
    query the current inferior for frame info, and is the inferior in a
    state where that is possible?  */

[-- Attachment #4: 3 --]
[-- Type: text/plain, Size: 1619 bytes --]

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index c50e11b..4a57b51 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -1662,6 +1687,8 @@ record_btrace_goto_target (struct thread_info *tp,
   struct btrace_insn_iterator *goto_target;
   struct btrace_thread_info *btinfo;
   struct target_waitstatus ws;
+  struct btrace_insn_iterator target_it;
+  volatile struct gdb_exception exception;
 
   btinfo = &tp->btrace;
 
@@ -1686,11 +1713,17 @@ record_btrace_goto_target (struct thread_info *tp,
   btinfo->flags |= BTHR_GOTO;
   btinfo->goto_target = goto_target;
 
-#if 0
+  TRY_CATCH (exception, RETURN_MASK_ALL)
+    {
+
+#if 1
   if (goto_target != NULL)
+    target_it = *goto_target;
+  else
+    btrace_insn_end (&target_it, btinfo);
     tp->control.exception_resume_breakpoint =
       set_momentary_breakpoint_at_pc (target_gdbarch (),
-				      btrace_insn_get (goto_target)->pc,
+				      btrace_insn_get (&target_it)->pc,
 				      bp_until);
 #endif
 #if 0
@@ -1701,8 +1734,18 @@ record_btrace_goto_target (struct thread_info *tp,
   proceed ((CORE_ADDR) -1, GDB_SIGNAL_0, 0);
 #endif
 
-  if (btinfo->goto_target != NULL || (btinfo->flags & BTHR_GOTO) != 0)
+  // It will need a fix if reverse mode supports target-async mode.
+  if ((btinfo->flags & BTHR_GOTO) != 0)
     error (_("Record goto failed."));
+  gdb_assert (btinfo->goto_target == NULL);
+
+    }
+  if (exception.reason < 0)
+    {
+      xfree (btinfo->goto_target);
+      btinfo->goto_target = NULL;
+      btinfo->flags &= ~BTHR_GOTO;
+    }
 }
 
 /* The to_goto_record_begin method of target record-btrace.  */

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

* RE: [+rfc] Re: [patch v6 00/21] record-btrace: reverse
  2013-11-28 22:35         ` Jan Kratochvil
@ 2013-11-29 14:27           ` Metzger, Markus T
  2013-12-11 19:57             ` Jan Kratochvil
  0 siblings, 1 reply; 41+ messages in thread
From: Metzger, Markus T @ 2013-11-29 14:27 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

> -----Original Message-----
> From: Jan Kratochvil [mailto:jan.kratochvil@redhat.com]
> Sent: Thursday, November 28, 2013 10:16 PM

Hello Jan,

> Rather than get_current_frame_nocheck I find safer to just temporarily
> switch
> off the executing flag.  There are many other checks which make sense
> which
> were omitted.
> 
> Calling set_step_info seems needlessly intrusive to me, there is no need to
> re-set tp->current_symtab + tp->current_line.  Despite in the moment of
> record_btrace_start_replaying() I see step_frame_id should be the current
> frame id it does not have to be.  Such as when we reverse-continue, it will be
> null_frame_id.

I implemented that based on your patches.  It works fine, no regressions.
Thanks for helping make that less hacky.


> I still believe inferior should resume + wait if it changes its PC.

Here I still have problems with multi-threaded inferiors.  When I switch to
a different thread and then "record goto", I get a "No more reverse-execution
history" error followed by a "Record goto failed" error.

I believe that "record goto" is resuming the wrong thread after I switch away
from the eventing thread.  Curiously, this does not seem to be a problem if
I do not set tp->control.exception_resume_breapoint.  This needs some
more debugging.


On a related topic, when we implement "record goto" via resume/wait, we will
trigger breakpoints at the target location.  This might or might not be desired.
We do not trigger breakpoints on our way to the record goto target location.
Again, this might or might not be desired.

Record full's "record goto" command does not trigger breakpoints; neither at
the target location nor on its way.  In fact, record full does not implement
"record goto" via resume/wait.  Instead, it single-steps through its execution
history in a simple do-while loop, similar to what I am doing in record btrace.


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] 41+ messages in thread

* Re: [+rfc] Re: [patch v6 00/21] record-btrace: reverse
  2013-11-29 14:27           ` Metzger, Markus T
@ 2013-12-11 19:57             ` Jan Kratochvil
  0 siblings, 0 replies; 41+ messages in thread
From: Jan Kratochvil @ 2013-12-11 19:57 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: gdb-patches

On Fri, 29 Nov 2013 15:00:22 +0100, Metzger, Markus T wrote:
> Here I still have problems with multi-threaded inferiors.  When I switch to
> a different thread and then "record goto", I get a "No more reverse-execution
> history" error followed by a "Record goto failed" error.
> 
> I believe that "record goto" is resuming the wrong thread after I switch away
> from the eventing thread.  Curiously, this does not seem to be a problem if
> I do not set tp->control.exception_resume_breapoint.  This needs some
> more debugging.
> 
> 
> On a related topic, when we implement "record goto" via resume/wait, we will
> trigger breakpoints at the target location.  This might or might not be desired.
> We do not trigger breakpoints on our way to the record goto target location.
> Again, this might or might not be desired.
> 
> Record full's "record goto" command does not trigger breakpoints; neither at
> the target location nor on its way.  In fact, record full does not implement
> "record goto" via resume/wait.  Instead, it single-steps through its execution
> history in a simple do-while loop, similar to what I am doing in record btrace.

Execution of commands of a breakpoint at the target location is sure a bug,
I did not realize it.  Thanks for finding it.

I was now looking how to suppress them but I do not think there is an easy
soltuion.  So I think your previous code without resume+wait during goto was
better and the changes should be reverted.

It will hopefully also fix the multi-threading bug above I have not
investigated.

Could you do a hopefully final v8 post?


Thanks,
Jan

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

end of thread, other threads:[~2013-12-11 19:57 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-09-20 11:30 [patch v6 00/21] record-btrace: reverse Markus Metzger
2013-09-20 11:30 ` [patch v6 15/21] record-btrace: add to_wait and to_resume target methods Markus Metzger
2013-09-20 11:30 ` [patch v6 12/21] frame, backtrace: allow targets to supply a frame unwinder Markus Metzger
2013-09-20 11:30 ` [patch v6 14/21] record-btrace: provide xfer_partial target method Markus Metzger
2013-09-20 11:30 ` [patch v6 02/21] gdbarch: add instruction predicate methods Markus Metzger
2013-09-20 11:30 ` [patch v6 10/21] target: add ops parameter to to_prepare_to_store method Markus Metzger
2013-09-20 11:30 ` [patch v6 16/21] record-btrace: provide target_find_new_threads method Markus Metzger
2013-09-20 11:30 ` [patch v6 06/21] btrace: increase buffer size Markus Metzger
2013-09-20 11:31 ` [patch v6 03/21] btrace: change branch trace data structure Markus Metzger
2013-10-06 19:46   ` Jan Kratochvil
2013-09-20 11:31 ` [patch v6 13/21] record-btrace, frame: supply target-specific unwinder Markus Metzger
2013-09-20 11:31 ` [patch v6 04/21] record-btrace: fix insn range in function call history Markus Metzger
2013-09-20 11:31 ` [patch v6 19/21] btrace, gdbserver: read branch trace incrementally Markus Metzger
2013-10-06 19:51   ` Jan Kratochvil
2013-09-20 11:31 ` [patch v6 07/21] record-btrace: optionally indent function call history Markus Metzger
2013-10-06 19:47   ` Jan Kratochvil
2013-09-20 11:31 ` [patch v6 20/21] record-btrace: show trace from enable location Markus Metzger
2013-09-20 11:31 ` [patch v6 08/21] record-btrace: make ranges include begin and end Markus Metzger
2013-09-20 11:31 ` [patch v6 09/21] btrace: add replay position to btrace thread info Markus Metzger
2013-09-20 11:31 ` [patch v6 21/21] record-btrace: add (reverse-)stepping support Markus Metzger
2013-10-06 19:52   ` Jan Kratochvil
2013-11-06 15:06     ` Metzger, Markus T
2013-11-26 13:48       ` Jan Kratochvil
2013-09-20 11:31 ` [patch v6 18/21] record-btrace: extend unwinder Markus Metzger
2013-10-06 19:49   ` Jan Kratochvil
2013-11-06 13:45     ` Metzger, Markus T
2013-11-25 21:11       ` Jan Kratochvil
2013-09-20 11:31 ` [patch v6 05/21] record-btrace: start counting at one Markus Metzger
2013-09-20 11:31 ` [patch v6 01/21] btrace, linux: fix memory leak when reading branch trace Markus Metzger
2013-09-20 11:31 ` [patch v6 17/21] record-btrace: add record goto target methods Markus Metzger
2013-10-06 19:48   ` Jan Kratochvil
2013-09-20 11:31 ` [patch v6 11/21] record-btrace: supply register " Markus Metzger
2013-09-26 19:16 ` v6 crash bugreport [Re: [patch v6 00/21] record-btrace: reverse] Jan Kratochvil
2013-09-27  6:37   ` Metzger, Markus T
2013-10-06 19:59 ` [+rfc] Re: [patch v6 00/21] record-btrace: reverse Jan Kratochvil
2013-11-07 15:44   ` Metzger, Markus T
2013-11-27 20:35     ` Jan Kratochvil
2013-11-28 10:54       ` Metzger, Markus T
2013-11-28 22:35         ` Jan Kratochvil
2013-11-29 14:27           ` Metzger, Markus T
2013-12-11 19:57             ` Jan Kratochvil

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).