public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v10 14/28] record-btrace: supply register target methods
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
  2014-01-14  8:04 ` [PATCH v10 01/28] btrace, test: fix multi-line btrace tests Markus Metzger
@ 2014-01-14  8:04 ` Markus Metzger
  2014-01-14  8:04 ` [PATCH v10 18/28] record-btrace, frame: supply target-specific unwinder Markus Metzger
                   ` (26 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:04 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

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

2014-01-14  Markus Metzger  <markus.t.metzger@intel.com>

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


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

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

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

* [PATCH v10 05/28] frame: add frame_id_build_unavailable_stack_special
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (5 preceding siblings ...)
  2014-01-14  8:04 ` [PATCH v10 10/28] record-btrace: optionally indent function call history Markus Metzger
@ 2014-01-14  8:04 ` Markus Metzger
  2014-01-14  8:04 ` [PATCH v10 03/28] btrace: uppercase btrace_read_type Markus Metzger
                   ` (21 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:04 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

Add a function to build a frame_id for a frame with unavailable stack
and with a special identifier address.

Reviewed-by:  Pedro Alves

2014-01-14  Markus Metzger  <markus.t.metzger@intel.com>

	* frame.h (frame_id_build_unavailable_stack_special): New.
	* frame.c (frame_id_build_unavailable_stack_special): New.


---
 gdb/frame.c | 16 ++++++++++++++++
 gdb/frame.h |  8 ++++++++
 2 files changed, 24 insertions(+)

diff --git a/gdb/frame.c b/gdb/frame.c
index 10d486c..b72d5e4 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -528,6 +528,22 @@ frame_id_build_unavailable_stack (CORE_ADDR code_addr)
   return id;
 }
 
+/* See frame.h.  */
+
+struct frame_id
+frame_id_build_unavailable_stack_special (CORE_ADDR code_addr,
+					  CORE_ADDR special_addr)
+{
+  struct frame_id id = null_frame_id;
+
+  id.stack_status = FID_STACK_UNAVAILABLE;
+  id.code_addr = code_addr;
+  id.code_addr_p = 1;
+  id.special_addr = special_addr;
+  id.special_addr_p = 1;
+  return id;
+}
+
 struct frame_id
 frame_id_build (CORE_ADDR stack_addr, CORE_ADDR code_addr)
 {
diff --git a/gdb/frame.h b/gdb/frame.h
index f7e46d5..e451a93 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -193,6 +193,14 @@ extern struct frame_id frame_id_build_special (CORE_ADDR stack_addr,
    address is set to indicate a wild card.  */
 extern struct frame_id frame_id_build_unavailable_stack (CORE_ADDR code_addr);
 
+/* Construct a frame ID representing a frame where the stack address
+   exists, but is unavailable.  CODE_ADDR is the frame's constant code
+   address (typically the entry point).  SPECIAL_ADDR is the special
+   identifier address.  */
+extern struct frame_id
+  frame_id_build_unavailable_stack_special (CORE_ADDR code_addr,
+					    CORE_ADDR special_addr);
+
 /* Construct a wild card frame ID.  The parameter is the frame's constant
    stack address (typically the outer-bound).  The code address as well
    as the special identifier address are set to indicate wild cards.  */
-- 
1.8.3.1

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

* [PATCH v10 26/28] record-btrace: show trace from enable location
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (3 preceding siblings ...)
  2014-01-14  8:04 ` [PATCH v10 20/28] record-btrace: provide xfer_partial target method Markus Metzger
@ 2014-01-14  8:04 ` Markus Metzger
  2014-01-14  8:04 ` [PATCH v10 10/28] record-btrace: optionally indent function call history Markus Metzger
                   ` (23 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:04 UTC (permalink / raw)
  To: jan.kratochvil, palves; +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.

2014-01-14  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/Makefile.in (EXECUTABLES): Add delta.
	* 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/Makefile.in             |  2 +-
 gdb/testsuite/gdb.btrace/delta.exp               | 68 +++++++++++++++++
 gdb/testsuite/gdb.btrace/exception.exp           | 22 +++---
 gdb/testsuite/gdb.btrace/instruction_history.exp | 68 ++++++++---------
 gdb/testsuite/gdb.btrace/record_goto.exp         | 97 ++++++++++++------------
 gdb/testsuite/gdb.btrace/tailcall.exp            | 16 ++--
 gdb/testsuite/gdb.btrace/unknown_functions.exp   | 22 +++---
 10 files changed, 240 insertions(+), 118 deletions(-)
 create mode 100644 gdb/testsuite/gdb.btrace/delta.exp

diff --git a/gdb/btrace.c b/gdb/btrace.c
index 28970c3..32918ce 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.  */
@@ -674,6 +675,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
@@ -688,6 +715,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.  */
@@ -1475,3 +1507,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 93e6940..193f916 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 07575f6..61caee4 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -68,7 +68,6 @@ static struct thread_info *
 require_btrace_thread (void)
 {
   struct thread_info *tp;
-  struct btrace_thread_info *btinfo;
 
   DEBUG ("require");
 
@@ -78,9 +77,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;
@@ -242,7 +239,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/Makefile.in b/gdb/testsuite/gdb.btrace/Makefile.in
index 8311cba..2ae673a 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 unknown_functions record_goto
+  exception unknown_functions record_goto delta
 
 MISCELLANEOUS =
 
diff --git a/gdb/testsuite/gdb.btrace/delta.exp b/gdb/testsuite/gdb.btrace/delta.exp
new file mode 100644
index 0000000..e606751
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/delta.exp
@@ -0,0 +1,68 @@
+# 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
+with_test_prefix "no trace" {
+  gdb_test "info record" [join [list \
+    "Active record target: record-btrace" \
+    "Recorded 0 instructions in 0 functions for .*" \
+    ] "\r\n"]
+  gdb_test "record instruction-history" "No trace\."
+  gdb_test "record function-call-history" "No trace\."
+}
+
+# we record each single-step, even if we have not seen a branch, yet.
+gdb_test "stepi"
+
+proc check_trace {} {
+  gdb_test "info record" [join [list \
+    "Active record target: record-btrace" \
+    "Recorded 1 instructions in 1 functions for .*" \
+    ] "\r\n"]
+  gdb_test "record instruction-history /f 1" \
+    "1\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tmov *\\\$0x0,%eax\r"
+  gdb_test "record function-call-history /c 1" "1\tmain"
+}
+
+# make sure we don't extend the trace when we ask twice.
+with_test_prefix "once" {
+  check_trace
+}
+
+with_test_prefix "twice" {
+  check_trace
+}
diff --git a/gdb/testsuite/gdb.btrace/exception.exp b/gdb/testsuite/gdb.btrace/exception.exp
index 46ed542..0279a7f 100755
--- a/gdb/testsuite/gdb.btrace/exception.exp
+++ b/gdb/testsuite/gdb.btrace/exception.exp
@@ -47,10 +47,11 @@ gdb_continue_to_breakpoint "cont to bp.2" ".*$srcfile:$bp_2\r\n.*"
 send_gdb "record function-call-history 1\n"
 gdb_expect_list "flat" "\r\n$gdb_prompt $" [list \
   [join [list \
-    "1\ttest\\(\\)" \
-    "2\tfoo\\(\\)" \
-    "3\tbar\\(\\)" \
-    "4\tbad\\(\\)" \
+    "1\tmain\\(\\)" \
+    "2\ttest\\(\\)" \
+    "3\tfoo\\(\\)" \
+    "4\tbar\\(\\)" \
+    "5\tbad\\(\\)\r" \
   ] "\r\n"] \
   "" \
   "\[0-9\]*\ttest\\(\\)"]
@@ -59,10 +60,11 @@ gdb_expect_list "flat" "\r\n$gdb_prompt $" [list \
 send_gdb "record function-call-history /c 1\n"
 gdb_expect_list "indented" "\r\n$gdb_prompt $" [list \
   [join [list \
-    "1\ttest\\(\\)" \
-    "2\t  foo\\(\\)" \
-    "3\t    bar\\(\\)" \
-    "4\t      bad\\(\\)\r" \
-    ] "\r\n"] \
+    "1\tmain\\(\\)" \
+  "2\t  test\\(\\)" \
+  "3\t    foo\\(\\)" \
+  "4\t      bar\\(\\)" \
+  "5\t        bad\\(\\)\r" \
+  ] "\r\n"] \
   "" \
-  "\[0-9\]*\ttest\\(\\)"]
+  "\[0-9\]*\t  test\\(\\)"]
diff --git a/gdb/testsuite/gdb.btrace/instruction_history.exp b/gdb/testsuite/gdb.btrace/instruction_history.exp
index 5769345..29102c4 100644
--- a/gdb/testsuite/gdb.btrace/instruction_history.exp
+++ b/gdb/testsuite/gdb.btrace/instruction_history.exp
@@ -56,48 +56,48 @@ 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" [join [list \
-  "2\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
-  "3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax" \
-  "4\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
-  "5\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
-  "6\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
+gdb_test "record instruction-history 3,7" [join [list \
+  "3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "4\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax" \
+  "5\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "6\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
+  "7\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
   ] "\r\n"]
 
-gdb_test "record instruction-history /f 2,+5" [join [list \
-  "2\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
-  "3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax" \
-  "4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
-  "5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
-  "6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
+gdb_test "record instruction-history /f 3,+5" [join [list \
+  "3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax" \
+  "5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
+  "7\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
   ] "\r\n"]
 
-gdb_test "record instruction-history /p 6,-5" [join [list \
-  "2\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
-  "3\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax" \
-  "4\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
-  "5\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
-  "6\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
+gdb_test "record instruction-history /p 7,-5" [join [list \
+  "3\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "4\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax" \
+  "5\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "6\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
+  "7\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
   ] "\r\n"]
 
-gdb_test "record instruction-history /pf 2,6" [join [list \
-  "2\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
-  "3\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax" \
-  "4\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
-  "5\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
-  "6\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
+gdb_test "record instruction-history /pf 3,7" [join [list \
+  "3\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "4\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax" \
+  "5\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "6\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
+  "7\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
   ] "\r\n"]
 
-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,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
@@ -140,7 +140,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]
@@ -150,8 +150,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 } {
@@ -162,7 +160,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
@@ -173,8 +171,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 } {
@@ -183,8 +179,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 } {
@@ -195,7 +189,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 ab69bef..43028f4 100644
--- a/gdb/testsuite/gdb.btrace/record_goto.exp
+++ b/gdb/testsuite/gdb.btrace/record_goto.exp
@@ -50,42 +50,43 @@ gdb_test "next"
 
 # start by listing all functions
 gdb_test "record function-call-history /ci 1, +20" [join [list \
-  "1\tfun4\tinst 1,3" \
-  "2\t  fun1\tinst 4,7" \
-  "3\tfun4\tinst 8,8" \
-  "4\t  fun2\tinst 9,11" \
-  "5\t    fun1\tinst 12,15" \
-  "6\t  fun2\tinst 16,17" \
-  "7\tfun4\tinst 18,18" \
-  "8\t  fun3\tinst 19,21" \
-  "9\t    fun1\tinst 22,25" \
-  "10\t  fun3\tinst 26,26" \
-  "11\t    fun2\tinst 27,29" \
-  "12\t      fun1\tinst 30,33" \
-  "13\t    fun2\tinst 34,35" \
-  "14\t  fun3\tinst 36,37" \
-  "15\tfun4\tinst 38,39" \
+  "1\tmain\tinst 1,1" \
+  "2\t  fun4\tinst 2,4" \
+  "3\t    fun1\tinst 5,8" \
+  "4\t  fun4\tinst 9,9" \
+  "5\t    fun2\tinst 10,12" \
+  "6\t      fun1\tinst 13,16" \
+  "7\t    fun2\tinst 17,18" \
+  "8\t  fun4\tinst 19,19" \
+  "9\t    fun3\tinst 20,22" \
+  "10\t      fun1\tinst 23,26" \
+  "11\t    fun3\tinst 27,27" \
+  "12\t      fun2\tinst 28,30" \
+  "13\t        fun1\tinst 31,34" \
+  "14\t      fun2\tinst 35,36" \
+  "15\t    fun3\tinst 37,38" \
+  "16\t  fun4\tinst 39,40" \
   ] "\r\n"]
 
 # let's see if we can go back in history
-gdb_test "record goto 18" ".*fun4 \\(\\) at record_goto.c:43.*"
+gdb_test "record goto 19" ".*fun4 \\(\\) at record_goto.c:43.*"
 
 # the function call history should start at the new location
 gdb_test "record function-call-history /ci" [join [list \
-  "7\tfun4\tinst 18,18" \
-  "8\t  fun3\tinst 19,21" \
-  "9\t    fun1\tinst 22,25" \
-  ] "\r\n"] "function-call-history from 18 forwards"
+  "8\t  fun4\tinst 19,19" \
+  "9\t    fun3\tinst 20,22" \
+  "10\t      fun1\tinst 23,26" \
+  ] "\r\n"] "function-call-history from 19 forwards"
 
 # the instruction history should start at the new location
 gdb_test "record instruction-history" [join [list \
-  "18.*" \
   "19.*" \
   "20.*" \
-  ] "\r\n"] "instruction-history from 18 forwards"
+  "21.*" \
+  ] "\r\n"] "instruction-history from 19 forwards"
 
 # let's go to another place in the history
-gdb_test "record goto 26" ".*fun3 \\(\\) at record_goto.c:35.*"
+gdb_test "record goto 27" ".*fun3 \\(\\) at record_goto.c:35.*"
 
 # check the back trace at that location
 gdb_test "backtrace" [join [list \
@@ -101,26 +102,26 @@ gdb_test "up" ".*main.*at record_goto.c:49.*" "up to main"
 
 # the function call history should start at the new location
 gdb_test "record function-call-history /ci -" [join [list \
-  "8\t  fun3\tinst 19,21" \
-  "9\t    fun1\tinst 22,25" \
-  "10\t  fun3\tinst 26,26\r" \
-  ] "\r\n"] "function-call-history from 26 backwards"
+  "9\t    fun3\tinst 20,22" \
+  "10\t      fun1\tinst 23,26" \
+  "11\t    fun3\tinst 27,27" \
+  ] "\r\n"] "function-call-history from 27 backwards"
 
 # the instruction history should start at the new location
 gdb_test "record instruction-history -" [join [list \
-  "24.*" \
   "25.*" \
-  "26.*\r" \
-  ] "\r\n"] "instruction-history from 26 backwards"
+  "26.*" \
+  "27.*" \
+  ] "\r\n"] "instruction-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.*"
+gdb_test "record goto begin" ".*main \\(\\) at record_goto.c:49.*"
 
 # check that we're filling up the context correctly
 gdb_test "record function-call-history /ci -" [join [list \
-  "1\tfun4\tinst 1,3" \
-  "2\t  fun1\tinst 4,7" \
-  "3\tfun4\tinst 8,8" \
+  "1\tmain\tinst 1,1" \
+  "2\t  fun4\tinst 2,4" \
+  "3\t    fun1\tinst 5,8" \
   ] "\r\n"] "function-call-history from begin backwards"
 
 # check that we're filling up the context correctly
@@ -135,9 +136,9 @@ gdb_test "record goto 2" ".*fun4 \\(\\) at record_goto.c:40.*"
 
 # check that we're filling up the context correctly
 gdb_test "record function-call-history /ci -" [join [list \
-  "1\tfun4\tinst 1,3" \
-  "2\t  fun1\tinst 4,7" \
-  "3\tfun4\tinst 8,8" \
+  "1\tmain\tinst 1,1" \
+  "2\t  fun4\tinst 2,4" \
+  "3\t    fun1\tinst 5,8\r" \
   ] "\r\n"] "function-call-history from 2 backwards"
 
 # check that we're filling up the context correctly
@@ -152,31 +153,31 @@ gdb_test "record goto end" ".*main \\(\\) at record_goto.c:50.*"
 
 # check that we're filling up the context correctly
 gdb_test "record function-call-history /ci" [join [list \
-  "13\t    fun2\tinst 34,35" \
-  "14\t  fun3\tinst 36,37" \
-  "15\tfun4\tinst 38,39" \
+  "14\t      fun2\tinst 35,36" \
+  "15\t    fun3\tinst 37,38" \
+  "16\t  fun4\tinst 39,40" \
   ] "\r\n"] "function-call-history from end forwards"
 
 # check that we're filling up the context correctly
 gdb_test "record instruction-history" [join [list \
-  "37.*" \
   "38.*" \
   "39.*" \
+  "40.*\r" \
   ] "\r\n"] "instruction-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.*"
+gdb_test "record goto 39" ".*fun4 \\(\\) at record_goto.c:44.*"
 
 # check that we're filling up the context correctly
 gdb_test "record function-call-history /ci" [join [list \
-  "13\t    fun2\tinst 34,35" \
-  "14\t  fun3\tinst 36,37" \
-  "15\tfun4\tinst 38,39" \
-  ] "\r\n"] "function-call-history from 38 forwards"
+  "14\t      fun2\tinst 35,36" \
+  "15\t    fun3\tinst 37,38" \
+  "16\t  fun4\tinst 39,40\r" \
+  ] "\r\n"] "function-call-history from 39 forwards"
 
 # check that we're filling up the context correctly
 gdb_test "record instruction-history" [join [list \
-  "37.*" \
   "38.*" \
   "39.*" \
-  ] "\r\n"] "instruction-history from 38 forwards"
+  "40.*\r" \
+  ] "\r\n"] "instruction-history from 39 forwards"
diff --git a/gdb/testsuite/gdb.btrace/tailcall.exp b/gdb/testsuite/gdb.btrace/tailcall.exp
index a001783..23988f2 100644
--- a/gdb/testsuite/gdb.btrace/tailcall.exp
+++ b/gdb/testsuite/gdb.btrace/tailcall.exp
@@ -49,20 +49,22 @@ gdb_test "next"
 
 # show the flat branch trace
 gdb_test "record function-call-history 1" [join [list \
-  "1\tfoo" \
-  "2\tbar" \
-  "3\tmain" \
+  "1\tmain" \
+  "2\tfoo" \
+  "3\tbar" \
+  "4\tmain" \
   ] "\r\n"] "flat"
 
 # show the branch trace with calls indented
 gdb_test "record function-call-history /c 1" [join [list \
-  "1\t  foo" \
-  "2\t    bar" \
-  "3\tmain" \
+  "1\tmain" \
+  "2\t  foo" \
+  "3\t    bar" \
+  "4\tmain" \
   ] "\r\n"] "indented"
 
 # go into bar
-gdb_test "record goto 3" ".*bar \\(\\) at .*x86-tailcall.c:24\r\n.*"
+gdb_test "record goto 4" ".*bar \\(\\) at .*x86-tailcall.c:24\r\n.*"
 
 # check the backtrace
 gdb_test "backtrace" [join [list \
diff --git a/gdb/testsuite/gdb.btrace/unknown_functions.exp b/gdb/testsuite/gdb.btrace/unknown_functions.exp
index f678358..44aa712 100644
--- a/gdb/testsuite/gdb.btrace/unknown_functions.exp
+++ b/gdb/testsuite/gdb.btrace/unknown_functions.exp
@@ -41,20 +41,22 @@ gdb_continue_to_breakpoint "cont to test" ".*test.*"
 
 # show the flat branch trace
 gdb_test "record function-call-history 1" [join [list \
-  "1\t\\\?\\\?" \
+  "1\ttest" \
   "2\t\\\?\\\?" \
   "3\t\\\?\\\?" \
-  "4\ttest" \
-  "5\tmain" \
-  "6\ttest" \
+  "4\t\\\?\\\?" \
+  "5\ttest" \
+  "6\tmain" \
+  "7\ttest" \
   ] "\r\n"] "flat"
 
 # show the branch trace with calls indented
 gdb_test "record function-call-history /c 1" [join [list \
-  "1\t    \\\?\\\?" \
-  "2\t      \\\?\\\?" \
-  "3\t    \\\?\\\?" \
-  "4\t  test" \
-  "5\tmain" \
-  "6\t  test" \
+  "1\t  test" \
+  "2\t    \\\?\\\?" \
+  "3\t      \\\?\\\?" \
+  "4\t    \\\?\\\?" \
+  "5\t  test" \
+  "6\tmain" \
+  "7\t  test" \
   ] "\r\n"] "indented"
-- 
1.8.3.1

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

* [PATCH v10 20/28] record-btrace: provide xfer_partial target method
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (2 preceding siblings ...)
  2014-01-14  8:04 ` [PATCH v10 18/28] record-btrace, frame: supply target-specific unwinder Markus Metzger
@ 2014-01-14  8:04 ` Markus Metzger
  2014-01-15 15:51   ` Pedro Alves
  2014-01-14  8:04 ` [PATCH v10 26/28] record-btrace: show trace from enable location Markus Metzger
                   ` (24 subsequent siblings)
  28 siblings, 1 reply; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:04 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

Provide the xfer_partial target method for the btrace record target.

Only allow memory read accesses to readonly memory while we're replaying,
except for inserting and removing breakpoints.

2014-01-14  Markus Metzger  <markus.t.metzger@intel.com>

	* record-btrace.c (record_btrace_xfer_partial)
	(record_btrace_insert_breakpoint, record_btrace_remove_breakpoint)
	(record_btrace_allow_memory_access): New.
	(init_record_btrace_ops): Initialize new methods.
	* target.c (raw_memory_xfer_partial): Bail out if target reports
	that this memory is not available.


---
 gdb/record-btrace.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/target.c        |   4 ++
 2 files changed, 116 insertions(+)

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 870ad7d..24e697f 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -41,6 +41,9 @@ static struct target_ops record_btrace_ops;
 /* A new thread observer enabling branch tracing for the new thread.  */
 static struct observer *record_btrace_thread_observer;
 
+/* Temporarily allow memory accesses.  */
+static int record_btrace_allow_memory_access;
+
 /* Print a record-btrace debug message.  Use do ... while (0) to avoid
    ambiguities when used in if statements.  */
 
@@ -753,6 +756,112 @@ 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_allow_memory_access && record_btrace_is_replaying ())
+    {
+      switch (object)
+	{
+	case TARGET_OBJECT_MEMORY:
+	  {
+	    struct target_section *section;
+
+	    /* We do not allow writing memory in general.  */
+	    if (writebuf != NULL)
+	      return TARGET_XFER_E_UNAVAILABLE;
+
+	    /* We allow reading readonly memory.  */
+	    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 TARGET_XFER_E_UNAVAILABLE;
+	  }
+	}
+    }
+
+  /* Forward the request.  */
+  for (ops = ops->beneath; ops != NULL; ops = ops->beneath)
+    if (ops->to_xfer_partial != NULL)
+      return ops->to_xfer_partial (ops, object, annex, readbuf, writebuf,
+				   offset, len);
+
+  return TARGET_XFER_E_UNAVAILABLE;
+}
+
+/* The to_insert_breakpoint method of target record-btrace.  */
+
+static int
+record_btrace_insert_breakpoint (struct target_ops *ops,
+				 struct gdbarch *gdbarch,
+				 struct bp_target_info *bp_tgt)
+{
+  volatile struct gdb_exception except;
+  int old, ret;
+
+  /* Inserting breakpoints requires accessing memory.  Allow it for the
+     duration of this function.  */
+  old = record_btrace_allow_memory_access;
+  record_btrace_allow_memory_access = 1;
+
+  ret = 0;
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    ret = forward_target_insert_breakpoint (ops->beneath, gdbarch, bp_tgt);
+
+  record_btrace_allow_memory_access = old;
+
+  if (except.reason < 0)
+    throw_exception (except);
+
+  return ret;
+}
+
+/* The to_remove_breakpoint method of target record-btrace.  */
+
+static int
+record_btrace_remove_breakpoint (struct target_ops *ops,
+				 struct gdbarch *gdbarch,
+				 struct bp_target_info *bp_tgt)
+{
+  volatile struct gdb_exception except;
+  int old, ret;
+
+  /* Removing breakpoints requires accessing memory.  Allow it for the
+     duration of this function.  */
+  old = record_btrace_allow_memory_access;
+  record_btrace_allow_memory_access = 1;
+
+  ret = 0;
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    ret = forward_target_remove_breakpoint (ops->beneath, gdbarch, bp_tgt);
+
+  record_btrace_allow_memory_access = old;
+
+  if (except.reason < 0)
+    throw_exception (except);
+
+  return ret;
+}
+
 /* The to_fetch_registers method of target record-btrace.  */
 
 static void
@@ -930,6 +1039,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_xfer_partial = record_btrace_xfer_partial;
+  ops->to_remove_breakpoint = record_btrace_remove_breakpoint;
+  ops->to_insert_breakpoint = record_btrace_insert_breakpoint;
   ops->to_fetch_registers = record_btrace_fetch_registers;
   ops->to_store_registers = record_btrace_store_registers;
   ops->to_prepare_to_store = record_btrace_prepare_to_store;
diff --git a/gdb/target.c b/gdb/target.c
index 36e23aa..c3e42f2 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -1406,6 +1406,10 @@ raw_memory_xfer_partial (struct target_ops *ops, void *readbuf,
       if (res > 0)
 	break;
 
+      /* Stop if the target reports that the memory is not available. */
+      if (res == TARGET_XFER_E_UNAVAILABLE)
+	break;
+
       /* We want to continue past core files to executables, but not
 	 past a running target's memory.  */
       if (ops->to_has_all_memory (ops))
-- 
1.8.3.1

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

* [PATCH v10 03/28] btrace: uppercase btrace_read_type
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (6 preceding siblings ...)
  2014-01-14  8:04 ` [PATCH v10 05/28] frame: add frame_id_build_unavailable_stack_special Markus Metzger
@ 2014-01-14  8:04 ` Markus Metzger
  2014-01-14  8:05 ` [PATCH v10 08/28] record-btrace: start counting at one Markus Metzger
                   ` (20 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:04 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

2014-01-14  Markus Metzger  <markus.t.metzger@intel.com>

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


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

diff --git a/gdb/btrace.c b/gdb/btrace.c
index 7f26e1e..df62da8 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -401,7 +401,7 @@ btrace_fetch (struct thread_info *tp)
   if (btinfo->target == NULL)
     return;
 
-  btrace = target_read_btrace (btinfo->target, btrace_read_new);
+  btrace = target_read_btrace (btinfo->target, BTRACE_READ_NEW);
   if (VEC_empty (btrace_block_s, btrace))
     return;
 
diff --git a/gdb/common/btrace-common.h b/gdb/common/btrace-common.h
index c7d8116..1d389af 100644
--- a/gdb/common/btrace-common.h
+++ b/gdb/common/btrace-common.h
@@ -64,10 +64,10 @@ struct btrace_target_info;
 enum btrace_read_type
 {
   /* Send all available trace.  */
-  btrace_read_all,
+  BTRACE_READ_ALL,
 
   /* Send all available trace, if it changed.  */
-  btrace_read_new
+  BTRACE_READ_NEW
 };
 
 #endif /* BTRACE_COMMON_H */
diff --git a/gdb/common/linux-btrace.c b/gdb/common/linux-btrace.c
index 0d36ea5..5474049 100644
--- a/gdb/common/linux-btrace.c
+++ b/gdb/common/linux-btrace.c
@@ -509,7 +509,7 @@ linux_read_btrace (struct btrace_target_info *tinfo,
   unsigned long data_head, retries = 5;
   size_t buffer_size;
 
-  if (type == btrace_read_new && !linux_btrace_has_changed (tinfo))
+  if (type == BTRACE_READ_NEW && !linux_btrace_has_changed (tinfo))
     return NULL;
 
   header = perf_event_header (tinfo);
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index c9d9eec..9ae28f8 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1377,9 +1377,9 @@ handle_qxfer_btrace (const char *annex,
     }
 
   if (strcmp (annex, "all") == 0)
-    type = btrace_read_all;
+    type = BTRACE_READ_ALL;
   else if (strcmp (annex, "new") == 0)
-    type = btrace_read_new;
+    type = BTRACE_READ_NEW;
   else
     {
       strcpy (own_buf, "E.Bad annex.");
diff --git a/gdb/remote.c b/gdb/remote.c
index 6560918..4250404 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -11505,10 +11505,10 @@ remote_read_btrace (struct btrace_target_info *tinfo,
 
   switch (type)
     {
-    case btrace_read_all:
+    case BTRACE_READ_ALL:
       annex = "all";
       break;
-    case btrace_read_new:
+    case BTRACE_READ_NEW:
       annex = "new";
       break;
     default:
-- 
1.8.3.1

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

* [PATCH v10 10/28] record-btrace: optionally indent function call history
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (4 preceding siblings ...)
  2014-01-14  8:04 ` [PATCH v10 26/28] record-btrace: show trace from enable location Markus Metzger
@ 2014-01-14  8:04 ` Markus Metzger
  2014-01-14 16:07   ` Eli Zaretskii
  2014-01-14  8:04 ` [PATCH v10 05/28] frame: add frame_id_build_unavailable_stack_special Markus Metzger
                   ` (22 subsequent siblings)
  28 siblings, 1 reply; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:04 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches, Eli Zaretskii

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.

CC: Eli Zaretskii  <eliz@gnu.org>

2014-01-14  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".
	Add /i modifier to "record function-call-history" example.


---
 gdb/NEWS                                           |   8 +
 gdb/btrace.c                                       |  15 +-
 gdb/doc/gdb.texinfo                                |  13 +-
 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             |  68 +++++
 gdb/testsuite/gdb.btrace/function_call_history.exp |  92 +++++--
 gdb/testsuite/gdb.btrace/tailcall.exp              |  62 +++++
 gdb/testsuite/gdb.btrace/unknown_functions.c       |  45 ++++
 gdb/testsuite/gdb.btrace/unknown_functions.exp     |  60 +++++
 gdb/testsuite/gdb.btrace/x86-tailcall.S            | 279 +++++++++++++++++++++
 gdb/testsuite/gdb.btrace/x86-tailcall.c            |  39 +++
 15 files changed, 740 insertions(+), 40 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 02d541b..8f0e6e6 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -43,6 +43,14 @@
   at one.  This also affects the instruction ranges reported by the
   'record function-call-history' command when given the /i modifier.
 
+* 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 source line range is now prefixed with 'at'.
+  The instruction range is now prefixed with 'inst'.
+  Both ranges are now printed as '<from>, <to>' to allow copy&paste to the
+  "record instruction-history" and "list" commands.
+
 * Python scripting
 
   ** Frame filters and frame decorators have been added.
diff --git a/gdb/btrace.c b/gdb/btrace.c
index 2e9e008..1d060d3e 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -630,8 +630,10 @@ btrace_compute_ftrace (struct btrace_thread_info *btinfo,
 	  if (begin == NULL)
 	    begin = end;
 
-	  /* Maintain the function level offset.  */
-	  level = min (level, end->level);
+	  /* Maintain the function level offset.
+	     For all but the last block, we do it here.  */
+	  if (blk != 0)
+	    level = min (level, end->level);
 
 	  ftrace_update_insns (end, pc);
 	  ftrace_update_lines (end, pc);
@@ -651,6 +653,15 @@ btrace_compute_ftrace (struct btrace_thread_info *btinfo,
 	    }
 
 	  pc += size;
+
+	  /* Maintain the function level offset.
+	     For the last block, we do it here to not consider the last
+	     instruction.
+	     Since the last instruction corresponds to the current instruction
+	     and is not really part of the execution history, it shouldn't
+	     affect the level.  */
+	  if (blk == 0)
+	    level = min (level, end->level);
 	}
     }
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index e4cff84..3e76cca 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6490,7 +6490,10 @@ 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.  The @code{/l}, @code{/i}, and @code{/c} modifiers can be
+given at the same time.
 
 @smallexample
 (@value{GDBP}) @b{list 1, 10}
@@ -6504,10 +6507,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 /ilc}
+1  bar     inst 1,4     at foo.c:6,8
+2    foo   inst 5,10    at foo.c:2,3
+3  bar     inst 11,13   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 116cf1d..7fe7cb2 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 e0df6b1..656e1da 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 ab5ea4b..18263fc 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..606de6e 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 unknown_functions
 
 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..46ed542
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/exception.exp
@@ -0,0 +1,68 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile exception.cc
+if [prepare_for_testing $testfile.exp $testfile $srcfile {c++ debug}] {
+    return -1
+}
+if ![runto_main] {
+    return -1
+}
+
+# we want to see the full trace for this test
+gdb_test_no_output "set record function-call-history-size 0"
+
+# set bp
+set bp_1 [gdb_get_line_number "bp.1" $srcfile]
+set bp_2 [gdb_get_line_number "bp.2" $srcfile]
+gdb_breakpoint $bp_1
+gdb_breakpoint $bp_2
+
+# trace the code between the two breakpoints
+gdb_continue_to_breakpoint "cont to bp.1" ".*$srcfile:$bp_1\r\n.*"
+gdb_test_no_output "record btrace"
+gdb_continue_to_breakpoint "cont to bp.2" ".*$srcfile:$bp_2\r\n.*"
+
+# show the flat branch trace
+send_gdb "record function-call-history 1\n"
+gdb_expect_list "flat" "\r\n$gdb_prompt $" [list \
+  [join [list \
+    "1\ttest\\(\\)" \
+    "2\tfoo\\(\\)" \
+    "3\tbar\\(\\)" \
+    "4\tbad\\(\\)" \
+  ] "\r\n"] \
+  "" \
+  "\[0-9\]*\ttest\\(\\)"]
+
+# show the branch trace with calls indented
+send_gdb "record function-call-history /c 1\n"
+gdb_expect_list "indented" "\r\n$gdb_prompt $" [list \
+  [join [list \
+    "1\ttest\\(\\)" \
+    "2\t  foo\\(\\)" \
+    "3\t    bar\\(\\)" \
+    "4\t      bad\\(\\)\r" \
+    ] "\r\n"] \
+  "" \
+  "\[0-9\]*\ttest\\(\\)"]
diff --git a/gdb/testsuite/gdb.btrace/function_call_history.exp b/gdb/testsuite/gdb.btrace/function_call_history.exp
index cd94dec..f7a9233 100644
--- a/gdb/testsuite/gdb.btrace/function_call_history.exp
+++ b/gdb/testsuite/gdb.btrace/function_call_history.exp
@@ -137,32 +137,37 @@ 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\\." "backward - 4"
 
+# 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 +" [join [list \
-  ".*$srcfile:40-41\tmain" \
-  ".*$srcfile:22-24\tinc" \
-  ".*$srcfile:40-41\tmain" \
-  ".*$srcfile:22-24\tinc" \
-  ".*$srcfile:40-41\tmain" \
-  ".*$srcfile:22-24\tinc" \
-  ".*$srcfile:40-41\tmain" \
-  ".*$srcfile:22-24\tinc" \
-  ".*$srcfile:40-41\tmain" \
-  ".*$srcfile:22-24\tinc" \
-  ".*$srcfile:40-41\tmain" \
-  ".*$srcfile:22-24\tinc" \
-  ".*$srcfile:40-41\tmain" \
-  ".*$srcfile:22-24\tinc" \
-  ".*$srcfile:40-41\tmain"] "\r\n"] "forward /l - 1"
+  "\[0-9\]*\tmain\tat $srcfile:40,41" \
+  "\[0-9\]*\tinc\tat $srcfile:22,24" \
+  "\[0-9\]*\tmain\tat $srcfile:40,41" \
+  "\[0-9\]*\tinc\tat $srcfile:22,24" \
+  "\[0-9\]*\tmain\tat $srcfile:40,41" \
+  "\[0-9\]*\tinc\tat $srcfile:22,24" \
+  "\[0-9\]*\tmain\tat $srcfile:40,41" \
+  "\[0-9\]*\tinc\tat $srcfile:22,24" \
+  "\[0-9\]*\tmain\tat $srcfile:40,41" \
+  "\[0-9\]*\tinc\tat $srcfile:22,24" \
+  "\[0-9\]*\tmain\tat $srcfile:40,41" \
+  "\[0-9\]*\tinc\tat $srcfile:22,24" \
+  "\[0-9\]*\tmain\tat $srcfile:40,41" \
+  "\[0-9\]*\tinc\tat $srcfile:22,24" \
+  "\[0-9\]*\tmain\tat $srcfile:40,41" \
+  ] "\r\n"] "forward /l - 1"
 
 # moving forward and expect to see the latest 6 entries
 gdb_test "record function-call-history /l +" [join [list \
-  ".*$srcfile:22-24\tinc" \
-  ".*$srcfile:40-41\tmain" \
-  ".*$srcfile:22-24\tinc" \
-  ".*$srcfile:40-41\tmain" \
-  ".*$srcfile:22-24\tinc" \
-  ".*$srcfile:40-43\tmain"] "\r\n"] "forward /l - 2"
+  "\[0-9\]*\tinc\tat $srcfile:22,24" \
+  "\[0-9\]*\tmain\tat $srcfile:40,41" \
+  "\[0-9\]*\tinc\tat $srcfile:22,24" \
+  "\[0-9\]*\tmain\tat $srcfile:40,41" \
+  "\[0-9\]*\tinc\tat $srcfile:22,24" \
+  "\[0-9\]*\tmain\tat $srcfile:40,43" \
+  ] "\r\n"] "forward /l - 2"
 
 # moving further forward shouldn't work
 gdb_test "record function-call-history /l +" "At the end of the branch trace record\\." "forward /l - 3"
@@ -202,3 +207,48 @@ gdb_test "record function-call-history" [join [list \
   "29\tfib" \
   "30\tfib" \
   "31\tmain"] "\r\n"] "recursive"
+
+# show indented function call history for fib
+gdb_test "record function-call-history /c 21, +11" [join [list \
+  "21\tmain" \
+  "22\t  fib" \
+  "23\t    fib" \
+  "24\t  fib" \
+  "25\t    fib" \
+  "26\t      fib" \
+  "27\t    fib" \
+  "28\t      fib" \
+  "29\t    fib" \
+  "30\t  fib" \
+  "31\tmain" \
+  ] "\r\n"] "indented"
+
+# 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" [join [list \
+  "1\t      fib" \
+  "2\t    fib" \
+  "3\t      fib" \
+  "4\t    fib" \
+  "5\t  fib" \
+  "6\tmain" \
+  ] "\r\n"] "indented tail"
diff --git a/gdb/testsuite/gdb.btrace/tailcall.exp b/gdb/testsuite/gdb.btrace/tailcall.exp
new file mode 100644
index 0000000..c965675
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/tailcall.exp
@@ -0,0 +1,62 @@
+# 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" [join [list \
+  "1\tfoo" \
+  "2\tbar" \
+  "3\tmain" \
+  ] "\r\n"] "flat"
+
+# show the branch trace with calls indented
+gdb_test "record function-call-history /c 1" [join [list \
+  "1\t  foo" \
+  "2\t    bar" \
+  "3\tmain" \
+  ] "\r\n"] "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..f678358
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/unknown_functions.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
+
+# 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" [join [list \
+  "1\t\\\?\\\?" \
+  "2\t\\\?\\\?" \
+  "3\t\\\?\\\?" \
+  "4\ttest" \
+  "5\tmain" \
+  "6\ttest" \
+  ] "\r\n"] "flat"
+
+# show the branch trace with calls indented
+gdb_test "record function-call-history /c 1" [join [list \
+  "1\t    \\\?\\\?" \
+  "2\t      \\\?\\\?" \
+  "3\t    \\\?\\\?" \
+  "4\t  test" \
+  "5\tmain" \
+  "6\t  test" \
+  ] "\r\n"] "indented"
diff --git a/gdb/testsuite/gdb.btrace/x86-tailcall.S b/gdb/testsuite/gdb.btrace/x86-tailcall.S
new file mode 100644
index 0000000..edf9a3b
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/x86-tailcall.S
@@ -0,0 +1,279 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2013 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <markus.t.metzger@intel.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+   This file has been generated using:
+   gcc -S -O2 -dA -g x86-tailcall.c -o x86-tailcall.S  */
+
+	.file	"x86-tailcall.c"
+	.section	.debug_abbrev,"",@progbits
+.Ldebug_abbrev0:
+	.section	.debug_info,"",@progbits
+.Ldebug_info0:
+	.section	.debug_line,"",@progbits
+.Ldebug_line0:
+	.text
+.Ltext0:
+	.p2align 4,,15
+	.type	bar, @function
+bar:
+.LFB0:
+	.file 1 "x86-tailcall.c"
+	# x86-tailcall.c:22
+	.loc 1 22 0
+	.cfi_startproc
+	# basic block 2
+	# x86-tailcall.c:24
+	.loc 1 24 0
+	movl	$42, %eax
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	bar, .-bar
+	.p2align 4,,15
+	.type	foo, @function
+foo:
+.LFB1:
+	# x86-tailcall.c:28
+	.loc 1 28 0
+	.cfi_startproc
+	# basic block 2
+	# x86-tailcall.c:29
+	.loc 1 29 0
+	jmp	bar
+	.cfi_endproc
+.LFE1:
+	.size	foo, .-foo
+	.p2align 4,,15
+.globl main
+	.type	main, @function
+main:
+.LFB2:
+	# x86-tailcall.c:34
+	.loc 1 34 0
+	.cfi_startproc
+	# basic block 2
+	# x86-tailcall.c:37
+	.loc 1 37 0
+	call	foo
+.LVL0:
+	addl	$1, %eax
+.LVL1:
+	# x86-tailcall.c:39
+	.loc 1 39 0
+	ret
+	.cfi_endproc
+.LFE2:
+	.size	main, .-main
+.Letext0:
+	.section	.debug_loc,"",@progbits
+.Ldebug_loc0:
+.LLST0:
+	.quad	.LVL0-.Ltext0	# Location list begin address (*.LLST0)
+	.quad	.LVL1-.Ltext0	# Location list end address (*.LLST0)
+	.value	0x3	# Location expression size
+	.byte	0x70	# DW_OP_breg0
+	.sleb128 1
+	.byte	0x9f	# DW_OP_stack_value
+	.quad	.LVL1-.Ltext0	# Location list begin address (*.LLST0)
+	.quad	.LFE2-.Ltext0	# Location list end address (*.LLST0)
+	.value	0x1	# Location expression size
+	.byte	0x50	# DW_OP_reg0
+	.quad	0x0	# Location list terminator begin (*.LLST0)
+	.quad	0x0	# Location list terminator end (*.LLST0)
+	.section	.debug_info
+	.long	0x9c	# Length of Compilation Unit Info
+	.value	0x3	# DWARF version number
+	.long	.Ldebug_abbrev0	# Offset Into Abbrev. Section
+	.byte	0x8	# Pointer Size (in bytes)
+	.uleb128 0x1	# (DIE (0xb) DW_TAG_compile_unit)
+	.long	.LASF0	# DW_AT_producer: "GNU C 4.4.4 20100726 (Red Hat 4.4.4-13)"
+	.byte	0x1	# DW_AT_language
+	.long	.LASF1	# DW_AT_name: "x86-tailcall.c"
+	.long	.LASF2	# DW_AT_comp_dir: ""
+	.quad	.Ltext0	# DW_AT_low_pc
+	.quad	.Letext0	# DW_AT_high_pc
+	.long	.Ldebug_line0	# DW_AT_stmt_list
+	.uleb128 0x2	# (DIE (0x2d) DW_TAG_subprogram)
+	.ascii "bar\0"	# DW_AT_name
+	.byte	0x1	# DW_AT_decl_file (x86-tailcall.c)
+	.byte	0x15	# DW_AT_decl_line
+	.byte	0x1	# DW_AT_prototyped
+	.long	0x4b	# DW_AT_type
+	.quad	.LFB0	# DW_AT_low_pc
+	.quad	.LFE0	# DW_AT_high_pc
+	.byte	0x1	# DW_AT_frame_base
+	.byte	0x9c	# DW_OP_call_frame_cfa
+	.uleb128 0x3	# (DIE (0x4b) DW_TAG_base_type)
+	.byte	0x4	# DW_AT_byte_size
+	.byte	0x5	# DW_AT_encoding
+	.ascii "int\0"	# DW_AT_name
+	.uleb128 0x2	# (DIE (0x52) DW_TAG_subprogram)
+	.ascii "foo\0"	# DW_AT_name
+	.byte	0x1	# DW_AT_decl_file (x86-tailcall.c)
+	.byte	0x1b	# DW_AT_decl_line
+	.byte	0x1	# DW_AT_prototyped
+	.long	0x4b	# DW_AT_type
+	.quad	.LFB1	# DW_AT_low_pc
+	.quad	.LFE1	# DW_AT_high_pc
+	.byte	0x1	# DW_AT_frame_base
+	.byte	0x9c	# DW_OP_call_frame_cfa
+	.uleb128 0x4	# (DIE (0x70) DW_TAG_subprogram)
+	.byte	0x1	# DW_AT_external
+	.long	.LASF3	# DW_AT_name: "main"
+	.byte	0x1	# DW_AT_decl_file (x86-tailcall.c)
+	.byte	0x21	# DW_AT_decl_line
+	.byte	0x1	# DW_AT_prototyped
+	.long	0x4b	# DW_AT_type
+	.quad	.LFB2	# DW_AT_low_pc
+	.quad	.LFE2	# DW_AT_high_pc
+	.byte	0x1	# DW_AT_frame_base
+	.byte	0x9c	# DW_OP_call_frame_cfa
+	.uleb128 0x5	# (DIE (0x8f) DW_TAG_variable)
+	.long	.LASF4	# DW_AT_name: "answer"
+	.byte	0x1	# DW_AT_decl_file (x86-tailcall.c)
+	.byte	0x23	# DW_AT_decl_line
+	.long	0x4b	# DW_AT_type
+	.long	.LLST0	# DW_AT_location
+	.byte	0x0	# end of children of DIE 0x70
+	.byte	0x0	# end of children of DIE 0xb
+	.section	.debug_abbrev
+	.uleb128 0x1	# (abbrev code)
+	.uleb128 0x11	# (TAG: DW_TAG_compile_unit)
+	.byte	0x1	# DW_children_yes
+	.uleb128 0x25	# (DW_AT_producer)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x13	# (DW_AT_language)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x1b	# (DW_AT_comp_dir)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x11	# (DW_AT_low_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x12	# (DW_AT_high_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x10	# (DW_AT_stmt_list)
+	.uleb128 0x6	# (DW_FORM_data4)
+	.byte	0x0
+	.byte	0x0
+	.uleb128 0x2	# (abbrev code)
+	.uleb128 0x2e	# (TAG: DW_TAG_subprogram)
+	.byte	0x0	# DW_children_no
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0x8	# (DW_FORM_string)
+	.uleb128 0x3a	# (DW_AT_decl_file)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3b	# (DW_AT_decl_line)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x27	# (DW_AT_prototyped)
+	.uleb128 0xc	# (DW_FORM_flag)
+	.uleb128 0x49	# (DW_AT_type)
+	.uleb128 0x13	# (DW_FORM_ref4)
+	.uleb128 0x11	# (DW_AT_low_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x12	# (DW_AT_high_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x40	# (DW_AT_frame_base)
+	.uleb128 0xa	# (DW_FORM_block1)
+	.byte	0x0
+	.byte	0x0
+	.uleb128 0x3	# (abbrev code)
+	.uleb128 0x24	# (TAG: DW_TAG_base_type)
+	.byte	0x0	# DW_children_no
+	.uleb128 0xb	# (DW_AT_byte_size)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3e	# (DW_AT_encoding)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0x8	# (DW_FORM_string)
+	.byte	0x0
+	.byte	0x0
+	.uleb128 0x4	# (abbrev code)
+	.uleb128 0x2e	# (TAG: DW_TAG_subprogram)
+	.byte	0x1	# DW_children_yes
+	.uleb128 0x3f	# (DW_AT_external)
+	.uleb128 0xc	# (DW_FORM_flag)
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x3a	# (DW_AT_decl_file)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3b	# (DW_AT_decl_line)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x27	# (DW_AT_prototyped)
+	.uleb128 0xc	# (DW_FORM_flag)
+	.uleb128 0x49	# (DW_AT_type)
+	.uleb128 0x13	# (DW_FORM_ref4)
+	.uleb128 0x11	# (DW_AT_low_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x12	# (DW_AT_high_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x40	# (DW_AT_frame_base)
+	.uleb128 0xa	# (DW_FORM_block1)
+	.byte	0x0
+	.byte	0x0
+	.uleb128 0x5	# (abbrev code)
+	.uleb128 0x34	# (TAG: DW_TAG_variable)
+	.byte	0x0	# DW_children_no
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x3a	# (DW_AT_decl_file)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3b	# (DW_AT_decl_line)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x49	# (DW_AT_type)
+	.uleb128 0x13	# (DW_FORM_ref4)
+	.uleb128 0x2	# (DW_AT_location)
+	.uleb128 0x6	# (DW_FORM_data4)
+	.byte	0x0
+	.byte	0x0
+	.byte	0x0
+	.section	.debug_pubnames,"",@progbits
+	.long	0x17	# Length of Public Names Info
+	.value	0x2	# DWARF Version
+	.long	.Ldebug_info0	# Offset of Compilation Unit Info
+	.long	0xa0	# Compilation Unit Length
+	.long	0x70	# DIE offset
+	.ascii "main\0"	# external name
+	.long	0x0
+	.section	.debug_aranges,"",@progbits
+	.long	0x2c	# Length of Address Ranges Info
+	.value	0x2	# DWARF Version
+	.long	.Ldebug_info0	# Offset of Compilation Unit Info
+	.byte	0x8	# Size of Address
+	.byte	0x0	# Size of Segment Descriptor
+	.value	0x0	# Pad to 16 byte boundary
+	.value	0x0
+	.quad	.Ltext0	# Address
+	.quad	.Letext0-.Ltext0	# Length
+	.quad	0x0
+	.quad	0x0
+	.section	.debug_str,"MS",@progbits,1
+.LASF0:
+	.string	"GNU C 4.4.4 20100726 (Red Hat 4.4.4-13)"
+.LASF3:
+	.string	"main"
+.LASF4:
+	.string	"answer"
+.LASF2:
+	.string	""
+.LASF1:
+	.string	"x86-tailcall.c"
+	.ident	"GCC: (GNU) 4.4.4 20100726 (Red Hat 4.4.4-13)"
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gdb/testsuite/gdb.btrace/x86-tailcall.c b/gdb/testsuite/gdb.btrace/x86-tailcall.c
new file mode 100644
index 0000000..9e3b183
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/x86-tailcall.c
@@ -0,0 +1,39 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2013 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <markus.t.metzger@intel.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+static __attribute__ ((noinline)) int
+bar (void)
+{
+  return 42;
+}
+
+static __attribute__ ((noinline)) int
+foo (void)
+{
+  return bar ();
+}
+
+int
+main (void)
+{
+  int answer;
+
+  answer = foo ();
+  return ++answer;
+}
-- 
1.8.3.1

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

* [PATCH v10 18/28] record-btrace, frame: supply target-specific unwinder
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
  2014-01-14  8:04 ` [PATCH v10 01/28] btrace, test: fix multi-line btrace tests Markus Metzger
  2014-01-14  8:04 ` [PATCH v10 14/28] record-btrace: supply register target methods Markus Metzger
@ 2014-01-14  8:04 ` Markus Metzger
  2014-01-14  8:04 ` [PATCH v10 20/28] record-btrace: provide xfer_partial target method Markus Metzger
                   ` (25 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:04 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

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

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

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


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

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

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

* [PATCH v10 01/28] btrace, test: fix multi-line btrace tests
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
@ 2014-01-14  8:04 ` Markus Metzger
  2014-01-14  8:04 ` [PATCH v10 14/28] record-btrace: supply register target methods Markus Metzger
                   ` (27 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:04 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

For testing multi-line test output, gdb.btrace tests used the following
pattern:

  gdb_test "..." "
  ...\r
  ..."

Change this to:

  gdb_test "..." [join [list \
    "..." \
    "..."] "\r\n"]

Also extract repeated tests into a test function and shorten or remove
test messages.

Reviewed-by:  Pedro Alves

2014-01-14  Markus Metzger <markus.t.metzger@intel.com>

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


---
 gdb/testsuite/gdb.btrace/function_call_history.exp | 247 ++++++++++-----------
 gdb/testsuite/gdb.btrace/instruction_history.exp   |  57 ++---
 2 files changed, 145 insertions(+), 159 deletions(-)

diff --git a/gdb/testsuite/gdb.btrace/function_call_history.exp b/gdb/testsuite/gdb.btrace/function_call_history.exp
index 5dd874a..7ee4e66 100644
--- a/gdb/testsuite/gdb.btrace/function_call_history.exp
+++ b/gdb/testsuite/gdb.btrace/function_call_history.exp
@@ -37,167 +37,150 @@ set bp_location [gdb_get_line_number "bp.1" $testfile.c]
 gdb_breakpoint $bp_location
 gdb_continue_to_breakpoint "cont to $bp_location" ".*$testfile.c:$bp_location.*"
 
+proc rec_fun_all {} {
+  gdb_test "record function-call-history 0" [join [list \
+    "0\tmain" \
+    "1\tinc" \
+    "2\tmain" \
+    "3\tinc" \
+    "4\tmain" \
+    "5\tinc" \
+    "6\tmain" \
+    "7\tinc" \
+    "8\tmain" \
+    "9\tinc" \
+    "10\tmain" \
+    "11\tinc" \
+    "12\tmain" \
+    "13\tinc" \
+    "14\tmain" \
+    "15\tinc" \
+    "16\tmain" \
+    "17\tinc" \
+    "18\tmain" \
+    "19\tinc" \
+    "20\tmain"] "\r\n"]
+}
+
 # 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"
+with_test_prefix "size unlimited" rec_fun_all
 
 # 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"
+with_test_prefix "size 21" rec_fun_all
 
 # 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 0" [join [list \
+  "0\tmain" \
+  "1\tinc" \
+  "2\tmain" \
+  "3\tinc" \
+  "4\tmain" \
+  "5\tinc" \
+  "6\tmain" \
+  "7\tinc" \
+  "8\tmain" \
+  "9\tinc" \
+  "10\tmain" \
+  "11\tinc" \
+  "12\tmain" \
+  "13\tinc" \
+  "14\tmain"] "\r\n"] "forward - 1"
 
 # 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"
+gdb_test "record function-call-history +" [join [list \
+  "15\tinc" \
+  "16\tmain" \
+  "17\tinc" \
+  "18\tmain" \
+  "19\tinc" \
+  "20\tmain"] "\r\n"] "forward - 2"
 
 # 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)"
+gdb_test "record function-call-history +" "At the end of the branch trace record\\." "forward - 3"
 
 # make sure we cannot move any further a second time
-gdb_test "record function-call-history +" "At the end of the branch trace record\\." "record function-call-history - at the end (2)"
+gdb_test "record function-call-history +" "At the end of the branch trace record\\." "forward - 4"
 
 # 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"
+gdb_test "record function-call-history -" [join [list \
+  "6\tmain" \
+  "7\tinc" \
+  "8\tmain" \
+  "9\tinc" \
+  "10\tmain" \
+  "11\tinc" \
+  "12\tmain" \
+  "13\tinc" \
+  "14\tmain" \
+  "15\tinc" \
+  "16\tmain" \
+  "17\tinc" \
+  "18\tmain" \
+  "19\tinc" \
+  "20\tmain"] "\r\n"] "backward - 1"
 
 # 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"
+gdb_test "record function-call-history -" [join [list \
+  "0\tmain" \
+  "1\tinc" \
+  "2\tmain" \
+  "3\tinc" \
+  "4\tmain" \
+  "5\tinc"] "\r\n"] "backward - 2"
 
 # 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)"
+gdb_test "record function-call-history -" "At the start of the branch trace record\\." "backward - 3"
 
 # 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)"
+gdb_test "record function-call-history -" "At the start of the branch trace record\\." "backward - 4"
 
 # 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"
+gdb_test "record function-call-history /l +" [join [list \
+  ".*$srcfile:40-41\tmain" \
+  ".*$srcfile:22-24\tinc" \
+  ".*$srcfile:40-41\tmain" \
+  ".*$srcfile:22-24\tinc" \
+  ".*$srcfile:40-41\tmain" \
+  ".*$srcfile:22-24\tinc" \
+  ".*$srcfile:40-41\tmain" \
+  ".*$srcfile:22-24\tinc" \
+  ".*$srcfile:40-41\tmain" \
+  ".*$srcfile:22-24\tinc" \
+  ".*$srcfile:40-41\tmain" \
+  ".*$srcfile:22-24\tinc" \
+  ".*$srcfile:40-41\tmain" \
+  ".*$srcfile:22-24\tinc" \
+  ".*$srcfile:40-41\tmain"] "\r\n"] "forward /l - 1"
 
 # 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"
+gdb_test "record function-call-history /l +" [join [list \
+  ".*$srcfile:22-24\tinc" \
+  ".*$srcfile:40-41\tmain" \
+  ".*$srcfile:22-24\tinc" \
+  ".*$srcfile:40-41\tmain" \
+  ".*$srcfile:22-24\tinc" \
+  ".*$srcfile:40-43\tmain"] "\r\n"] "forward /l - 2"
 
 # 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)"
-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"
+gdb_test "record function-call-history /l +" "At the end of the branch trace record\\." "forward /l - 3"
+gdb_test "record function-call-history /l" "At the end of the branch trace record\\." "forward /l - 4"
+
+set expected_range [join [list \
+  "3\tinc" \
+  "4\tmain" \
+  "5\tinc" \
+  "6\tmain" \
+  "7\tinc" \
+  "8\tmain" \
+  "9\tinc"] "\r\n"]
 
 # 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 3,10" $expected_range
+gdb_test "record function-call-history 3,+7" $expected_range
+gdb_test "record function-call-history 10,-7" $expected_range
 
 # set bp after fib recursion and continue
 set bp_location [gdb_get_line_number "bp.2" $testfile.c]
@@ -207,7 +190,7 @@ 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"
+set message "recursive"
 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
diff --git a/gdb/testsuite/gdb.btrace/instruction_history.exp b/gdb/testsuite/gdb.btrace/instruction_history.exp
index 695c1a0..6048ba1 100644
--- a/gdb/testsuite/gdb.btrace/instruction_history.exp
+++ b/gdb/testsuite/gdb.btrace/instruction_history.exp
@@ -65,33 +65,36 @@ if { $traced != 7 } {
 }
 
 # 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 1,6" [join [list \
+  "1\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "2\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax" \
+  "3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "4\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
+  "5\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>"] "\r\n"]
+
+gdb_test "record instruction-history /f 1,+5" [join [list \
+  "1\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "2\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax" \
+  "3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
+  "5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
+] "\r\n"]
+
+gdb_test "record instruction-history /p 6,-5" [join [list \
+  "1\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "2\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax" \
+  "3\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "4\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
+  "5\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
+] "\r\n"]
+
+gdb_test "record instruction-history /pf 1,6" [join [list \
+  "1\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "2\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax" \
+  "3\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "4\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
+  "5\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
+] "\r\n"]
 
 # the following tests are checking the iterators
 # to avoid lots of regexps, we just check the number of lines that
-- 
1.8.3.1

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

* [PATCH v10 09/28] btrace: increase buffer size
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (19 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 06/28] btrace: change branch trace data structure Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-14  8:05 ` [PATCH v10 21/28] record-btrace: add to_wait and to_resume target methods Markus Metzger
                   ` (7 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

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

2014-01-14  Markus Metzger  <markus.t.metzger@intel.com>

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


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

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

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

* [PATCH v10 08/28] record-btrace: start counting at one
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (7 preceding siblings ...)
  2014-01-14  8:04 ` [PATCH v10 03/28] btrace: uppercase btrace_read_type Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-14  8:05 ` [PATCH v10 25/28] btrace, gdbserver: read branch trace incrementally Markus Metzger
                   ` (19 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +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.

Reviewed-by: Eli Zaretskii

2014-01-14  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/NEWS                                           |   4 +
 gdb/btrace.c                                       |   8 +-
 gdb/record-btrace.c                                |   4 +-
 gdb/testsuite/gdb.btrace/function_call_history.exp | 156 ++++++++++-----------
 gdb/testsuite/gdb.btrace/instruction_history.exp   |  67 ++++-----
 5 files changed, 125 insertions(+), 114 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 00defe1..02d541b 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -39,6 +39,10 @@
 * New script contrib/gdb-add-index.sh for adding .gdb_index sections
   to binaries.
 
+* The 'record instruction-history' command now starts counting instructions
+  at one.  This also affects the instruction ranges reported by the
+  'record function-call-history' command when given the /i modifier.
+
 * Python scripting
 
   ** Frame filters and frame decorators have been added.
diff --git a/gdb/btrace.c b/gdb/btrace.c
index 5bda127..2e9e008 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 875b138..116cf1d 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 bf2458b..cd94dec 100644
--- a/gdb/testsuite/gdb.btrace/function_call_history.exp
+++ b/gdb/testsuite/gdb.btrace/function_call_history.exp
@@ -38,28 +38,28 @@ gdb_breakpoint $bp_location
 gdb_continue_to_breakpoint "cont to $bp_location" ".*$testfile.c:$bp_location.*"
 
 proc rec_fun_all {} {
-  gdb_test "record function-call-history 0" [join [list \
-    "0\tmain" \
-    "1\tinc" \
-    "2\tmain" \
-    "3\tinc" \
-    "4\tmain" \
-    "5\tinc" \
-    "6\tmain" \
-    "7\tinc" \
-    "8\tmain" \
-    "9\tinc" \
-    "10\tmain" \
-    "11\tinc" \
-    "12\tmain" \
-    "13\tinc" \
-    "14\tmain" \
-    "15\tinc" \
-    "16\tmain" \
-    "17\tinc" \
-    "18\tmain" \
-    "19\tinc" \
-    "20\tmain"] "\r\n"]
+  gdb_test "record function-call-history 1" [join [list \
+    "1\tmain" \
+    "2\tinc" \
+    "3\tmain" \
+    "4\tinc" \
+    "5\tmain" \
+    "6\tinc" \
+    "7\tmain" \
+    "8\tinc" \
+    "9\tmain" \
+    "10\tinc" \
+    "11\tmain" \
+    "12\tinc" \
+    "13\tmain" \
+    "14\tinc" \
+    "15\tmain" \
+    "16\tinc" \
+    "17\tmain" \
+    "18\tinc" \
+    "19\tmain" \
+    "20\tinc" \
+    "21\tmain"] "\r\n"]
 }
 
 # show function call history with unlimited size, we expect to see all 21 entries
@@ -72,31 +72,31 @@ with_test_prefix "size 21" rec_fun_all
 
 # show first 15 entries
 gdb_test_no_output "set record function-call-history-size 15"
-gdb_test "record function-call-history 0" [join [list \
-  "0\tmain" \
-  "1\tinc" \
-  "2\tmain" \
-  "3\tinc" \
-  "4\tmain" \
-  "5\tinc" \
-  "6\tmain" \
-  "7\tinc" \
-  "8\tmain" \
-  "9\tinc" \
-  "10\tmain" \
-  "11\tinc" \
-  "12\tmain" \
-  "13\tinc" \
-  "14\tmain"] "\r\n"] "forward - 1"
+gdb_test "record function-call-history 1" [join [list \
+  "1\tmain" \
+  "2\tinc" \
+  "3\tmain" \
+  "4\tinc" \
+  "5\tmain" \
+  "6\tinc" \
+  "7\tmain" \
+  "8\tinc" \
+  "9\tmain" \
+  "10\tinc" \
+  "11\tmain" \
+  "12\tinc" \
+  "13\tmain" \
+  "14\tinc" \
+  "15\tmain"] "\r\n"] "forward - 1"
 
 # show last 6 entries
 gdb_test "record function-call-history +" [join [list \
-  "15\tinc" \
-  "16\tmain" \
-  "17\tinc" \
-  "18\tmain" \
-  "19\tinc" \
-  "20\tmain"] "\r\n"] "forward - 2"
+  "16\tinc" \
+  "17\tmain" \
+  "18\tinc" \
+  "19\tmain" \
+  "20\tinc" \
+  "21\tmain"] "\r\n"] "forward - 2"
 
 # moving further should not work
 gdb_test "record function-call-history +" "At the end of the branch trace record\\." "forward - 3"
@@ -106,30 +106,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 -" [join [list \
-  "6\tmain" \
-  "7\tinc" \
-  "8\tmain" \
-  "9\tinc" \
-  "10\tmain" \
-  "11\tinc" \
-  "12\tmain" \
-  "13\tinc" \
-  "14\tmain" \
-  "15\tinc" \
-  "16\tmain" \
-  "17\tinc" \
-  "18\tmain" \
-  "19\tinc" \
-  "20\tmain"] "\r\n"] "backward - 1"
+  "7\tmain" \
+  "8\tinc" \
+  "9\tmain" \
+  "10\tinc" \
+  "11\tmain" \
+  "12\tinc" \
+  "13\tmain" \
+  "14\tinc" \
+  "15\tmain" \
+  "16\tinc" \
+  "17\tmain" \
+  "18\tinc" \
+  "19\tmain" \
+  "20\tinc" \
+  "21\tmain"] "\r\n"] "backward - 1"
 
 # moving further back shows the 6 first function calls
 gdb_test "record function-call-history -" [join [list \
-  "0\tmain" \
-  "1\tinc" \
-  "2\tmain" \
-  "3\tinc" \
-  "4\tmain" \
-  "5\tinc"] "\r\n"] "backward - 2"
+  "1\tmain" \
+  "2\tinc" \
+  "3\tmain" \
+  "4\tinc" \
+  "5\tmain" \
+  "6\tinc"] "\r\n"] "backward - 2"
 
 # moving further back shouldn't work
 gdb_test "record function-call-history -" "At the start of the branch trace record\\." "backward - 3"
@@ -169,18 +169,18 @@ gdb_test "record function-call-history /l +" "At the end of the branch trace rec
 gdb_test "record function-call-history /l" "At the end of the branch trace record\\." "forward /l - 4"
 
 set expected_range [join [list \
-  "3\tinc" \
-  "4\tmain" \
-  "5\tinc" \
-  "6\tmain" \
-  "7\tinc" \
-  "8\tmain" \
-  "9\tinc"] "\r\n"]
+  "4\tinc" \
+  "5\tmain" \
+  "6\tinc" \
+  "7\tmain" \
+  "8\tinc" \
+  "9\tmain" \
+  "10\tinc"] "\r\n"]
 
 # show functions in instruction range
-gdb_test "record function-call-history 3,10" $expected_range
-gdb_test "record function-call-history 3,+7" $expected_range
-gdb_test "record function-call-history 10,-7" $expected_range
+gdb_test "record function-call-history 4,11" $expected_range
+gdb_test "record function-call-history 4,+7" $expected_range
+gdb_test "record function-call-history 11,-7" $expected_range
 
 # set bp after fib recursion and continue
 set bp_location [gdb_get_line_number "bp.2" $testfile.c]
@@ -191,8 +191,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" [join [list \
-  "20\tmain" \
-  "21\tfib" \
+  "21\tmain" \
   "22\tfib" \
   "23\tfib" \
   "24\tfib" \
@@ -201,4 +200,5 @@ gdb_test "record function-call-history" [join [list \
   "27\tfib" \
   "28\tfib" \
   "29\tfib" \
-  "30\tmain"] "\r\n"] "recursive"
+  "30\tfib" \
+  "31\tmain"] "\r\n"] "recursive"
diff --git a/gdb/testsuite/gdb.btrace/instruction_history.exp b/gdb/testsuite/gdb.btrace/instruction_history.exp
index c6f6500..46ef65e 100644
--- a/gdb/testsuite/gdb.btrace/instruction_history.exp
+++ b/gdb/testsuite/gdb.btrace/instruction_history.exp
@@ -65,36 +65,37 @@ if { $traced != 6 } {
 }
 
 # test that we see the expected instructions
-gdb_test "record instruction-history 1,6" [join [list \
-  "1\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
-  "2\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax" \
-  "3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
-  "4\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
-  "5\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>"] "\r\n"]
-
-gdb_test "record instruction-history /f 1,+5" [join [list \
-  "1\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
-  "2\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax" \
-  "3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
-  "4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
-  "5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
-] "\r\n"]
-
-gdb_test "record instruction-history /p 6,-5" [join [list \
-  "1\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
-  "2\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax" \
-  "3\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
-  "4\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
-  "5\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
-] "\r\n"]
-
-gdb_test "record instruction-history /pf 1,6" [join [list \
-  "1\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
-  "2\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax" \
-  "3\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
-  "4\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
-  "5\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
-] "\r\n"]
+gdb_test "record instruction-history 2,7" [join [list \
+  "2\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax" \
+  "4\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "5\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
+  "6\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
+  ] "\r\n"]
+
+gdb_test "record instruction-history /f 2,+5" [join [list \
+  "2\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax" \
+  "4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
+  "6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
+  ] "\r\n"]
+
+gdb_test "record instruction-history /p 7,-5" [join [list \
+  "2\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "3\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax" \
+  "4\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "5\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
+  "6\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
+  ] "\r\n"]
+
+gdb_test "record instruction-history /pf 2,7" [join [list \
+  "2\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "3\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax" \
+  "4\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
+  "5\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
+  "6\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
+  ] "\r\n"]
 
 # the following tests are checking the iterators
 # to avoid lots of regexps, we just check the number of lines that
@@ -120,7 +121,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 {
@@ -129,7 +130,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 {
@@ -140,7 +141,7 @@ if { $traced != $lines } {
 set history_size 3
 gdb_test_no_output "set record instruction-history-size $history_size"
 set message "browse history forward start"
-set lines [test_lines_length "record instruction-history 0" $message]
+set lines [test_lines_length "record instruction-history 1" $message]
 if { $lines != $history_size } {
     fail $message
 } else {
-- 
1.8.3.1

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

* [PATCH v10 25/28] btrace, gdbserver: read branch trace incrementally
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (8 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 08/28] record-btrace: start counting at one Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-16 17:57   ` Tom Tromey
  2014-01-14  8:05 ` [PATCH v10 16/28] frame, cfa: check unwind stop reason first Markus Metzger
                   ` (18 subsequent siblings)
  28 siblings, 1 reply; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

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

2014-01-14  Markus Metzger  <markus.t.metzger@intel.com>

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

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


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

diff --git a/gdb/NEWS b/gdb/NEWS
index 506920e..440f04b 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -261,6 +261,10 @@ qXfer:libraries-svr4:read's annex
   necessary for library list updating, resulting in significant
   speedup.
 
+qXfer:btrace:read's annex
+  The qXfer:btrace:read packet supports a new annex 'delta' to read
+  branch trace incrementally.
+
 * New features in the GDB remote stub, GDBserver
 
   ** GDBserver now supports target-assisted range stepping.  Currently
diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c
index 7c967f8..55973a6 100644
--- a/gdb/amd64-linux-nat.c
+++ b/gdb/amd64-linux-nat.c
@@ -1173,10 +1173,10 @@ amd64_linux_enable_btrace (ptid_t ptid)
 static void
 amd64_linux_disable_btrace (struct btrace_target_info *tinfo)
 {
-  int errcode = linux_disable_btrace (tinfo);
+  enum btrace_error errcode = linux_disable_btrace (tinfo);
 
-  if (errcode != 0)
-    error (_("Could not disable branch tracing: %s."), safe_strerror (errcode));
+  if (errcode != BTRACE_ERR_NONE)
+    error (_("Could not disable branch tracing."));
 }
 
 /* Teardown branch tracing.  */
diff --git a/gdb/btrace.c b/gdb/btrace.c
index ba87e16..28970c3 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)
@@ -728,27 +728,158 @@ btrace_teardown (struct thread_info *tp)
   btrace_clear (tp);
 }
 
+/* Adjust the block trace in order to stitch old and new trace together.
+   BTRACE is the new delta trace between the last and the current stop.
+   BTINFO is the old branch trace until the last stop.
+   May modify BTRACE as well as the existing trace in BTINFO.
+   Return 0 on success, -1 otherwise.  */
+
+static int
+btrace_stitch_trace (VEC (btrace_block_s) **btrace,
+		     const struct btrace_thread_info *btinfo)
+{
+  struct btrace_function *last_bfun;
+  struct btrace_insn *last_insn;
+  btrace_block_s *first_new_block;
+
+  /* If we don't have trace, there's nothing to do.  */
+  if (VEC_empty (btrace_block_s, *btrace))
+    return 0;
+
+  last_bfun = btinfo->end;
+  gdb_assert (last_bfun != NULL);
+
+  /* Beware that block trace starts with the most recent block, so the
+     chronologically first block in the new trace is the last block in
+     the new trace's block vector.  */
+  first_new_block = VEC_last (btrace_block_s, *btrace);
+  last_insn = VEC_last (btrace_insn_s, last_bfun->insn);
+
+  /* If the current PC at the end of the block is the same as in our current
+     trace, there are two explanations:
+       1. we executed the instruction and some branch brought us back.
+       2. we have not made any progress.
+     In the first case, the delta trace vector should contain at least two
+     entries.
+     In the second case, the delta trace vector should contain exactly one
+     entry for the partial block containing the current PC.  Remove it.  */
+  if (first_new_block->end == last_insn->pc
+      && VEC_length (btrace_block_s, *btrace) == 1)
+    {
+      VEC_pop (btrace_block_s, *btrace);
+      return 0;
+    }
+
+  DEBUG ("stitching %s to %s", ftrace_print_insn_addr (last_insn),
+	 core_addr_to_string_nz (first_new_block->end));
+
+  /* Do a simple sanity check to make sure we don't accidentally end up
+     with a bad block.  This should not occur in practice.  */
+  if (first_new_block->end < last_insn->pc)
+    {
+      warning (_("Error while trying to read delta trace.  Falling back to "
+		 "a full read."));
+      return -1;
+    }
+
+  /* We adjust the last block to start at the end of our current trace.  */
+  gdb_assert (first_new_block->begin == 0);
+  first_new_block->begin = last_insn->pc;
+
+  /* We simply pop the last insn so we can insert it again as part of
+     the normal branch trace computation.
+     Since instruction iterators are based on indices in the instructions
+     vector, we don't leave any pointers dangling.  */
+  DEBUG ("pruning insn at %s for stitching",
+	 ftrace_print_insn_addr (last_insn));
+
+  VEC_pop (btrace_insn_s, last_bfun->insn);
+
+  /* The instructions vector may become empty temporarily if this has
+     been the only instruction in this function segment.
+     This violates the invariant but will be remedied shortly by
+     btrace_compute_ftrace when we add the new trace.  */
+  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);
     }
 
@@ -783,13 +914,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.  */
@@ -881,10 +1006,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 1d389af..25617bb 100644
--- a/gdb/common/btrace-common.h
+++ b/gdb/common/btrace-common.h
@@ -42,7 +42,9 @@
    asynchronous, e.g. interrupts.  */
 struct btrace_block
 {
-  /* The address of the first byte of the first instruction in the block.  */
+  /* The address of the first byte of the first instruction in the block.
+     The address may be zero if we do not know the beginning of this block,
+     such as for the first block in a delta trace.  */
   CORE_ADDR begin;
 
   /* The address of the first byte of the last instruction in the block.  */
@@ -67,7 +69,28 @@ enum btrace_read_type
   BTRACE_READ_ALL,
 
   /* Send all available trace, if it changed.  */
-  BTRACE_READ_NEW
+  BTRACE_READ_NEW,
+
+  /* Send the trace since the last request.  This will fail if the trace
+     buffer overflowed.  */
+  BTRACE_READ_DELTA
+};
+
+/* Enumeration of btrace errors.  */
+
+enum btrace_error
+{
+  /* No error.  Everything is OK.  */
+  BTRACE_ERR_NONE,
+
+  /* An unknown error.  */
+  BTRACE_ERR_UNKNOWN,
+
+  /* Branch tracing is not supported on this system.  */
+  BTRACE_ERR_NOT_SUPPORTED,
+
+  /* The branch trace buffer overflowed; no delta read possible.  */
+  BTRACE_ERR_OVERFLOW
 };
 
 #endif /* BTRACE_COMMON_H */
diff --git a/gdb/common/linux-btrace.c b/gdb/common/linux-btrace.c
index e469900..218e0ce 100644
--- a/gdb/common/linux-btrace.c
+++ b/gdb/common/linux-btrace.c
@@ -172,11 +172,11 @@ perf_event_sample_ok (const struct perf_event_sample *sample)
 
 static VEC (btrace_block_s) *
 perf_event_read_bts (struct btrace_target_info* tinfo, const uint8_t *begin,
-		     const uint8_t *end, const uint8_t *start)
+		     const uint8_t *end, const uint8_t *start, size_t size)
 {
   VEC (btrace_block_s) *btrace = NULL;
   struct perf_event_sample sample;
-  size_t read = 0, size = (end - begin);
+  size_t read = 0;
   struct btrace_block block = { 0, 0 };
   struct regcache *regcache;
 
@@ -252,6 +252,13 @@ perf_event_read_bts (struct btrace_target_info* tinfo, const uint8_t *begin,
       block.end = psample->bts.from;
     }
 
+  /* Push the last block (i.e. the first one of inferior execution), as well.
+     We don't know where it ends, but we know where it starts.  If we're
+     reading delta trace, we can fill in the start address later on.
+     Otherwise we will prune it.  */
+  block.begin = 0;
+  VEC_safe_push (btrace_block_s, btrace, &block);
+
   return btrace;
 }
 
@@ -476,7 +483,7 @@ linux_enable_btrace (ptid_t ptid)
 
 /* See linux-btrace.h.  */
 
-int
+enum btrace_error
 linux_disable_btrace (struct btrace_target_info *tinfo)
 {
   int errcode;
@@ -484,12 +491,12 @@ linux_disable_btrace (struct btrace_target_info *tinfo)
   errno = 0;
   errcode = munmap (tinfo->buffer, perf_event_mmap_size (tinfo));
   if (errcode != 0)
-    return errno;
+    return BTRACE_ERR_UNKNOWN;
 
   close (tinfo->file);
   xfree (tinfo);
 
-  return 0;
+  return BTRACE_ERR_NONE;
 }
 
 /* Check whether the branch trace has changed.  */
@@ -504,21 +511,24 @@ linux_btrace_has_changed (struct btrace_target_info *tinfo)
 
 /* See linux-btrace.h.  */
 
-VEC (btrace_block_s) *
-linux_read_btrace (struct btrace_target_info *tinfo,
+enum btrace_error
+linux_read_btrace (VEC (btrace_block_s) **btrace,
+		   struct btrace_target_info *tinfo,
 		   enum btrace_read_type type)
 {
-  VEC (btrace_block_s) *btrace = NULL;
   volatile struct perf_event_mmap_page *header;
   const uint8_t *begin, *end, *start;
-  unsigned long data_head, retries = 5;
-  size_t buffer_size;
+  unsigned long data_head, data_tail, retries = 5;
+  size_t buffer_size, size;
 
+  /* For delta reads, we return at least the partial last block containing
+     the current PC.  */
   if (type == BTRACE_READ_NEW && !linux_btrace_has_changed (tinfo))
-    return NULL;
+    return BTRACE_ERR_NONE;
 
   header = perf_event_header (tinfo);
   buffer_size = perf_event_buffer_size (tinfo);
+  data_tail = tinfo->data_head;
 
   /* We may need to retry reading the trace.  See below.  */
   while (retries--)
@@ -526,23 +536,45 @@ linux_read_btrace (struct btrace_target_info *tinfo,
       data_head = header->data_head;
 
       /* Delete any leftover trace from the previous iteration.  */
-      VEC_free (btrace_block_s, btrace);
+      VEC_free (btrace_block_s, *btrace);
 
-      /* If there's new trace, let's read it.  */
-      if (data_head != tinfo->data_head)
+      if (type == BTRACE_READ_DELTA)
 	{
-	  /* Data_head keeps growing; the buffer itself is circular.  */
-	  begin = perf_event_buffer_begin (tinfo);
-	  start = begin + data_head % buffer_size;
-
-	  if (data_head <= buffer_size)
-	    end = start;
-	  else
-	    end = perf_event_buffer_end (tinfo);
+	  /* Determine the number of bytes to read and check for buffer
+	     overflows.  */
+
+	  /* Check for data head overflows.  We might be able to recover from
+	     those but they are very unlikely and it's not really worth the
+	     effort, I think.  */
+	  if (data_head < data_tail)
+	    return BTRACE_ERR_OVERFLOW;
+
+	  /* If the buffer is smaller than the trace delta, we overflowed.  */
+	  size = data_head - data_tail;
+	  if (buffer_size < size)
+	    return BTRACE_ERR_OVERFLOW;
+	}
+      else
+	{
+	  /* Read the entire buffer.  */
+	  size = buffer_size;
 
-	  btrace = perf_event_read_bts (tinfo, begin, end, start);
+	  /* Adjust the size if the buffer has not overflowed, yet.  */
+	  if (data_head < size)
+	    size = data_head;
 	}
 
+      /* Data_head keeps growing; the buffer itself is circular.  */
+      begin = perf_event_buffer_begin (tinfo);
+      start = begin + data_head % buffer_size;
+
+      if (data_head <= buffer_size)
+	end = start;
+      else
+	end = perf_event_buffer_end (tinfo);
+
+      *btrace = perf_event_read_bts (tinfo, begin, end, start, size);
+
       /* The stopping thread notifies its ptracer before it is scheduled out.
 	 On multi-core systems, the debugger might therefore run while the
 	 kernel might be writing the last branch trace records.
@@ -554,7 +586,13 @@ linux_read_btrace (struct btrace_target_info *tinfo,
 
   tinfo->data_head = data_head;
 
-  return btrace;
+  /* Prune the incomplete last block (i.e. the first one of inferior execution)
+     if we're not doing a delta read.  There is no way of filling in its zeroed
+     BEGIN element.  */
+  if (!VEC_empty (btrace_block_s, *btrace) && type != BTRACE_READ_DELTA)
+    VEC_pop (btrace_block_s, *btrace);
+
+  return BTRACE_ERR_NONE;
 }
 
 #else /* !HAVE_LINUX_PERF_EVENT_H */
@@ -577,19 +615,20 @@ linux_enable_btrace (ptid_t ptid)
 
 /* See linux-btrace.h.  */
 
-int
+enum btrace_error
 linux_disable_btrace (struct btrace_target_info *tinfo)
 {
-  return ENOSYS;
+  return BTRACE_ERR_NOT_SUPPORTED;
 }
 
 /* See linux-btrace.h.  */
 
-VEC (btrace_block_s) *
-linux_read_btrace (struct btrace_target_info *tinfo,
+enum btrace_error
+linux_read_btrace (VEC (btrace_block_s) **btrace,
+		   struct btrace_target_info *tinfo,
 		   enum btrace_read_type type)
 {
-  return NULL;
+  return BTRACE_ERR_NOT_SUPPORTED;
 }
 
 #endif /* !HAVE_LINUX_PERF_EVENT_H */
diff --git a/gdb/common/linux-btrace.h b/gdb/common/linux-btrace.h
index 32a0403..a97b697 100644
--- a/gdb/common/linux-btrace.h
+++ b/gdb/common/linux-btrace.h
@@ -61,17 +61,18 @@ struct btrace_target_info
   int ptr_bits;
 };
 
-/* Check whether branch tracing is supported.  */
+/* See to_supports_btrace in target.h.  */
 extern int linux_supports_btrace (void);
 
-/* Enable branch tracing for @ptid.  */
+/* See to_enable_btrace in target.h.  */
 extern struct btrace_target_info *linux_enable_btrace (ptid_t ptid);
 
-/* Disable branch tracing and deallocate @tinfo.  */
-extern int linux_disable_btrace (struct btrace_target_info *tinfo);
+/* See to_disable_btrace in target.h.  */
+extern enum btrace_error linux_disable_btrace (struct btrace_target_info *ti);
 
-/* Read branch trace data.  */
-extern VEC (btrace_block_s) *linux_read_btrace (struct btrace_target_info *,
-						enum btrace_read_type);
+/* See to_read_btrace in target.h.  */
+extern enum btrace_error linux_read_btrace (VEC (btrace_block_s) **btrace,
+					    struct btrace_target_info *btinfo,
+					    enum btrace_read_type type);
 
 #endif /* LINUX_BTRACE_H */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 4367028..3ee7538 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -39978,6 +39978,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 2bc619a..01d0e84 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -5705,7 +5705,7 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
 
 #ifdef HAVE_LINUX_BTRACE
 
-/* Enable branch tracing.  */
+/* See to_enable_btrace target method.  */
 
 static struct btrace_target_info *
 linux_low_enable_btrace (ptid_t ptid)
@@ -5725,17 +5725,39 @@ linux_low_enable_btrace (ptid_t ptid)
   return tinfo;
 }
 
-/* Read branch trace data as btrace xml document.  */
+/* See to_disable_btrace target method.  */
 
-static void
+static int
+linux_low_disable_btrace (struct btrace_target_info *tinfo)
+{
+  enum btrace_error err;
+
+  err = linux_disable_btrace (tinfo);
+  return (err == BTRACE_ERR_NONE ? 0 : -1);
+}
+
+/* See to_read_btrace target method.  */
+
+static int
 linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
 		       int type)
 {
   VEC (btrace_block_s) *btrace;
   struct btrace_block *block;
+  enum btrace_error err;
   int i;
 
-  btrace = linux_read_btrace (tinfo, type);
+  btrace = NULL;
+  err = linux_read_btrace (&btrace, tinfo, type);
+  if (err != BTRACE_ERR_NONE)
+    {
+      if (err == BTRACE_ERR_OVERFLOW)
+	buffer_grow_str0 (buffer, "E.Overflow.");
+      else
+	buffer_grow_str0 (buffer, "E.Generic Error.");
+
+      return -1;
+    }
 
   buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
   buffer_grow_str (buffer, "<btrace version=\"1.0\">\n");
@@ -5744,9 +5766,11 @@ linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
     buffer_xml_printf (buffer, "<block begin=\"0x%s\" end=\"0x%s\"/>\n",
 		       paddress (block->begin), paddress (block->end));
 
-  buffer_grow_str (buffer, "</btrace>\n");
+  buffer_grow_str0 (buffer, "</btrace>\n");
 
   VEC_free (btrace_block_s, btrace);
+
+  return 0;
 }
 #endif /* HAVE_LINUX_BTRACE */
 
@@ -5819,7 +5843,7 @@ static struct target_ops linux_target_ops = {
 #ifdef HAVE_LINUX_BTRACE
   linux_supports_btrace,
   linux_low_enable_btrace,
-  linux_disable_btrace,
+  linux_low_disable_btrace,
   linux_low_read_btrace,
 #else
   NULL,
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 9ae28f8..28ea048 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 d090a30..ae48cd7 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -356,12 +356,15 @@ struct target_ops
      information struct for reading and for disabling branch trace.  */
   struct btrace_target_info *(*enable_btrace) (ptid_t ptid);
 
-  /* Disable branch tracing.  */
+  /* Disable branch tracing.
+     Returns zero on success, non-zero otherwise.  */
   int (*disable_btrace) (struct btrace_target_info *tinfo);
 
   /* Read branch trace data into buffer.  We use an int to specify the type
-     to break a cyclic dependency.  */
-  void (*read_btrace) (struct btrace_target_info *, struct buffer *, int type);
+     to break a cyclic dependency.
+     Return 0 on success; print an error message into BUFFER and return -1,
+     otherwise.  */
+  int (*read_btrace) (struct btrace_target_info *, struct buffer *, int type);
 
   /* Return true if target supports range stepping.  */
   int (*supports_range_stepping) (void);
diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c
index 75d3fa0..fdf5dee 100644
--- a/gdb/i386-linux-nat.c
+++ b/gdb/i386-linux-nat.c
@@ -1084,10 +1084,10 @@ i386_linux_enable_btrace (ptid_t ptid)
 static void
 i386_linux_disable_btrace (struct btrace_target_info *tinfo)
 {
-  int errcode = linux_disable_btrace (tinfo);
+  enum btrace_error errcode = linux_disable_btrace (tinfo);
 
-  if (errcode != 0)
-    error (_("Could not disable branch tracing: %s."), safe_strerror (errcode));
+  if (errcode != BTRACE_ERR_NONE)
+    error (_("Could not disable branch tracing."));
 }
 
 /* Teardown branch tracing.  */
diff --git a/gdb/remote.c b/gdb/remote.c
index 0eda325..d3ed428 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -11489,13 +11489,14 @@ remote_teardown_btrace (struct btrace_target_info *tinfo)
 
 /* Read the branch trace.  */
 
-static VEC (btrace_block_s) *
-remote_read_btrace (struct btrace_target_info *tinfo,
+static enum btrace_error
+remote_read_btrace (VEC (btrace_block_s) **btrace,
+		    struct btrace_target_info *tinfo,
 		    enum btrace_read_type type)
 {
   struct packet_config *packet = &remote_protocol_packets[PACKET_qXfer_btrace];
   struct remote_state *rs = get_remote_state ();
-  VEC (btrace_block_s) *btrace = NULL;
+  struct cleanup *cleanup;
   const char *annex;
   char *xml;
 
@@ -11514,6 +11515,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."),
@@ -11522,15 +11526,14 @@ remote_read_btrace (struct btrace_target_info *tinfo,
 
   xml = target_read_stralloc (&current_target,
                               TARGET_OBJECT_BTRACE, annex);
-  if (xml != NULL)
-    {
-      struct cleanup *cleanup = make_cleanup (xfree, xml);
+  if (xml == NULL)
+    return BTRACE_ERR_UNKNOWN;
 
-      btrace = parse_xml_btrace (xml);
-      do_cleanups (cleanup);
-    }
+  cleanup = make_cleanup (xfree, xml);
+  *btrace = parse_xml_btrace (xml);
+  do_cleanups (cleanup);
 
-  return btrace;
+  return BTRACE_ERR_NONE;
 }
 
 static int
diff --git a/gdb/target.c b/gdb/target.c
index c3e42f2..e275d07 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4232,18 +4232,19 @@ target_teardown_btrace (struct btrace_target_info *btinfo)
 
 /* See target.h.  */
 
-VEC (btrace_block_s) *
-target_read_btrace (struct btrace_target_info *btinfo,
+enum btrace_error
+target_read_btrace (VEC (btrace_block_s) **btrace,
+		    struct btrace_target_info *btinfo,
 		    enum btrace_read_type type)
 {
   struct target_ops *t;
 
   for (t = current_target.beneath; t != NULL; t = t->beneath)
     if (t->to_read_btrace != NULL)
-      return t->to_read_btrace (btinfo, type);
+      return t->to_read_btrace (btrace, btinfo, type);
 
   tcomplain ();
-  return NULL;
+  return BTRACE_ERR_NOT_SUPPORTED;
 }
 
 /* See target.h.  */
diff --git a/gdb/target.h b/gdb/target.h
index 224a575..946814c 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -834,9 +834,13 @@ struct target_ops
        be attempting to talk to a remote target.  */
     void (*to_teardown_btrace) (struct btrace_target_info *tinfo);
 
-    /* Read branch trace data.  */
-    VEC (btrace_block_s) *(*to_read_btrace) (struct btrace_target_info *,
-					     enum btrace_read_type);
+    /* Read branch trace data for the thread indicated by BTINFO into DATA.
+       DATA is cleared before new trace is added.
+       The branch trace will start with the most recent block and continue
+       towards older blocks.  */
+    enum btrace_error (*to_read_btrace) (VEC (btrace_block_s) **data,
+					 struct btrace_target_info *btinfo,
+					 enum btrace_read_type type);
 
     /* Stop trace recording.  */
     void (*to_stop_recording) (void);
@@ -1993,8 +1997,9 @@ extern void target_disable_btrace (struct btrace_target_info *btinfo);
 extern void target_teardown_btrace (struct btrace_target_info *btinfo);
 
 /* See to_read_btrace in struct target_ops.  */
-extern VEC (btrace_block_s) *target_read_btrace (struct btrace_target_info *,
-						 enum btrace_read_type);
+extern enum btrace_error target_read_btrace (VEC (btrace_block_s) **,
+					     struct btrace_target_info *,
+					     enum btrace_read_type);
 
 /* See to_stop_recording in struct target_ops.  */
 extern void target_stop_recording (void);
-- 
1.8.3.1

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

* [PATCH v10 13/28] Add target_ops argument to to_prepare_to_store
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (21 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 21/28] record-btrace: add to_wait and to_resume target methods Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-14  8:05 ` [PATCH v10 22/28] record-btrace: provide target_find_new_threads method Markus Metzger
                   ` (5 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

2013-12-17  Tom Tromey  <tromey@redhat.com>

	* windows-nat.c (windows_prepare_to_store): Add 'self' argument.
	* target.h (struct target_ops) <to_prepare_to_store>: Add
	argument.
	(target_prepare_to_store): Add argument.
	* target.c (debug_to_prepare_to_store): Add argument.
	(update_current_target): Update.
	* remote.c (remote_prepare_to_store): Add 'self' argument.
	* remote-sim.c (gdbsim_prepare_to_store): Add 'self' argument.
	* remote-mips.c (mips_prepare_to_store): Add 'self' argument.
	* remote-m32r-sdi.c (m32r_prepare_to_store): Add 'self' argument.
	* record-full.c (record_full_core_prepare_to_store): Add 'self'
	argument.
	* ravenscar-thread.c (ravenscar_prepare_to_store): Add argument.
	* nto-procfs.c (procfs_prepare_to_store): Add 'self' argument.
	* monitor.c (monitor_prepare_to_store): Add 'self' argument.
	* inf-child.c (inf_child_prepare_to_store): Add 'self' argument.
	* go32-nat.c (go32_prepare_to_store): Add 'self' argument.


---
 gdb/go32-nat.c         | 5 +++--
 gdb/inf-child.c        | 3 ++-
 gdb/monitor.c          | 2 +-
 gdb/nto-procfs.c       | 2 +-
 gdb/ravenscar-thread.c | 8 +++++---
 gdb/record-full.c      | 3 ++-
 gdb/remote-m32r-sdi.c  | 2 +-
 gdb/remote-mips.c      | 5 +++--
 gdb/remote-sim.c       | 5 +++--
 gdb/remote.c           | 5 +++--
 gdb/target.c           | 9 +++++----
 gdb/target.h           | 4 ++--
 gdb/windows-nat.c      | 2 +-
 13 files changed, 32 insertions(+), 23 deletions(-)

diff --git a/gdb/go32-nat.c b/gdb/go32-nat.c
index 8c8e1c7..aea80e5 100644
--- a/gdb/go32-nat.c
+++ b/gdb/go32-nat.c
@@ -246,7 +246,8 @@ static void go32_fetch_registers (struct target_ops *ops,
 static void store_register (const struct regcache *, int regno);
 static void go32_store_registers (struct target_ops *ops,
 				  struct regcache *, int regno);
-static void go32_prepare_to_store (struct regcache *);
+static void go32_prepare_to_store (struct target_ops *self,
+				   struct regcache *);
 static int go32_xfer_memory (CORE_ADDR memaddr, gdb_byte *myaddr, int len,
 			     int write,
 			     struct mem_attrib *attrib,
@@ -598,7 +599,7 @@ go32_store_registers (struct target_ops *ops,
 }
 
 static void
-go32_prepare_to_store (struct regcache *regcache)
+go32_prepare_to_store (struct target_ops *self, struct regcache *regcache)
 {
 }
 
diff --git a/gdb/inf-child.c b/gdb/inf-child.c
index 75944a0..549e44a 100644
--- a/gdb/inf-child.c
+++ b/gdb/inf-child.c
@@ -100,7 +100,8 @@ 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 *self,
+			    struct regcache *regcache)
 {
 }
 
diff --git a/gdb/monitor.c b/gdb/monitor.c
index c86f040b..bbb06c6 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 *self, struct regcache *regcache)
 {
   /* Do nothing, since we can store individual regs.  */
 }
diff --git a/gdb/nto-procfs.c b/gdb/nto-procfs.c
index dee4988..c2d0d30 100644
--- a/gdb/nto-procfs.c
+++ b/gdb/nto-procfs.c
@@ -1229,7 +1229,7 @@ procfs_kill_inferior (struct target_ops *ops)
 /* Store register REGNO, or all registers if REGNO == -1, from the contents
    of REGISTERS.  */
 static void
-procfs_prepare_to_store (struct regcache *regcache)
+procfs_prepare_to_store (struct target_ops *self, struct regcache *regcache)
 {
 }
 
diff --git a/gdb/ravenscar-thread.c b/gdb/ravenscar-thread.c
index 3fdca52..fc87d79 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 *self,
+					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,15 @@ ravenscar_store_registers (struct target_ops *ops,
 }
 
 static void
-ravenscar_prepare_to_store (struct regcache *regcache)
+ravenscar_prepare_to_store (struct target_ops *self,
+			    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 3fb77ef..ce0504e 100644
--- a/gdb/record-full.c
+++ b/gdb/record-full.c
@@ -2135,7 +2135,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 *self,
+				   struct regcache *regcache)
 {
 }
 
diff --git a/gdb/remote-m32r-sdi.c b/gdb/remote-m32r-sdi.c
index 7b122d7..9364b5f 100644
--- a/gdb/remote-m32r-sdi.c
+++ b/gdb/remote-m32r-sdi.c
@@ -1012,7 +1012,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 *self, 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 04c4a40..adf2643 100644
--- a/gdb/remote-mips.c
+++ b/gdb/remote-mips.c
@@ -90,7 +90,8 @@ static int mips_map_regno (struct gdbarch *, int);
 
 static void mips_set_register (int regno, ULONGEST value);
 
-static void mips_prepare_to_store (struct regcache *regcache);
+static void mips_prepare_to_store (struct target_ops *self,
+				   struct regcache *regcache);
 
 static int mips_fetch_word (CORE_ADDR addr, unsigned int *valp);
 
@@ -2064,7 +2065,7 @@ mips_fetch_registers (struct target_ops *ops,
    registers, so this function doesn't have to do anything.  */
 
 static void
-mips_prepare_to_store (struct regcache *regcache)
+mips_prepare_to_store (struct target_ops *self, struct regcache *regcache)
 {
 }
 
diff --git a/gdb/remote-sim.c b/gdb/remote-sim.c
index 7263727..3e7fd20 100644
--- a/gdb/remote-sim.c
+++ b/gdb/remote-sim.c
@@ -81,7 +81,8 @@ static void gdbsim_close (void);
 static void gdbsim_detach (struct target_ops *ops, const char *args,
 			   int from_tty);
 
-static void gdbsim_prepare_to_store (struct regcache *regcache);
+static void gdbsim_prepare_to_store (struct target_ops *self,
+				     struct regcache *regcache);
 
 static void gdbsim_files_info (struct target_ops *target);
 
@@ -1052,7 +1053,7 @@ gdbsim_wait (struct target_ops *ops,
    debugged.  */
 
 static void
-gdbsim_prepare_to_store (struct regcache *regcache)
+gdbsim_prepare_to_store (struct target_ops *self, struct regcache *regcache)
 {
   /* Do nothing, since we can store individual regs.  */
 }
diff --git a/gdb/remote.c b/gdb/remote.c
index 4250404..4370f6d 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -97,7 +97,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 *self,
+				     struct regcache *regcache);
 
 static void remote_open (char *name, int from_tty);
 
@@ -6466,7 +6467,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 *self, 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 42a8741..36064ca 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -85,7 +85,8 @@ 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_prepare_to_store (struct target_ops *self,
+				       struct regcache *);
 
 static void debug_to_files_info (struct target_ops *);
 
@@ -716,7 +717,7 @@ update_current_target (void)
 	    (void (*) (int))
 	    target_ignore);
   de_fault (to_prepare_to_store,
-	    (void (*) (struct regcache *))
+	    (void (*) (struct target_ops *, struct regcache *))
 	    noprocess);
   de_fault (deprecated_xfer_memory,
 	    (int (*) (CORE_ADDR, gdb_byte *, int, int,
@@ -4463,9 +4464,9 @@ target_call_history_range (ULONGEST begin, ULONGEST end, int flags)
 }
 
 static void
-debug_to_prepare_to_store (struct regcache *regcache)
+debug_to_prepare_to_store (struct target_ops *self, struct regcache *regcache)
 {
-  debug_target.to_prepare_to_store (regcache);
+  debug_target.to_prepare_to_store (&debug_target, regcache);
 
   fprintf_unfiltered (gdb_stdlog, "target_prepare_to_store ()\n");
 }
diff --git a/gdb/target.h b/gdb/target.h
index 7e14075..757a927 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -374,7 +374,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
@@ -1006,7 +1006,7 @@ extern void target_store_registers (struct regcache *regcache, int regs);
    debugged.  */
 
 #define	target_prepare_to_store(regcache)	\
-     (*current_target.to_prepare_to_store) (regcache)
+     (*current_target.to_prepare_to_store) (&current_target, regcache)
 
 /* Determine current address space of thread PTID.  */
 
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index b42e5df..28f421a 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -2468,7 +2468,7 @@ windows_kill_inferior (struct target_ops *ops)
 }
 
 static void
-windows_prepare_to_store (struct regcache *regcache)
+windows_prepare_to_store (struct target_ops *self, struct regcache *regcache)
 {
   /* Do nothing, since we can store individual regs.  */
 }
-- 
1.8.3.1

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

* [PATCH v10 02/28] btrace, linux: fix memory leak when reading branch trace
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (11 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 19/28] target, breakpoint: allow insert/remove breakpoint to be forwarded Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-14  8:05 ` [PATCH v10 04/28] gdbarch: add instruction predicate methods Markus Metzger
                   ` (15 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +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.

2014-01-14  Markus Metzger  <markus.t.metzger@intel.com>

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


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

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

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

* [PATCH v10 28/28] record-btrace: add (reverse-)stepping support
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (25 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 07/28] record-btrace: fix insn range in function call history Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-14  8:05 ` [PATCH v10 27/28] target: allow decr_pc_after_break to be defined by the target Markus Metzger
  2014-01-15 15:54 ` [PATCH v10 00/28] record-btrace: reverse Pedro Alves
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

Provide to_resume and to_wait target methods for the btrace record target
to allow reverse stepping and replay support.

Replay is limited in the sense that only stepping and source correlation
are supported.  We do not record data and thus can not show variables.

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

Reviewed-by: Eli Zaretskii

2014-01-14  Markus Metzger  <markus.t.metzger@intel.com>

	* btrace.h (btrace_thread_flag): New.
	(struct btrace_thread_info) <flags>: New.
	* record-btrace.c (record_btrace_resume_thread)
	(record_btrace_find_thread_to_move, btrace_step_no_history)
	(btrace_step_stopped, record_btrace_start_replaying)
	(record_btrace_step_thread, record_btrace_decr_pc_after_break)
	(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.
	* gdb.btrace/data.c: New.
	* gdb.btrace/data.exp: New.
	* gdb.btrace/Makefile.in (EXECUTABLES): Add new.

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


---
 gdb/NEWS                                       |   4 +
 gdb/btrace.h                                   |  22 ++
 gdb/doc/gdb.texinfo                            |   6 +-
 gdb/record-btrace.c                            | 430 +++++++++++++++++++++++--
 gdb/testsuite/gdb.btrace/Makefile.in           |   3 +-
 gdb/testsuite/gdb.btrace/data.c                |  36 +++
 gdb/testsuite/gdb.btrace/data.exp              |  45 +++
 gdb/testsuite/gdb.btrace/delta.exp             |  15 +
 gdb/testsuite/gdb.btrace/finish.exp            |  59 ++++
 gdb/testsuite/gdb.btrace/multi-thread-step.c   |  53 +++
 gdb/testsuite/gdb.btrace/multi-thread-step.exp | 135 ++++++++
 gdb/testsuite/gdb.btrace/next.exp              |  76 +++++
 gdb/testsuite/gdb.btrace/nexti.exp             |  76 +++++
 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        |  52 +++
 gdb/testsuite/gdb.btrace/step.exp              |  89 +++++
 gdb/testsuite/gdb.btrace/stepi.exp             |  93 ++++++
 gdb/testsuite/gdb.btrace/tailcall.exp          |  13 +
 19 files changed, 1238 insertions(+), 42 deletions(-)
 create mode 100644 gdb/testsuite/gdb.btrace/data.c
 create mode 100644 gdb/testsuite/gdb.btrace/data.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/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 440f04b..bf205b2 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -117,6 +117,10 @@ info exceptions REGEXP
   For locations inside the execution trace, the back trace is computed
   based on the information stored in the execution trace.
 
+* The btrace record target supports limited reverse execution and replay.
+  The target does not record data and therefore does not allow reading
+  memory or registers.
+
 * New options
 
 set debug symfile off|on
diff --git a/gdb/btrace.h b/gdb/btrace.h
index 193f916..f83a80f 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 3ee7538..5aac39b 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6263,8 +6263,10 @@ replay implementation.  This method allows replaying and reverse
 execution.
 
 @item btrace
-Hardware-supported instruction recording.  This method does not allow
-replaying and reverse execution.
+Hardware-supported instruction recording.  This method does not record
+data.  Further, the data is collected in a ring buffer so old data will
+be overwritten when the buffer is full.  It allows limited replay and
+reverse execution.
 
 This recording method may not be available on all processors.
 @end table
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 61caee4..67a7095 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -170,6 +170,9 @@ 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."));
+
   gdb_assert (record_btrace_thread_observer == NULL);
 
   disable_chain = make_cleanup (null_cleanup, NULL);
@@ -1291,14 +1294,166 @@ 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);
+}
+
+/* Start replaying a thread.  */
+
+static struct btrace_insn_iterator *
+record_btrace_start_replaying (struct thread_info *tp)
+{
+  volatile struct gdb_exception except;
+  struct btrace_insn_iterator *replay;
+  struct btrace_thread_info *btinfo;
+  int executing;
+
+  btinfo = &tp->btrace;
+  replay = NULL;
+
+  /* We can't start replaying without trace.  */
+  if (btinfo->begin == NULL)
+    return NULL;
+
+  /* Clear the executing flag to allow changes to the current frame.
+     We are not actually running, yet.  We just started a reverse execution
+     command or a record goto command.
+     For the latter, EXECUTING is false and this has no effect.
+     For the former, EXECUTING is true and we're in to_wait, about to
+     move the thread.  Since we need to recompute the stack, we temporarily
+     set EXECUTING to flase.  */
+  executing = is_executing (tp->ptid);
+  set_executing (tp->ptid, 0);
+
+  /* GDB stores the current frame_id when stepping in order to detects steps
+     into subroutines.
+     Since frames are computed differently when we're replaying, we need to
+     recompute those stored frames and fix them up so we can still detect
+     subroutines after we started replaying.  */
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      struct frame_info *frame;
+      struct frame_id frame_id;
+      int upd_step_frame_id, upd_step_stack_frame_id;
+
+      /* The current frame without replaying - computed via normal unwind.  */
+      frame = get_current_frame ();
+      frame_id = get_frame_id (frame);
+
+      /* Check if we need to update any stepping-related frame id's.  */
+      upd_step_frame_id = frame_id_eq (frame_id,
+				       tp->control.step_frame_id);
+      upd_step_stack_frame_id = frame_id_eq (frame_id,
+					     tp->control.step_stack_frame_id);
+
+      /* We start replaying at the end of the branch trace.  This corresponds
+	 to the current instruction.  */
+      replay = xmalloc (sizeof (*replay));
+      btrace_insn_end (replay, btinfo);
+
+      /* We're not replaying, yet.  */
+      gdb_assert (btinfo->replay == NULL);
+      btinfo->replay = replay;
+
+      /* Make sure we're not using any stale registers.  */
+      registers_changed_ptid (tp->ptid);
+
+      /* The current frame with replaying - computed via btrace unwind.  */
+      frame = get_current_frame ();
+      frame_id = get_frame_id (frame);
+
+      /* Replace stepping related frames where necessary.  */
+      if (upd_step_frame_id)
+	tp->control.step_frame_id = frame_id;
+      if (upd_step_stack_frame_id)
+	tp->control.step_stack_frame_id = frame_id;
+    }
+
+  /* Restore the previous execution state.  */
+  set_executing (tp->ptid, executing);
+
+  if (except.reason < 0)
+    {
+      xfree (btinfo->replay);
+      btinfo->replay = NULL;
+
+      registers_changed_ptid (tp->ptid);
+
+      throw_exception (except);
+    }
+
+  return replay;
+}
+
+/* Stop replaying a thread.  */
+
+static void
+record_btrace_stop_replaying (struct thread_info *tp)
+{
+  struct btrace_thread_info *btinfo;
+
+  btinfo = &tp->btrace;
+
+  xfree (btinfo->replay);
+  btinfo->replay = NULL;
+
+  /* Make sure we're not leaving any stale registers.  */
+  registers_changed_ptid (tp->ptid);
+}
+
 /* The to_resume method of target record-btrace.  */
 
 static void
 record_btrace_resume (struct target_ops *ops, ptid_t ptid, int step,
 		      enum gdb_signal signal)
 {
+  struct thread_info *tp, *other;
+  enum btrace_thread_flag flag;
+
+  DEBUG ("resume %s: %s", target_pid_to_str (ptid), step ? "step" : "cont");
+
+  tp = record_btrace_find_resume_thread (ptid);
+  if (tp == NULL)
+    error (_("Cannot find thread to resume."));
+
+  /* Stop replaying other threads if the thread to resume is not replaying.  */
+  if (!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)
@@ -1307,7 +1462,200 @@ record_btrace_resume (struct target_ops *ops, ptid_t ptid, int step,
       error (_("Cannot find target for stepping."));
     }
 
-  error (_("You can't do this from here.  Do 'record goto end', first."));
+  /* Compute the btrace thread flag for the requested move.  */
+  if (step == 0)
+    flag = execution_direction == EXEC_REVERSE ? BTHR_RCONT : BTHR_CONT;
+  else
+    flag = execution_direction == EXEC_REVERSE ? BTHR_RSTEP : BTHR_STEP;
+
+  /* At the moment, we only move a single thread.  We could also move
+     all threads in parallel by single-stepping each resumed thread
+     until the first runs into an event.
+     When we do that, we would want to continue all other threads.
+     For now, just resume one thread to not confuse to_wait.  */
+  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;
+
+  /* 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 target_waitstatus 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 target_waitstatus indicating that a step finished.  */
+
+static struct target_waitstatus
+btrace_step_stopped (void)
+{
+  struct target_waitstatus status;
+
+  status.kind = TARGET_WAITKIND_STOPPED;
+  status.value.sig = GDB_SIGNAL_TRAP;
+
+  return status;
+}
+
+/* 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 ("reverse-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 ();
+	}
+    }
 }
 
 /* The to_wait method of target record-btrace.  */
@@ -1316,8 +1664,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)
@@ -1326,7 +1678,53 @@ 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);
+
+  /* We moved the replay position but did not update registers.  */
+  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_decr_pc_after_break method of target record-btrace.  */
+
+static CORE_ADDR
+record_btrace_decr_pc_after_break (struct target_ops *ops,
+				   struct gdbarch *gdbarch)
+{
+  /* When replaying, we do not actually execute the breakpoint instruction
+     so there is no need to adjust the PC after hitting a breakpoint.  */
+  if (record_btrace_is_replaying ())
+    return 0;
+
+  return forward_target_decr_pc_after_break (ops->beneath, gdbarch);
 }
 
 /* The to_find_new_threads method of target record-btrace.  */
@@ -1376,32 +1774,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.  */
@@ -1503,6 +1889,8 @@ 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_decr_pc_after_break = record_btrace_decr_pc_after_break;
   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 2ae673a..ec00b59 100644
--- a/gdb/testsuite/gdb.btrace/Makefile.in
+++ b/gdb/testsuite/gdb.btrace/Makefile.in
@@ -2,7 +2,8 @@ VPATH = @srcdir@
 srcdir = @srcdir@
 
 EXECUTABLES   = enable function_call_history instruction_history tailcall \
-  exception unknown_functions record_goto delta
+  exception unknown_functions record_goto delta finish next nexti step \
+  stepi multi-thread-step rn-dl-bind data
 
 MISCELLANEOUS =
 
diff --git a/gdb/testsuite/gdb.btrace/data.c b/gdb/testsuite/gdb.btrace/data.c
new file mode 100644
index 0000000..7368394
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/data.c
@@ -0,0 +1,36 @@
+/* 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/>.  */
+
+volatile static int glob;
+
+void
+test (void)
+{		/* test.1 */
+  volatile static int loc;
+
+  loc += 1;	/* test.2 */
+  glob += loc;	/* test.3 */
+}		/* test.4 */
+
+int
+main (void)
+{		/* main.1 */
+  test ();	/* main.2 */
+  return 0;	/* main.3 */
+}		/* main.4 */
diff --git a/gdb/testsuite/gdb.btrace/data.exp b/gdb/testsuite/gdb.btrace/data.exp
new file mode 100644
index 0000000..d6bc854
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/data.exp
@@ -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/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile
+if [prepare_for_testing $testfile.exp $testfile $srcfile] {
+    return -1
+}
+if ![runto_main] {
+    return -1
+}
+
+# trace the test code
+gdb_test_no_output "record btrace"
+gdb_test "next" ".*main\.3.*"
+
+# reverse step into test
+gdb_test "reverse-step" ".*test\.4.*"
+
+# we can't read memory while we're replaying
+gdb_test "print glob" "Memory at .* unavailable\."
+gdb_test "print loc" "Memory at .* unavailable\."
+
+# stop replaying and try again
+gdb_test "record goto end"
+gdb_test "print glob" "1"
diff --git a/gdb/testsuite/gdb.btrace/delta.exp b/gdb/testsuite/gdb.btrace/delta.exp
index e606751..5c3505c 100644
--- a/gdb/testsuite/gdb.btrace/delta.exp
+++ b/gdb/testsuite/gdb.btrace/delta.exp
@@ -66,3 +66,18 @@ with_test_prefix "once" {
 with_test_prefix "twice" {
   check_trace
 }
+
+# check that we can reverse-stepi that instruction
+gdb_test "reverse-stepi"
+gdb_test "info record" [join [list \
+  "Active record target: record-btrace" \
+  "Recorded 1 instructions in 1 functions for .*" \
+  "Replay in progress\.  At instruction 1\." \
+  ] "\r\n"] "reverse-stepi"
+
+# and back
+gdb_test "stepi"
+gdb_test "info record" [join [list \
+  "Active record target: record-btrace" \
+  "Recorded 1 instructions in 1 functions for .*" \
+  ] "\r\n"] "and back"
diff --git a/gdb/testsuite/gdb.btrace/finish.exp b/gdb/testsuite/gdb.btrace/finish.exp
new file mode 100644
index 0000000..a27082e
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/finish.exp
@@ -0,0 +1,59 @@
+# 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"
+
+proc check_replay_at { insn } {
+  gdb_test "info record" [join [list \
+    "Active record target: record-btrace" \
+    "Recorded 40 instructions in 16 functions for .*" \
+    "Replay in progress\.  At instruction $insn\." \
+    ] "\r\n"]
+}
+
+# let's go somewhere where we can finish
+gdb_test "record goto 32" ".*fun1\.1.*"
+with_test_prefix "at 32" { check_replay_at 32 }
+
+gdb_test "finish" ".*fun2\.3.*"
+with_test_prefix "finish into fun2" { check_replay_at 35 }
+
+gdb_test "reverse-finish" ".*fun3\.3.*"
+with_test_prefix "reverse-finish into fun3" { check_replay_at 27 }
+
+gdb_test "finish" ".*fun4\.5.*"
+with_test_prefix "finish into fun4" { check_replay_at 39 }
+
+gdb_test "reverse-finish" ".*main\.2.*"
+with_test_prefix "reverse-finish into main" { check_replay_at 1 }
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..2c108b2
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/multi-thread-step.exp
@@ -0,0 +1,135 @@
+# 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 } {
+	gdb_breakpoint $line
+	gdb_continue_to_breakpoint "cont to $line" ".*$line\r\n.*"
+	delete_breakpoints
+}
+
+# trace the code between the two breakpoints
+delete_breakpoints
+gdb_cont_to_line $srcfile:$bp_1
+# make sure GDB knows about the new thread
+gdb_test "info threads" ".*"
+gdb_test_no_output "record btrace"
+gdb_cont_to_line $srcfile:$bp_2
+
+# navigate in the trace history for both threads
+with_test_prefix "navigate" {
+  gdb_test "thread 1" ".*"
+  with_test_prefix "thread 1" {
+    gdb_test "record goto begin" ".*"
+    gdb_test "info record" ".*Replay in progress\.  At instruction 1\."
+  }
+  gdb_test "thread 2" ".*"
+  with_test_prefix "thread 2" {
+    gdb_test "record goto begin" ".*"
+    gdb_test "info record" ".*Replay in progress\.  At instruction 1\."
+  }
+}
+
+# step both threads
+with_test_prefix "step" {
+  gdb_test "thread 1" ".*"
+  with_test_prefix "thread 1" {
+    gdb_test "info record" ".*Replay in progress\.  At instruction 1\."
+    gdb_test "stepi" ".*"
+    gdb_test "info record" ".*Replay in progress\.  At instruction 2\."
+  }
+  gdb_test "thread 2" ".*"
+  with_test_prefix "thread 2" {
+    gdb_test "info record" ".*Replay in progress\.  At instruction 1\."
+    gdb_test "stepi" ".*"
+    gdb_test "info record" ".*Replay in progress\.  At instruction 2\."
+  }
+}
+
+# run to the end of the history for both threads
+with_test_prefix "cont" {
+  gdb_test "thread 1" ".*"
+  with_test_prefix "thread 1" {
+    gdb_test "info record" ".*Replay in progress\.  At instruction 2\."
+    gdb_test "continue" "No more reverse-execution history.*"
+  }
+  gdb_test "thread 2" ".*"
+  with_test_prefix "thread 2" {
+    gdb_test "info record" ".*Replay in progress\.  At instruction 2\."
+    gdb_test "continue" "No more reverse-execution history.*"
+  }
+}
+
+# reverse-step both threads
+with_test_prefix "reverse-step" {
+  gdb_test "thread 1" ".*"
+  with_test_prefix "thread 1" {
+    gdb_test "reverse-stepi" ".*"
+    gdb_test "info record" ".*Replay in progress\..*"
+  }
+  gdb_test "thread 2" ".*"
+  with_test_prefix "thread 2" {
+    gdb_test "reverse-stepi" ".*"
+    gdb_test "info record" ".*Replay in progress\..*"
+  }
+}
+
+# both threads are still replaying
+with_test_prefix "check" {
+  gdb_test "thread 1" ".*"
+  with_test_prefix "thread 1" {
+    gdb_test "info record" ".*Replay in progress\..*"
+  }
+  gdb_test "thread 2" ".*"
+  with_test_prefix "thread 2" {
+    gdb_test "info record" ".*Replay in progress\..*"
+  }
+}
+
+# navigate back into the history for thread 1 and continue thread 2
+with_test_prefix "cont" {
+  gdb_test "thread 1" ".*"
+  with_test_prefix "thread 1" {
+    gdb_test "record goto begin" ".*"
+    gdb_test "info record" ".*Replay in progress\.  At instruction 1\."
+  }
+  gdb_test "thread 2" ".*"
+  with_test_prefix "thread 2" {
+    gdb_test "record goto end" ".*"
+    gdb_cont_to_line $srcfile:$bp_3
+  }
+}
diff --git a/gdb/testsuite/gdb.btrace/next.exp b/gdb/testsuite/gdb.btrace/next.exp
new file mode 100644
index 0000000..b39eb79
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/next.exp
@@ -0,0 +1,76 @@
+# 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"
+
+proc check_replay_at { insn } {
+  gdb_test "info record" [join [list \
+    "Active record target: record-btrace" \
+    "Recorded 40 instructions in 16 functions for .*" \
+    "Replay in progress\.  At instruction $insn\." \
+    ] "\r\n"]
+}
+
+# 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.*"
+with_test_prefix "reverse-next - 1" { check_replay_at 1 }
+
+# we can't reverse-step any further
+gdb_test "reverse-next" "No more reverse-execution history\.\r\n.*main\.2.*"
+with_test_prefix "reverse-next - 2" { check_replay_at 1 }
+
+# but we can step back again
+gdb_test "next" ".*main\.3.*"
+gdb_test "info record" [join [list \
+  "Active record target: record-btrace" \
+  "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
+  ] "\r\n"] "next back"
+
+# let's go somewhere where we can step some more
+gdb_test "record goto 22" ".*fun3\.2.*"
+with_test_prefix "goto 22" { check_replay_at 22 }
+
+gdb_test "next" ".*fun3\.3.*"
+with_test_prefix "next to 27" { check_replay_at 27 }
+
+gdb_test "next" ".*fun3\.4.*"
+with_test_prefix "next to 37" { check_replay_at 37 }
+
+# and back again
+gdb_test "reverse-next" ".*fun3\.3.*"
+with_test_prefix "reverse-next to 27" { check_replay_at 27 }
+
+gdb_test "reverse-next" ".*fun3\.2.*"
+with_test_prefix "reverse-next to 22" { check_replay_at 22 }
diff --git a/gdb/testsuite/gdb.btrace/nexti.exp b/gdb/testsuite/gdb.btrace/nexti.exp
new file mode 100644
index 0000000..fd94df6
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/nexti.exp
@@ -0,0 +1,76 @@
+# 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"
+
+proc check_replay_at { insn } {
+  gdb_test "info record" [join [list \
+    "Active record target: record-btrace" \
+    "Recorded 40 instructions in 16 functions for .*" \
+    "Replay in progress\.  At instruction $insn\." \
+    ] "\r\n"]
+}
+
+# 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.*"
+with_test_prefix "reverse-nexti - 1" { check_replay_at 1 }
+
+# we can't reverse-step any further
+gdb_test "reverse-nexti" "No more reverse-execution history\.\r\n.*main\.2.*"
+with_test_prefix "reverse-nexti - 1" { check_replay_at 1 }
+
+# but we can step back again
+gdb_test "nexti" ".*main\.3.*" "next, 1.5"
+gdb_test "info record" [join [list \
+  "Active record target: record-btrace" \
+  "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
+  ] "\r\n"] "nexti back"
+
+# let's go somewhere where we can step some more
+gdb_test "record goto 22" ".*fun3\.2.*"
+with_test_prefix "goto 22" { check_replay_at 22 }
+
+gdb_test "nexti" ".*fun3\.3.*"
+with_test_prefix "nexti to 27" { check_replay_at 27 }
+
+gdb_test "nexti" ".*fun3\.4.*"
+with_test_prefix "nexti to 37" { check_replay_at 37 }
+
+# and back again
+gdb_test "reverse-nexti" ".*fun3\.3.*"
+with_test_prefix "reverse-nexti to 27" { check_replay_at 27 }
+
+gdb_test "reverse-nexti" ".*fun3\.2.*"
+with_test_prefix "reverse-nexti to 22" { check_replay_at 22 }
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..2f2250a
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/rn-dl-bind.exp
@@ -0,0 +1,52 @@
+# 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/>.
+#
+#
+# Test that we can reverse-next over the dynamic linker's symbol
+# lookup code.
+
+# 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"
+gdb_test "next" ".*main\.2.*"
+
+# just dump the function-call-history to help debugging
+gdb_test_no_output "set record function-call-history-size 0"
+gdb_test "record function-call-history /cli 1" ".*"
+
+# check that we can reverse-next and next
+gdb_test "reverse-next" ".*main\.1.*"
+gdb_test "next" ".*main\.2.*"
+
+# now go into test and try to reverse-next and next over the library call
+gdb_test "reverse-step" ".*test\.3.*"
+gdb_test "reverse-step" ".*test\.2.*"
+gdb_test "reverse-next" ".*test\.1.*"
+gdb_test "next" ".*test\.2.*"
diff --git a/gdb/testsuite/gdb.btrace/step.exp b/gdb/testsuite/gdb.btrace/step.exp
new file mode 100644
index 0000000..7c34d25
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/step.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 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"
+
+proc check_replay_at { insn } {
+  gdb_test "info record" [join [list \
+    "Active record target: record-btrace" \
+    "Recorded 40 instructions in 16 functions for .*" \
+    "Replay in progress\.  At instruction $insn\." \
+    ] "\r\n"]
+}
+
+# let's start by stepping back into the function we just returned from
+gdb_test "reverse-step" ".*fun4\.5.*"
+with_test_prefix "reverse-step to 39" { check_replay_at 39 }
+
+# again
+gdb_test "reverse-step" ".*fun3\.4.*"
+with_test_prefix "reverse-step to 37" { check_replay_at 37 }
+
+# and again
+gdb_test "reverse-step" ".*fun2\.3.*"
+with_test_prefix "reverse-step to 35" { check_replay_at 35 }
+
+# once more
+gdb_test "reverse-step" ".*fun1\.2.*"
+with_test_prefix "reverse-step to 33" { check_replay_at 33 }
+
+# and out again the other side
+gdb_test "reverse-step" ".*fun2\.2.*"
+with_test_prefix "reverse-step to 30" { check_replay_at 30 }
+
+# once again
+gdb_test "reverse-step" ".*fun3\.3.*"
+with_test_prefix "reverse-step to 27" { check_replay_at 27 }
+
+# and back the way we came
+gdb_test "step" ".*fun2\.2.*"
+with_test_prefix "step to 30" { check_replay_at 30 }
+
+gdb_test "step" ".*fun1\.2.*"
+with_test_prefix "step to 33" { check_replay_at 33 }
+
+gdb_test "step" ".*fun2\.3.*"
+with_test_prefix "step to 35" { check_replay_at 35 }
+
+gdb_test "step" ".*fun3\.4.*"
+with_test_prefix "step to 37" { check_replay_at 37 }
+
+gdb_test "step" ".*fun4\.5.*"
+with_test_prefix "step to 39" { check_replay_at 39 }
+
+gdb_test "step" ".*main\.3.*"
+gdb_test "info record" [join [list \
+  "Active record target: record-btrace" \
+  "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
+  ] "\r\n"] "step to live"
diff --git a/gdb/testsuite/gdb.btrace/stepi.exp b/gdb/testsuite/gdb.btrace/stepi.exp
new file mode 100644
index 0000000..fb12e28
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/stepi.exp
@@ -0,0 +1,93 @@
+# 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
+}
+
+proc check_replay_at { insn } {
+  gdb_test "info record" [join [list \
+    "Active record target: record-btrace" \
+    "Recorded 40 instructions in 16 functions for .*" \
+    "Replay in progress\.  At instruction $insn\." \
+    ] "\r\n"]
+}
+
+# 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.*"
+gdb_test "reverse-stepi" ".*fun4\.5.*"
+
+# let's check where we are in the trace
+with_test_prefix "reverse-stepi to 39" { check_replay_at 39 }
+
+# let's step forward and check again
+gdb_test "stepi" ".*fun4\.5.*"
+with_test_prefix "stepi to 40" { check_replay_at 40 }
+
+# with the next step, we stop replaying
+gdb_test "stepi" ".*main\.3.*"
+gdb_test "info record" [join [list \
+  "Active record target: record-btrace" \
+  "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
+  ] "\r\n"] "stepi to live"
+
+# let's step from a goto position somewhere in the middle
+gdb_test "record goto 22" ".*fun3\.2.*"
+with_test_prefix "goto 22" { check_replay_at 22 }
+
+gdb_test "stepi" ".*fun1\.1.*"
+with_test_prefix "stepi to 23" { check_replay_at 23 }
+
+# and back again
+gdb_test "reverse-stepi" ".*fun3\.2.*"
+gdb_test "reverse-stepi" ".*fun3\.1.*"
+with_test_prefix "reverse-stepi to 21" { check_replay_at 21 }
+
+# let's try to step off the left end
+gdb_test "record goto begin" ".*main\.2.*"
+with_test_prefix "goto begin" { check_replay_at 1 }
+
+gdb_test "reverse-stepi" "No more reverse-execution history\.\r\n.*main\.2.*"
+gdb_test "reverse-stepi" "No more reverse-execution history\.\r\n.*main\.2.*"
+with_test_prefix "reverse-stepi at begin" { check_replay_at 1 }
+
+# we can step forward, though
+gdb_test "stepi" ".*fun4\.1.*"
+with_test_prefix "stepi to 2" { check_replay_at 2 }
+
+# let's try to step off the left end again
+gdb_test "reverse-stepi" ".*main\.2.*"
+gdb_test "reverse-stepi" "No more reverse-execution history\.\r\n.*main\.2.*"
+gdb_test "reverse-stepi" "No more reverse-execution history\.\r\n.*main\.2.*"
+with_test_prefix "reverse-stepi at begin" { check_replay_at 1 }
diff --git a/gdb/testsuite/gdb.btrace/tailcall.exp b/gdb/testsuite/gdb.btrace/tailcall.exp
index 23988f2..ce1f053 100644
--- a/gdb/testsuite/gdb.btrace/tailcall.exp
+++ b/gdb/testsuite/gdb.btrace/tailcall.exp
@@ -77,3 +77,16 @@ gdb_test "backtrace" [join [list \
 # 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"
+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.*"
+gdb_test "reverse-step" "\[^\r\n\]*bar \\(\\) at x86-tailcall.c:24\r\n.*"
+gdb_test "reverse-finish" "\[^\r\n\]*foo \\(\\) at x86-tailcall.c:29\r\n.*"
+gdb_test "reverse-step" "\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*"
+gdb_test "next" "\[^\r\n\]*main \\(\\) at x86-tailcall.c:39\r\n.*"
+gdb_test "reverse-next" "\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*"
+gdb_test "step" "\[^\r\n\]*foo \\(\\) at x86-tailcall.c:29\r\n.*"
+gdb_test "finish" "\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*"
+gdb_test "reverse-step" "\[^\r\n\]*bar \\(\\) at x86-tailcall.c:24\r\n.*"
+gdb_test "finish" "\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*"
-- 
1.8.3.1

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

* [PATCH v10 24/28] record-btrace: extend unwinder
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (17 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 12/28] btrace: add replay position to btrace thread info Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-14  8:05 ` [PATCH v10 06/28] btrace: change branch trace data structure Markus Metzger
                   ` (9 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

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

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

Reviewed-by:  Eli Zaretskii

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

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

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


---
 gdb/NEWS                                 |   2 +
 gdb/btrace.c                             |   4 +
 gdb/dwarf2-frame.c                       |   6 +
 gdb/record-btrace.c                      | 288 +++++++++++++++++++++++++++++--
 gdb/record.h                             |   4 +
 gdb/testsuite/gdb.btrace/record_goto.exp |  12 ++
 gdb/testsuite/gdb.btrace/tailcall.exp    |  15 ++
 7 files changed, 321 insertions(+), 10 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index a031aba..506920e 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -114,6 +114,8 @@ info exceptions REGEXP
   are listed.
 
 * The btrace record target now supports the 'record goto' command.
+  For locations inside the execution trace, the back trace is computed
+  based on the information stored in the execution trace.
 
 * New options
 
diff --git a/gdb/btrace.c b/gdb/btrace.c
index 632ebe1..ba87e16 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -765,6 +765,10 @@ btrace_clear (struct thread_info *tp)
 
   DEBUG ("clear thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
 
+  /* Make sure btrace frames that may hold a pointer into the branch
+     trace data are destroyed.  */
+  reinit_frame_cache ();
+
   btinfo = &tp->btrace;
 
   it = btinfo->begin;
diff --git a/gdb/dwarf2-frame.c b/gdb/dwarf2-frame.c
index 772de56..ce21112 100644
--- a/gdb/dwarf2-frame.c
+++ b/gdb/dwarf2-frame.c
@@ -31,6 +31,7 @@
 #include "objfiles.h"
 #include "regcache.h"
 #include "value.h"
+#include "record.h"
 
 #include "gdb_assert.h"
 #include <string.h>
@@ -1510,6 +1511,11 @@ dwarf2_frame_base_sniffer (struct frame_info *this_frame)
 CORE_ADDR
 dwarf2_frame_cfa (struct frame_info *this_frame)
 {
+  if (frame_unwinder_is (this_frame, &record_btrace_tailcall_frame_unwind)
+      || frame_unwinder_is (this_frame, &record_btrace_frame_unwind))
+    throw_error (NOT_AVAILABLE_ERROR,
+		 _("cfa not available for record btrace target"));
+
   while (get_frame_type (this_frame) == INLINE_FRAME)
     this_frame = get_prev_frame (this_frame);
   if (get_frame_unwind_stop_reason (this_frame) == UNWIND_UNAVAILABLE)
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index ce62cd5..07575f6 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;
@@ -525,6 +526,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
@@ -546,8 +569,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);
@@ -966,13 +989,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.  */
@@ -981,7 +1091,27 @@ static void
 record_btrace_frame_this_id (struct frame_info *this_frame, void **this_cache,
 			     struct frame_id *this_id)
 {
-  /* Leave there the outer_frame_id value.  */
+  const struct btrace_frame_cache *cache;
+  const struct btrace_function *bfun;
+  CORE_ADDR code, special;
+
+  cache = *this_cache;
+
+  bfun = cache->bfun;
+  gdb_assert (bfun != NULL);
+
+  while (bfun->segment.prev != NULL)
+    bfun = bfun->segment.prev;
+
+  code = get_frame_func (this_frame);
+  special = bfun->number;
+
+  *this_id = frame_id_build_unavailable_stack_special (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.  */
@@ -991,8 +1121,46 @@ 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;
+
+      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.  */
@@ -1002,15 +1170,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
@@ -1019,14 +1271,26 @@ record_btrace_frame_sniffer (const struct frame_unwind *self,
    Therefore this unwinder reports any possibly unwound registers as
    <unavailable>.  */
 
-static const struct frame_unwind record_btrace_frame_unwind =
+const struct frame_unwind record_btrace_frame_unwind =
 {
   NORMAL_FRAME,
   record_btrace_frame_unwind_stop_reason,
   record_btrace_frame_this_id,
   record_btrace_frame_prev_register,
   NULL,
-  record_btrace_frame_sniffer
+  record_btrace_frame_sniffer,
+  record_btrace_frame_dealloc_cache
+};
+
+const struct frame_unwind record_btrace_tailcall_frame_unwind =
+{
+  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.  */
@@ -1233,6 +1497,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;
@@ -1269,4 +1534,7 @@ _initialize_record_btrace (void)
 
   init_record_btrace_ops ();
   add_target (&record_btrace_ops);
+
+  bfcache = htab_create_alloc (50, bfcache_hash, bfcache_eq, NULL,
+			       xcalloc, xfree);
 }
diff --git a/gdb/record.h b/gdb/record.h
index 18263fc..063ed96 100644
--- a/gdb/record.h
+++ b/gdb/record.h
@@ -32,6 +32,10 @@ extern struct cmd_list_element *set_record_cmdlist;
 extern struct cmd_list_element *show_record_cmdlist;
 extern struct cmd_list_element *info_record_cmdlist;
 
+/* Unwinders for some record targets.  */
+extern const struct frame_unwind record_btrace_frame_unwind;
+extern const struct frame_unwind record_btrace_tailcall_frame_unwind;
+
 /* A list of flags specifying what record target methods should print.  */
 enum record_print_flag
 {
diff --git a/gdb/testsuite/gdb.btrace/record_goto.exp b/gdb/testsuite/gdb.btrace/record_goto.exp
index eb07b9b..ab69bef 100644
--- a/gdb/testsuite/gdb.btrace/record_goto.exp
+++ b/gdb/testsuite/gdb.btrace/record_goto.exp
@@ -87,6 +87,18 @@ gdb_test "record instruction-history" [join [list \
 # let's go to another place in the history
 gdb_test "record goto 26" ".*fun3 \\(\\) at record_goto.c:35.*"
 
+# check the back trace at that location
+gdb_test "backtrace" [join [list \
+  "#0.*fun3.*at record_goto.c:35.*" \
+  "#1.*fun4.*at record_goto.c:43.*" \
+  "#2.*main.*at record_goto.c:49.*" \
+  "Backtrace stopped: not enough registers or memory available to unwind further" \
+  ] "\r\n"]
+
+# walk the backtrace
+gdb_test "up" ".*fun4.*at record_goto.c:43.*" "up to fun4"
+gdb_test "up" ".*main.*at record_goto.c:49.*" "up to main"
+
 # the function call history should start at the new location
 gdb_test "record function-call-history /ci -" [join [list \
   "8\t  fun3\tinst 19,21" \
diff --git a/gdb/testsuite/gdb.btrace/tailcall.exp b/gdb/testsuite/gdb.btrace/tailcall.exp
index c965675..a001783 100644
--- a/gdb/testsuite/gdb.btrace/tailcall.exp
+++ b/gdb/testsuite/gdb.btrace/tailcall.exp
@@ -60,3 +60,18 @@ gdb_test "record function-call-history /c 1" [join [list \
   "2\t    bar" \
   "3\tmain" \
   ] "\r\n"] "indented"
+
+# go into bar
+gdb_test "record goto 3" ".*bar \\(\\) at .*x86-tailcall.c:24\r\n.*"
+
+# check the backtrace
+gdb_test "backtrace" [join [list \
+  "#0.*bar \\(\\) at x86-tailcall.c:24" \
+  "#1.*foo \\(\\) at x86-tailcall.c:29" \
+  "#2.*main \\(\\) at x86-tailcall.c:37" \
+  "Backtrace stopped: not enough registers or memory available to unwind further" \
+  ] "\r\n"]
+
+# walk the backtrace
+gdb_test "up" "#1\[^\r\n\]*foo \\(\\) at x86-tailcall.c:29\r\n.*" "up to foo"
+gdb_test "up" "#2\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*" "up to main"
-- 
1.8.3.1

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

* [PATCH v10 27/28] target: allow decr_pc_after_break to be defined by the target
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (26 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 28/28] record-btrace: add (reverse-)stepping support Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-15 15:54 ` [PATCH v10 00/28] record-btrace: reverse Pedro Alves
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

Allow the target to define which value to use in decr_pc_after_break.
It defaults to gdbarch_decr_pc_after_break (GDBARCH).

2014-01-14  Markus Metzger  <markus.t.metzger@intel.com>

	* target.h (struct target_ops) <to_decr_pc_after_break>: New.
	(forward_target_decr_pc_after_break)
	(target_decr_pc_after_break): New.
	* target.c (forward_target_decr_pc_after_break)
	(target_decr_pc_after_break): New.
	* aix-thread.c (aix_thread_wait): Call target_decr_pc_after_break
	instead of gdbarch_decr_pc_after_break.
	* darwin-nat.c (cancel_breakpoint): Call target_decr_pc_after_break
	instead of gdbarch_decr_pc_after_break.
	* infrun.c (adjust_pc_after_break): Call target_decr_pc_after_break
	instead of gdbarch_decr_pc_after_break.
	* linux-nat.c (cancel_breakpoint): Call target_decr_pc_after_break
	instead of gdbarch_decr_pc_after_break.
	* linux-thread-db.c (check_event): Call target_decr_pc_after_break
	instead of gdbarch_decr_pc_after_break.
	* record-full.c (record_full_wait_1): Call target_decr_pc_after_break
	instead of gdbarch_decr_pc_after_break.


---
 gdb/aix-thread.c      |  2 +-
 gdb/darwin-nat.c      |  4 ++--
 gdb/infrun.c          |  9 +++++----
 gdb/linux-nat.c       |  4 ++--
 gdb/linux-thread-db.c |  2 +-
 gdb/record-full.c     |  6 +++---
 gdb/target.c          | 21 +++++++++++++++++++++
 gdb/target.h          | 13 +++++++++++++
 8 files changed, 48 insertions(+), 13 deletions(-)

diff --git a/gdb/aix-thread.c b/gdb/aix-thread.c
index 20c7872..58e71cc 100644
--- a/gdb/aix-thread.c
+++ b/gdb/aix-thread.c
@@ -1044,7 +1044,7 @@ aix_thread_wait (struct target_ops *ops,
       struct gdbarch *gdbarch = get_regcache_arch (regcache);
 
       if (regcache_read_pc (regcache)
-	  - gdbarch_decr_pc_after_break (gdbarch) == pd_brk_addr)
+	  - target_decr_pc_after_break (gdbarch) == pd_brk_addr)
 	return pd_activate (0);
     }
 
diff --git a/gdb/darwin-nat.c b/gdb/darwin-nat.c
index f0935a6..197115e 100644
--- a/gdb/darwin-nat.c
+++ b/gdb/darwin-nat.c
@@ -1011,14 +1011,14 @@ cancel_breakpoint (ptid_t ptid)
   struct gdbarch *gdbarch = get_regcache_arch (regcache);
   CORE_ADDR pc;
 
-  pc = regcache_read_pc (regcache) - gdbarch_decr_pc_after_break (gdbarch);
+  pc = regcache_read_pc (regcache) - target_decr_pc_after_break (gdbarch);
   if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
     {
       inferior_debug (4, "cancel_breakpoint for thread 0x%x\n",
 		      ptid_get_tid (ptid));
 
       /* Back up the PC if necessary.  */
-      if (gdbarch_decr_pc_after_break (gdbarch))
+      if (target_decr_pc_after_break (gdbarch))
 	regcache_write_pc (regcache, pc);
 
       return 1;
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 73038a3..9dd142d 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -2963,7 +2963,7 @@ adjust_pc_after_break (struct execution_control_state *ecs)
   struct regcache *regcache;
   struct gdbarch *gdbarch;
   struct address_space *aspace;
-  CORE_ADDR breakpoint_pc;
+  CORE_ADDR breakpoint_pc, decr_pc;
 
   /* If we've hit a breakpoint, we'll normally be stopped with SIGTRAP.  If
      we aren't, just return.
@@ -3025,15 +3025,16 @@ adjust_pc_after_break (struct execution_control_state *ecs)
      we have nothing to do.  */
   regcache = get_thread_regcache (ecs->ptid);
   gdbarch = get_regcache_arch (regcache);
-  if (gdbarch_decr_pc_after_break (gdbarch) == 0)
+
+  decr_pc = target_decr_pc_after_break (gdbarch);
+  if (decr_pc == 0)
     return;
 
   aspace = get_regcache_aspace (regcache);
 
   /* Find the location where (if we've hit a breakpoint) the
      breakpoint would be.  */
-  breakpoint_pc = regcache_read_pc (regcache)
-		  - gdbarch_decr_pc_after_break (gdbarch);
+  breakpoint_pc = regcache_read_pc (regcache) - decr_pc;
 
   /* Check whether there actually is a software breakpoint inserted at
      that location.
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 4dfb3ff..922326b 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -2736,7 +2736,7 @@ cancel_breakpoint (struct lwp_info *lp)
   struct gdbarch *gdbarch = get_regcache_arch (regcache);
   CORE_ADDR pc;
 
-  pc = regcache_read_pc (regcache) - gdbarch_decr_pc_after_break (gdbarch);
+  pc = regcache_read_pc (regcache) - target_decr_pc_after_break (gdbarch);
   if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
     {
       if (debug_linux_nat)
@@ -2745,7 +2745,7 @@ cancel_breakpoint (struct lwp_info *lp)
 			    target_pid_to_str (lp->ptid));
 
       /* Back up the PC if necessary.  */
-      if (gdbarch_decr_pc_after_break (gdbarch))
+      if (target_decr_pc_after_break (gdbarch))
 	regcache_write_pc (regcache, pc);
 
       return 1;
diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c
index 0daf24c..c05ebdf 100644
--- a/gdb/linux-thread-db.c
+++ b/gdb/linux-thread-db.c
@@ -1402,7 +1402,7 @@ check_event (ptid_t ptid)
 
   /* Bail out early if we're not at a thread event breakpoint.  */
   stop_pc = regcache_read_pc (regcache)
-	    - gdbarch_decr_pc_after_break (gdbarch);
+	    - target_decr_pc_after_break (gdbarch);
   if (stop_pc != info->td_create_bp_addr
       && stop_pc != info->td_death_bp_addr)
     return;
diff --git a/gdb/record-full.c b/gdb/record-full.c
index aef7665..8fae32b 100644
--- a/gdb/record-full.c
+++ b/gdb/record-full.c
@@ -1273,7 +1273,7 @@ record_full_wait_1 (struct target_ops *ops,
 			  struct gdbarch *gdbarch
 			    = get_regcache_arch (regcache);
 			  CORE_ADDR decr_pc_after_break
-			    = gdbarch_decr_pc_after_break (gdbarch);
+			    = target_decr_pc_after_break (gdbarch);
 			  if (decr_pc_after_break)
 			    regcache_write_pc (regcache,
 					       tmp_pc + decr_pc_after_break);
@@ -1346,7 +1346,7 @@ record_full_wait_1 (struct target_ops *ops,
 	  tmp_pc = regcache_read_pc (regcache);
 	  if (breakpoint_inserted_here_p (aspace, tmp_pc))
 	    {
-	      int decr_pc_after_break = gdbarch_decr_pc_after_break (gdbarch);
+	      int decr_pc_after_break = target_decr_pc_after_break (gdbarch);
 
 	      if (record_debug)
 		fprintf_unfiltered (gdb_stdlog,
@@ -1428,7 +1428,7 @@ record_full_wait_1 (struct target_ops *ops,
 		  if (breakpoint_inserted_here_p (aspace, tmp_pc))
 		    {
 		      int decr_pc_after_break
-			= gdbarch_decr_pc_after_break (gdbarch);
+			= target_decr_pc_after_break (gdbarch);
 
 		      if (record_debug)
 			fprintf_unfiltered (gdb_stdlog,
diff --git a/gdb/target.c b/gdb/target.c
index e275d07..167baa7 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4532,6 +4532,27 @@ target_get_tailcall_unwinder (void)
   return NULL;
 }
 
+/* See target.h.  */
+
+CORE_ADDR
+forward_target_decr_pc_after_break (struct target_ops *ops,
+				    struct gdbarch *gdbarch)
+{
+  for (; ops != NULL; ops = ops->beneath)
+    if (ops->to_decr_pc_after_break != NULL)
+      return ops->to_decr_pc_after_break (ops, gdbarch);
+
+  return gdbarch_decr_pc_after_break (gdbarch);
+}
+
+/* See target.h.  */
+
+CORE_ADDR
+target_decr_pc_after_break (struct gdbarch *gdbarch)
+{
+  return forward_target_decr_pc_after_break (current_target.beneath, gdbarch);
+}
+
 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 946814c..7bd581d 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -906,6 +906,12 @@ struct target_ops
     const struct frame_unwind *to_get_unwinder;
     const struct frame_unwind *to_get_tailcall_unwinder;
 
+    /* Return the number of bytes by which the PC needs to be decremented
+       after executing a breakpoint instruction.
+       Defaults to gdbarch_decr_pc_after_break (GDBARCH).  */
+    CORE_ADDR (*to_decr_pc_after_break) (struct target_ops *ops,
+					 struct gdbarch *gdbarch);
+
     int to_magic;
     /* Need sub-structure for target machine related rather than comm related?
      */
@@ -2046,4 +2052,11 @@ extern void target_call_history_from (ULONGEST begin, int size, int flags);
 /* See to_call_history_range.  */
 extern void target_call_history_range (ULONGEST begin, ULONGEST end, int flags);
 
+/* See to_decr_pc_after_break.  Start searching for the target at OPS.  */
+extern CORE_ADDR forward_target_decr_pc_after_break (struct target_ops *ops,
+						     struct gdbarch *gdbarch);
+
+/* See to_decr_pc_after_break.  */
+extern CORE_ADDR target_decr_pc_after_break (struct gdbarch *gdbarch);
+
 #endif /* !defined (TARGET_H) */
-- 
1.8.3.1

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

* [PATCH v10 21/28] record-btrace: add to_wait and to_resume target methods.
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (20 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 09/28] btrace: increase buffer size Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-14  8:05 ` [PATCH v10 13/28] Add target_ops argument to to_prepare_to_store Markus Metzger
                   ` (6 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +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.

2014-01-14  Markus Metzger  <markus.t.metzger@intel.com>

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


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

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 24e697f..7701fc4 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -1012,6 +1012,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
@@ -1046,6 +1085,8 @@ init_record_btrace_ops (void)
   ops->to_store_registers = record_btrace_store_registers;
   ops->to_prepare_to_store = record_btrace_prepare_to_store;
   ops->to_get_unwinder = &record_btrace_frame_unwind;
+  ops->to_resume = record_btrace_resume;
+  ops->to_wait = record_btrace_wait;
   ops->to_stratum = record_stratum;
   ops->to_magic = OPS_MAGIC;
 }
-- 
1.8.3.1

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

* [PATCH v10 06/28] btrace: change branch trace data structure
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (18 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 24/28] record-btrace: extend unwinder Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2015-01-08 20:49   ` x86_64-m32 internal error for multi-thread-step.exp [Re: [PATCH v10 06/28] btrace: change branch trace data structure] Jan Kratochvil
  2014-01-14  8:05 ` [PATCH v10 09/28] btrace: increase buffer size Markus Metzger
                   ` (8 subsequent siblings)
  28 siblings, 1 reply; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

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.

2014-01-14  Markus Metzger  <markus.t.metzger@intel.com>

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

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


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

diff --git a/gdb/btrace.c b/gdb/btrace.c
index df62da8..5bda127 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -45,92 +45,11 @@
 
 #define DEBUG_FTRACE(msg, args...) DEBUG ("[ftrace] " msg, ##args)
 
-/* Initialize the instruction iterator.  */
-
-static void
-btrace_init_insn_iterator (struct btrace_thread_info *btinfo)
-{
-  DEBUG ("init insn iterator");
-
-  btinfo->insn_iterator.begin = 1;
-  btinfo->insn_iterator.end = 0;
-}
-
-/* Initialize the function iterator.  */
-
-static void
-btrace_init_func_iterator (struct btrace_thread_info *btinfo)
-{
-  DEBUG ("init func iterator");
-
-  btinfo->func_iterator.begin = 1;
-  btinfo->func_iterator.end = 0;
-}
-
-/* Compute the instruction trace from the block trace.  */
-
-static VEC (btrace_inst_s) *
-compute_itrace (VEC (btrace_block_s) *btrace)
-{
-  VEC (btrace_inst_s) *itrace;
-  struct gdbarch *gdbarch;
-  unsigned int b;
-
-  DEBUG ("compute itrace");
-
-  itrace = NULL;
-  gdbarch = target_gdbarch ();
-  b = VEC_length (btrace_block_s, btrace);
-
-  while (b-- != 0)
-    {
-      btrace_block_s *block;
-      CORE_ADDR pc;
-
-      block = VEC_index (btrace_block_s, btrace, b);
-      pc = block->begin;
-
-      /* Add instructions for this block.  */
-      for (;;)
-	{
-	  btrace_inst_s *inst;
-	  int size;
-
-	  /* We should hit the end of the block.  Warn if we went too far.  */
-	  if (block->end < pc)
-	    {
-	      warning (_("Recorded trace may be corrupted."));
-	      break;
-	    }
-
-	  inst = VEC_safe_push (btrace_inst_s, itrace, NULL);
-	  inst->pc = pc;
-
-	  /* We're done once we pushed the instruction at the end.  */
-	  if (block->end == pc)
-	    break;
-
-	  size = gdb_insn_length (gdbarch, pc);
-
-	  /* Make sure we terminate if we fail to compute the size.  */
-	  if (size <= 0)
-	    {
-	      warning (_("Recorded trace may be incomplete."));
-	      break;
-	    }
-
-	  pc += size;
-	}
-    }
-
-  return itrace;
-}
-
 /* Return the function name of a recorded function segment for printing.
    This function never returns NULL.  */
 
 static const char *
-ftrace_print_function_name (struct btrace_func *bfun)
+ftrace_print_function_name (const struct btrace_function *bfun)
 {
   struct minimal_symbol *msym;
   struct symbol *sym;
@@ -151,7 +70,7 @@ ftrace_print_function_name (struct btrace_func *bfun)
    This function never returns NULL.  */
 
 static const char *
-ftrace_print_filename (struct btrace_func *bfun)
+ftrace_print_filename (const struct btrace_function *bfun)
 {
   struct symbol *sym;
   const char *filename;
@@ -166,44 +85,53 @@ ftrace_print_filename (struct btrace_func *bfun)
   return filename;
 }
 
-/* Print an ftrace debug status message.  */
+/* Return a string representation of the address of an instruction.
+   This function never returns NULL.  */
 
-static void
-ftrace_debug (struct btrace_func *bfun, const char *prefix)
+static const char *
+ftrace_print_insn_addr (const struct btrace_insn *insn)
 {
-  DEBUG_FTRACE ("%s: fun = %s, file = %s, lines = [%d; %d], insn = [%u; %u]",
-		prefix, ftrace_print_function_name (bfun),
-		ftrace_print_filename (bfun), bfun->lbegin, bfun->lend,
-		bfun->ibegin, bfun->iend);
+  if (insn == NULL)
+    return "<nil>";
+
+  return core_addr_to_string_nz (insn->pc);
 }
 
-/* Initialize a recorded function segment.  */
+/* Print an ftrace debug status message.  */
 
 static void
-ftrace_init_func (struct btrace_func *bfun, struct minimal_symbol *mfun,
-		  struct symbol *fun, unsigned int idx)
+ftrace_debug (const struct btrace_function *bfun, const char *prefix)
 {
-  bfun->msym = mfun;
-  bfun->sym = fun;
-  bfun->lbegin = INT_MAX;
-  bfun->lend = 0;
-  bfun->ibegin = idx;
-  bfun->iend = idx;
+  const char *fun, *file;
+  unsigned int ibegin, iend;
+  int lbegin, lend, level;
+
+  fun = ftrace_print_function_name (bfun);
+  file = ftrace_print_filename (bfun);
+  level = bfun->level;
+
+  lbegin = bfun->lbegin;
+  lend = bfun->lend;
+
+  ibegin = bfun->insn_offset;
+  iend = ibegin + VEC_length (btrace_insn_s, bfun->insn);
+
+  DEBUG_FTRACE ("%s: fun = %s, file = %s, level = %d, lines = [%d; %d], "
+		"insn = [%u; %u)", prefix, fun, file, level, lbegin, lend,
+		ibegin, iend);
 }
 
-/* Check whether the function has changed.  */
+/* Return non-zero if BFUN does not match MFUN and FUN,
+   return zero otherwise.  */
 
 static int
-ftrace_function_switched (struct btrace_func *bfun,
-			  struct minimal_symbol *mfun, struct symbol *fun)
+ftrace_function_switched (const struct btrace_function *bfun,
+			  const struct minimal_symbol *mfun,
+			  const struct symbol *fun)
 {
   struct minimal_symbol *msym;
   struct symbol *sym;
 
-  /* The function changed if we did not have one before.  */
-  if (bfun == NULL)
-    return 1;
-
   msym = bfun->msym;
   sym = bfun->sym;
 
@@ -228,109 +156,505 @@ ftrace_function_switched (struct btrace_func *bfun,
 	return 1;
     }
 
+  /* If we lost symbol information, we switched functions.  */
+  if (!(msym == NULL && sym == NULL) && mfun == NULL && fun == NULL)
+    return 1;
+
+  /* If we gained symbol information, we switched functions.  */
+  if (msym == NULL && sym == NULL && !(mfun == NULL && fun == NULL))
+    return 1;
+
   return 0;
 }
 
-/* Check if we should skip this file when generating the function call
-   history.  We would want to do that if, say, a macro that is defined
-   in another file is expanded in this function.  */
+/* Return non-zero if we should skip this file when generating the function
+   call history, zero otherwise.
+   We would want to do that if, say, a macro that is defined in another file
+   is expanded in this function.  */
 
 static int
-ftrace_skip_file (struct btrace_func *bfun, const char *filename)
+ftrace_skip_file (const struct btrace_function *bfun, const char *fullname)
 {
   struct symbol *sym;
   const char *bfile;
 
   sym = bfun->sym;
+  if (sym == NULL)
+    return 1;
 
-  if (sym != NULL)
-    bfile = symtab_to_fullname (sym->symtab);
-  else
-    bfile = "";
+  bfile = symtab_to_fullname (sym->symtab);
+
+  return (filename_cmp (bfile, fullname) != 0);
+}
+
+/* Allocate and initialize a new branch trace function segment.
+   PREV is the chronologically preceding function segment.
+   MFUN and FUN are the symbol information we have for this function.  */
+
+static struct btrace_function *
+ftrace_new_function (struct btrace_function *prev,
+		     struct minimal_symbol *mfun,
+		     struct symbol *fun)
+{
+  struct btrace_function *bfun;
+
+  bfun = xzalloc (sizeof (*bfun));
+
+  bfun->msym = mfun;
+  bfun->sym = fun;
+  bfun->flow.prev = prev;
+
+  /* We start with the identities of min and max, respectively.  */
+  bfun->lbegin = INT_MAX;
+  bfun->lend = INT_MIN;
 
-  if (filename == NULL)
-    filename = "";
+  if (prev != NULL)
+    {
+      gdb_assert (prev->flow.next == NULL);
+      prev->flow.next = bfun;
 
-  return (filename_cmp (bfile, filename) != 0);
+      bfun->number = prev->number + 1;
+      bfun->insn_offset = (prev->insn_offset
+			   + VEC_length (btrace_insn_s, prev->insn));
+    }
+
+  return bfun;
 }
 
-/* Compute the function trace from the instruction trace.  */
+/* Update the UP field of a function segment.  */
 
-static VEC (btrace_func_s) *
-compute_ftrace (VEC (btrace_inst_s) *itrace)
+static void
+ftrace_update_caller (struct btrace_function *bfun,
+		      struct btrace_function *caller,
+		      enum btrace_function_flag flags)
 {
-  VEC (btrace_func_s) *ftrace;
-  struct btrace_inst *binst;
-  struct btrace_func *bfun;
-  unsigned int idx;
+  if (bfun->up != NULL)
+    ftrace_debug (bfun, "updating caller");
 
-  DEBUG ("compute ftrace");
+  bfun->up = caller;
+  bfun->flags = flags;
+
+  ftrace_debug (bfun, "set caller");
+}
+
+/* Fix up the caller for all segments of a function.  */
+
+static void
+ftrace_fixup_caller (struct btrace_function *bfun,
+		     struct btrace_function *caller,
+		     enum btrace_function_flag flags)
+{
+  struct btrace_function *prev, *next;
+
+  ftrace_update_caller (bfun, caller, flags);
+
+  /* Update all function segments belonging to the same function.  */
+  for (prev = bfun->segment.prev; prev != NULL; prev = prev->segment.prev)
+    ftrace_update_caller (prev, caller, flags);
+
+  for (next = bfun->segment.next; next != NULL; next = next->segment.next)
+    ftrace_update_caller (next, caller, flags);
+}
+
+/* Add a new function segment for a call.
+   CALLER is the chronologically preceding function segment.
+   MFUN and FUN are the symbol information we have for this function.  */
+
+static struct btrace_function *
+ftrace_new_call (struct btrace_function *caller,
+		 struct minimal_symbol *mfun,
+		 struct symbol *fun)
+{
+  struct btrace_function *bfun;
+
+  bfun = ftrace_new_function (caller, mfun, fun);
+  bfun->up = caller;
+  bfun->level = caller->level + 1;
+
+  ftrace_debug (bfun, "new call");
+
+  return bfun;
+}
+
+/* Add a new function segment for a tail call.
+   CALLER is the chronologically preceding function segment.
+   MFUN and FUN are the symbol information we have for this function.  */
+
+static struct btrace_function *
+ftrace_new_tailcall (struct btrace_function *caller,
+		     struct minimal_symbol *mfun,
+		     struct symbol *fun)
+{
+  struct btrace_function *bfun;
 
-  ftrace = NULL;
-  bfun = NULL;
+  bfun = ftrace_new_function (caller, mfun, fun);
+  bfun->up = caller;
+  bfun->level = caller->level + 1;
+  bfun->flags |= BFUN_UP_LINKS_TO_TAILCALL;
 
-  for (idx = 0; VEC_iterate (btrace_inst_s, itrace, idx, binst); ++idx)
+  ftrace_debug (bfun, "new tail call");
+
+  return bfun;
+}
+
+/* Find the innermost caller in the back trace of BFUN with MFUN/FUN
+   symbol information.  */
+
+static struct btrace_function *
+ftrace_find_caller (struct btrace_function *bfun,
+		    struct minimal_symbol *mfun,
+		    struct symbol *fun)
+{
+  for (; bfun != NULL; bfun = bfun->up)
+    {
+      /* Skip functions with incompatible symbol information.  */
+      if (ftrace_function_switched (bfun, mfun, fun))
+	continue;
+
+      /* This is the function segment we're looking for.  */
+      break;
+    }
+
+  return bfun;
+}
+
+/* Find the innermost caller in the back trace of BFUN, skipping all
+   function segments that do not end with a call instruction (e.g.
+   tail calls ending with a jump).  */
+
+static struct btrace_function *
+ftrace_find_call (struct gdbarch *gdbarch, struct btrace_function *bfun)
+{
+  for (; bfun != NULL; bfun = bfun->up)
     {
-      struct symtab_and_line sal;
-      struct bound_minimal_symbol mfun;
-      struct symbol *fun;
-      const char *filename;
+      struct btrace_insn *last;
       CORE_ADDR pc;
 
-      pc = binst->pc;
+      /* We do not allow empty function segments.  */
+      gdb_assert (!VEC_empty (btrace_insn_s, bfun->insn));
+
+      last = VEC_last (btrace_insn_s, bfun->insn);
+      pc = last->pc;
 
-      /* Try to determine the function we're in.  We use both types of symbols
-	 to avoid surprises when we sometimes get a full symbol and sometimes
-	 only a minimal symbol.  */
-      fun = find_pc_function (pc);
-      mfun = lookup_minimal_symbol_by_pc (pc);
+      if (gdbarch_insn_is_call (gdbarch, pc))
+	break;
+    }
+
+  return bfun;
+}
+
+/* Add a continuation segment for a function into which we return.
+   PREV is the chronologically preceding function segment.
+   MFUN and FUN are the symbol information we have for this function.  */
+
+static struct btrace_function *
+ftrace_new_return (struct gdbarch *gdbarch,
+		   struct btrace_function *prev,
+		   struct minimal_symbol *mfun,
+		   struct symbol *fun)
+{
+  struct btrace_function *bfun, *caller;
+
+  bfun = ftrace_new_function (prev, mfun, fun);
+
+  /* It is important to start at PREV's caller.  Otherwise, we might find
+     PREV itself, if PREV is a recursive function.  */
+  caller = ftrace_find_caller (prev->up, mfun, fun);
+  if (caller != NULL)
+    {
+      /* The caller of PREV is the preceding btrace function segment in this
+	 function instance.  */
+      gdb_assert (caller->segment.next == NULL);
+
+      caller->segment.next = bfun;
+      bfun->segment.prev = caller;
+
+      /* Maintain the function level.  */
+      bfun->level = caller->level;
+
+      /* Maintain the call stack.  */
+      bfun->up = caller->up;
+      bfun->flags = caller->flags;
+
+      ftrace_debug (bfun, "new return");
+    }
+  else
+    {
+      /* We did not find a caller.  This could mean that something went
+	 wrong or that the call is simply not included in the trace.  */
 
-      if (fun == NULL && mfun.minsym == NULL)
+      /* Let's search for some actual call.  */
+      caller = ftrace_find_call (gdbarch, prev->up);
+      if (caller == NULL)
 	{
-	  DEBUG_FTRACE ("no symbol at %u, pc=%s", idx,
-			core_addr_to_string_nz (pc));
-	  continue;
-	}
+	  /* There is no call in PREV's back trace.  We assume that the
+	     branch trace did not include it.  */
+
+	  /* Let's find the topmost call function - this skips tail calls.  */
+	  while (prev->up != NULL)
+	    prev = prev->up;
 
-      /* If we're switching functions, we start over.  */
-      if (ftrace_function_switched (bfun, mfun.minsym, fun))
+	  /* We maintain levels for a series of returns for which we have
+	     not seen the calls.
+	     We start at the preceding function's level in case this has
+	     already been a return for which we have not seen the call.
+	     We start at level 0 otherwise, to handle tail calls correctly.  */
+	  bfun->level = min (0, prev->level) - 1;
+
+	  /* Fix up the call stack for PREV.  */
+	  ftrace_fixup_caller (prev, bfun, BFUN_UP_LINKS_TO_RET);
+
+	  ftrace_debug (bfun, "new return - no caller");
+	}
+      else
 	{
-	  bfun = VEC_safe_push (btrace_func_s, ftrace, NULL);
+	  /* There is a call in PREV's back trace to which we should have
+	     returned.  Let's remain at this level.  */
+	  bfun->level = prev->level;
 
-	  ftrace_init_func (bfun, mfun.minsym, fun, idx);
-	  ftrace_debug (bfun, "init");
+	  ftrace_debug (bfun, "new return - unknown caller");
 	}
+    }
+
+  return bfun;
+}
+
+/* Add a new function segment for a function switch.
+   PREV is the chronologically preceding function segment.
+   MFUN and FUN are the symbol information we have for this function.  */
+
+static struct btrace_function *
+ftrace_new_switch (struct btrace_function *prev,
+		   struct minimal_symbol *mfun,
+		   struct symbol *fun)
+{
+  struct btrace_function *bfun;
+
+  /* This is an unexplained function switch.  The call stack will likely
+     be wrong at this point.  */
+  bfun = ftrace_new_function (prev, mfun, fun);
 
-      /* Update the instruction range.  */
-      bfun->iend = idx;
-      ftrace_debug (bfun, "update insns");
+  /* We keep the function level.  */
+  bfun->level = prev->level;
 
-      /* Let's see if we have source correlation, as well.  */
-      sal = find_pc_line (pc, 0);
-      if (sal.symtab == NULL || sal.line == 0)
+  ftrace_debug (bfun, "new switch");
+
+  return bfun;
+}
+
+/* Update BFUN with respect to the instruction at PC.  This may create new
+   function segments.
+   Return the chronologically latest function segment, never NULL.  */
+
+static struct btrace_function *
+ftrace_update_function (struct gdbarch *gdbarch,
+			struct btrace_function *bfun, CORE_ADDR pc)
+{
+  struct bound_minimal_symbol bmfun;
+  struct minimal_symbol *mfun;
+  struct symbol *fun;
+  struct btrace_insn *last;
+
+  /* Try to determine the function we're in.  We use both types of symbols
+     to avoid surprises when we sometimes get a full symbol and sometimes
+     only a minimal symbol.  */
+  fun = find_pc_function (pc);
+  bmfun = lookup_minimal_symbol_by_pc (pc);
+  mfun = bmfun.minsym;
+
+  if (fun == NULL && mfun == NULL)
+    DEBUG_FTRACE ("no symbol at %s", core_addr_to_string_nz (pc));
+
+  /* If we didn't have a function before, we create one.  */
+  if (bfun == NULL)
+    return ftrace_new_function (bfun, mfun, fun);
+
+  /* Check the last instruction, if we have one.
+     We do this check first, since it allows us to fill in the call stack
+     links in addition to the normal flow links.  */
+  last = NULL;
+  if (!VEC_empty (btrace_insn_s, bfun->insn))
+    last = VEC_last (btrace_insn_s, bfun->insn);
+
+  if (last != NULL)
+    {
+      CORE_ADDR lpc;
+
+      lpc = last->pc;
+
+      /* Check for returns.  */
+      if (gdbarch_insn_is_ret (gdbarch, lpc))
+	return ftrace_new_return (gdbarch, bfun, mfun, fun);
+
+      /* Check for calls.  */
+      if (gdbarch_insn_is_call (gdbarch, lpc))
 	{
-	  DEBUG_FTRACE ("no lines at %u, pc=%s", idx,
-			core_addr_to_string_nz (pc));
-	  continue;
+	  int size;
+
+	  size = gdb_insn_length (gdbarch, lpc);
+
+	  /* Ignore calls to the next instruction.  They are used for PIC.  */
+	  if (lpc + size != pc)
+	    return ftrace_new_call (bfun, mfun, fun);
 	}
+    }
+
+  /* Check if we're switching functions for some other reason.  */
+  if (ftrace_function_switched (bfun, mfun, fun))
+    {
+      DEBUG_FTRACE ("switching from %s in %s at %s",
+		    ftrace_print_insn_addr (last),
+		    ftrace_print_function_name (bfun),
+		    ftrace_print_filename (bfun));
 
-      /* Check if we switched files.  This could happen if, say, a macro that
-	 is defined in another file is expanded here.  */
-      filename = symtab_to_fullname (sal.symtab);
-      if (ftrace_skip_file (bfun, filename))
+      if (last != NULL)
 	{
-	  DEBUG_FTRACE ("ignoring file at %u, pc=%s, file=%s", idx,
-			core_addr_to_string_nz (pc), filename);
-	  continue;
+	  CORE_ADDR start, lpc;
+
+	  start = get_pc_function_start (pc);
+
+	  /* If we can't determine the function for PC, we treat a jump at
+	     the end of the block as tail call.  */
+	  if (start == 0)
+	    start = pc;
+
+	  lpc = last->pc;
+
+	  /* Jumps indicate optimized tail calls.  */
+	  if (start == pc && gdbarch_insn_is_jump (gdbarch, lpc))
+	    return ftrace_new_tailcall (bfun, mfun, fun);
 	}
 
-      /* Update the line range.  */
-      bfun->lbegin = min (bfun->lbegin, sal.line);
-      bfun->lend = max (bfun->lend, sal.line);
-      ftrace_debug (bfun, "update lines");
+      return ftrace_new_switch (bfun, mfun, fun);
+    }
+
+  return bfun;
+}
+
+/* Update BFUN's source range with respect to the instruction at PC.  */
+
+static void
+ftrace_update_lines (struct btrace_function *bfun, CORE_ADDR pc)
+{
+  struct symtab_and_line sal;
+  const char *fullname;
+
+  sal = find_pc_line (pc, 0);
+  if (sal.symtab == NULL || sal.line == 0)
+    {
+      DEBUG_FTRACE ("no lines at %s", core_addr_to_string_nz (pc));
+      return;
+    }
+
+  /* Check if we switched files.  This could happen if, say, a macro that
+     is defined in another file is expanded here.  */
+  fullname = symtab_to_fullname (sal.symtab);
+  if (ftrace_skip_file (bfun, fullname))
+    {
+      DEBUG_FTRACE ("ignoring file at %s, file=%s",
+		    core_addr_to_string_nz (pc), fullname);
+      return;
+    }
+
+  /* Update the line range.  */
+  bfun->lbegin = min (bfun->lbegin, sal.line);
+  bfun->lend = max (bfun->lend, sal.line);
+
+  if (record_debug > 1)
+    ftrace_debug (bfun, "update lines");
+}
+
+/* Add the instruction at PC to BFUN's instructions.  */
+
+static void
+ftrace_update_insns (struct btrace_function *bfun, CORE_ADDR pc)
+{
+  struct btrace_insn *insn;
+
+  insn = VEC_safe_push (btrace_insn_s, bfun->insn, NULL);
+  insn->pc = pc;
+
+  if (record_debug > 1)
+    ftrace_debug (bfun, "update insn");
+}
+
+/* Compute the function branch trace from a block branch trace BTRACE for
+   a thread given by BTINFO.  */
+
+static void
+btrace_compute_ftrace (struct btrace_thread_info *btinfo,
+		       VEC (btrace_block_s) *btrace)
+{
+  struct btrace_function *begin, *end;
+  struct gdbarch *gdbarch;
+  unsigned int blk;
+  int level;
+
+  DEBUG ("compute ftrace");
+
+  gdbarch = target_gdbarch ();
+  begin = NULL;
+  end = NULL;
+  level = INT_MAX;
+  blk = VEC_length (btrace_block_s, btrace);
+
+  while (blk != 0)
+    {
+      btrace_block_s *block;
+      CORE_ADDR pc;
+
+      blk -= 1;
+
+      block = VEC_index (btrace_block_s, btrace, blk);
+      pc = block->begin;
+
+      for (;;)
+	{
+	  int size;
+
+	  /* We should hit the end of the block.  Warn if we went too far.  */
+	  if (block->end < pc)
+	    {
+	      warning (_("Recorded trace may be corrupted around %s."),
+		       core_addr_to_string_nz (pc));
+	      break;
+	    }
+
+	  end = ftrace_update_function (gdbarch, end, pc);
+	  if (begin == NULL)
+	    begin = end;
+
+	  /* Maintain the function level offset.  */
+	  level = min (level, end->level);
+
+	  ftrace_update_insns (end, pc);
+	  ftrace_update_lines (end, pc);
+
+	  /* We're done once we pushed the instruction at the end.  */
+	  if (block->end == pc)
+	    break;
+
+	  size = gdb_insn_length (gdbarch, pc);
+
+	  /* Make sure we terminate if we fail to compute the size.  */
+	  if (size <= 0)
+	    {
+	      warning (_("Recorded trace may be incomplete around %s."),
+		       core_addr_to_string_nz (pc));
+	      break;
+	    }
+
+	  pc += size;
+	}
     }
 
-  return ftrace;
+  btinfo->begin = begin;
+  btinfo->end = end;
+
+  /* LEVEL is the minimal function level of all btrace function segments.
+     Define the global level offset to -LEVEL so all function levels are
+     normalized to start at zero.  */
+  btinfo->level = -level;
 }
 
 /* See btrace.h.  */
@@ -394,6 +718,7 @@ btrace_fetch (struct thread_info *tp)
 {
   struct btrace_thread_info *btinfo;
   VEC (btrace_block_s) *btrace;
+  struct cleanup *cleanup;
 
   DEBUG ("fetch thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
 
@@ -402,18 +727,15 @@ btrace_fetch (struct thread_info *tp)
     return;
 
   btrace = target_read_btrace (btinfo->target, BTRACE_READ_NEW);
-  if (VEC_empty (btrace_block_s, btrace))
-    return;
-
-  btrace_clear (tp);
+  cleanup = make_cleanup (VEC_cleanup (btrace_block_s), &btrace);
 
-  btinfo->btrace = btrace;
-  btinfo->itrace = compute_itrace (btinfo->btrace);
-  btinfo->ftrace = compute_ftrace (btinfo->itrace);
+  if (!VEC_empty (btrace_block_s, btrace))
+    {
+      btrace_clear (tp);
+      btrace_compute_ftrace (btinfo, btrace);
+    }
 
-  /* Initialize branch trace iterators.  */
-  btrace_init_insn_iterator (btinfo);
-  btrace_init_func_iterator (btinfo);
+  do_cleanups (cleanup);
 }
 
 /* See btrace.h.  */
@@ -422,18 +744,29 @@ void
 btrace_clear (struct thread_info *tp)
 {
   struct btrace_thread_info *btinfo;
+  struct btrace_function *it, *trash;
 
   DEBUG ("clear thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
 
   btinfo = &tp->btrace;
 
-  VEC_free (btrace_block_s, btinfo->btrace);
-  VEC_free (btrace_inst_s, btinfo->itrace);
-  VEC_free (btrace_func_s, btinfo->ftrace);
+  it = btinfo->begin;
+  while (it != NULL)
+    {
+      trash = it;
+      it = it->flow.next;
 
-  btinfo->btrace = NULL;
-  btinfo->itrace = NULL;
-  btinfo->ftrace = NULL;
+      xfree (trash);
+    }
+
+  btinfo->begin = NULL;
+  btinfo->end = NULL;
+
+  xfree (btinfo->insn_history);
+  xfree (btinfo->call_history);
+
+  btinfo->insn_history = NULL;
+  btinfo->call_history = NULL;
 }
 
 /* See btrace.h.  */
@@ -541,3 +874,451 @@ parse_xml_btrace (const char *buffer)
 
   return btrace;
 }
+
+/* See btrace.h.  */
+
+const struct btrace_insn *
+btrace_insn_get (const struct btrace_insn_iterator *it)
+{
+  const struct btrace_function *bfun;
+  unsigned int index, end;
+
+  index = it->index;
+  bfun = it->function;
+
+  /* The index is within the bounds of this function's instruction vector.  */
+  end = VEC_length (btrace_insn_s, bfun->insn);
+  gdb_assert (0 < end);
+  gdb_assert (index < end);
+
+  return VEC_index (btrace_insn_s, bfun->insn, index);
+}
+
+/* See btrace.h.  */
+
+unsigned int
+btrace_insn_number (const struct btrace_insn_iterator *it)
+{
+  const struct btrace_function *bfun;
+
+  bfun = it->function;
+  return bfun->insn_offset + it->index;
+}
+
+/* See btrace.h.  */
+
+void
+btrace_insn_begin (struct btrace_insn_iterator *it,
+		   const struct btrace_thread_info *btinfo)
+{
+  const struct btrace_function *bfun;
+
+  bfun = btinfo->begin;
+  if (bfun == NULL)
+    error (_("No trace."));
+
+  it->function = bfun;
+  it->index = 0;
+}
+
+/* See btrace.h.  */
+
+void
+btrace_insn_end (struct btrace_insn_iterator *it,
+		 const struct btrace_thread_info *btinfo)
+{
+  const struct btrace_function *bfun;
+  unsigned int length;
+
+  bfun = btinfo->end;
+  if (bfun == NULL)
+    error (_("No trace."));
+
+  /* The last instruction in the last function is the current instruction.
+     We point to it - it is one past the end of the execution trace.  */
+  length = VEC_length (btrace_insn_s, bfun->insn);
+
+  it->function = bfun;
+  it->index = length - 1;
+}
+
+/* See btrace.h.  */
+
+unsigned int
+btrace_insn_next (struct btrace_insn_iterator *it, unsigned int stride)
+{
+  const struct btrace_function *bfun;
+  unsigned int index, steps;
+
+  bfun = it->function;
+  steps = 0;
+  index = it->index;
+
+  while (stride != 0)
+    {
+      unsigned int end, space, adv;
+
+      end = VEC_length (btrace_insn_s, bfun->insn);
+
+      gdb_assert (0 < end);
+      gdb_assert (index < end);
+
+      /* Compute the number of instructions remaining in this segment.  */
+      space = end - index;
+
+      /* Advance the iterator as far as possible within this segment.  */
+      adv = min (space, stride);
+      stride -= adv;
+      index += adv;
+      steps += adv;
+
+      /* Move to the next function if we're at the end of this one.  */
+      if (index == end)
+	{
+	  const struct btrace_function *next;
+
+	  next = bfun->flow.next;
+	  if (next == NULL)
+	    {
+	      /* We stepped past the last function.
+
+		 Let's adjust the index to point to the last instruction in
+		 the previous function.  */
+	      index -= 1;
+	      steps -= 1;
+	      break;
+	    }
+
+	  /* We now point to the first instruction in the new function.  */
+	  bfun = next;
+	  index = 0;
+	}
+
+      /* We did make progress.  */
+      gdb_assert (adv > 0);
+    }
+
+  /* Update the iterator.  */
+  it->function = bfun;
+  it->index = index;
+
+  return steps;
+}
+
+/* See btrace.h.  */
+
+unsigned int
+btrace_insn_prev (struct btrace_insn_iterator *it, unsigned int stride)
+{
+  const struct btrace_function *bfun;
+  unsigned int index, steps;
+
+  bfun = it->function;
+  steps = 0;
+  index = it->index;
+
+  while (stride != 0)
+    {
+      unsigned int adv;
+
+      /* Move to the previous function if we're at the start of this one.  */
+      if (index == 0)
+	{
+	  const struct btrace_function *prev;
+
+	  prev = bfun->flow.prev;
+	  if (prev == NULL)
+	    break;
+
+	  /* We point to one after the last instruction in the new function.  */
+	  bfun = prev;
+	  index = VEC_length (btrace_insn_s, bfun->insn);
+
+	  /* There is at least one instruction in this function segment.  */
+	  gdb_assert (index > 0);
+	}
+
+      /* Advance the iterator as far as possible within this segment.  */
+      adv = min (index, stride);
+      stride -= adv;
+      index -= adv;
+      steps += adv;
+
+      /* We did make progress.  */
+      gdb_assert (adv > 0);
+    }
+
+  /* Update the iterator.  */
+  it->function = bfun;
+  it->index = index;
+
+  return steps;
+}
+
+/* See btrace.h.  */
+
+int
+btrace_insn_cmp (const struct btrace_insn_iterator *lhs,
+		 const struct btrace_insn_iterator *rhs)
+{
+  unsigned int lnum, rnum;
+
+  lnum = btrace_insn_number (lhs);
+  rnum = btrace_insn_number (rhs);
+
+  return (int) (lnum - rnum);
+}
+
+/* See btrace.h.  */
+
+int
+btrace_find_insn_by_number (struct btrace_insn_iterator *it,
+			    const struct btrace_thread_info *btinfo,
+			    unsigned int number)
+{
+  const struct btrace_function *bfun;
+  unsigned int end;
+
+  for (bfun = btinfo->end; bfun != NULL; bfun = bfun->flow.prev)
+    if (bfun->insn_offset <= number)
+      break;
+
+  if (bfun == NULL)
+    return 0;
+
+  end = bfun->insn_offset + VEC_length (btrace_insn_s, bfun->insn);
+  if (end <= number)
+    return 0;
+
+  it->function = bfun;
+  it->index = number - bfun->insn_offset;
+
+  return 1;
+}
+
+/* See btrace.h.  */
+
+const struct btrace_function *
+btrace_call_get (const struct btrace_call_iterator *it)
+{
+  return it->function;
+}
+
+/* See btrace.h.  */
+
+unsigned int
+btrace_call_number (const struct btrace_call_iterator *it)
+{
+  const struct btrace_thread_info *btinfo;
+  const struct btrace_function *bfun;
+  unsigned int insns;
+
+  btinfo = it->btinfo;
+  bfun = it->function;
+  if (bfun != NULL)
+    return bfun->number;
+
+  /* For the end iterator, i.e. bfun == NULL, we return one more than the
+     number of the last function.  */
+  bfun = btinfo->end;
+  insns = VEC_length (btrace_insn_s, bfun->insn);
+
+  /* If the function contains only a single instruction (i.e. the current
+     instruction), it will be skipped and its number is already the number
+     we seek.  */
+  if (insns == 1)
+    return bfun->number;
+
+  /* Otherwise, return one more than the number of the last function.  */
+  return bfun->number + 1;
+}
+
+/* See btrace.h.  */
+
+void
+btrace_call_begin (struct btrace_call_iterator *it,
+		   const struct btrace_thread_info *btinfo)
+{
+  const struct btrace_function *bfun;
+
+  bfun = btinfo->begin;
+  if (bfun == NULL)
+    error (_("No trace."));
+
+  it->btinfo = btinfo;
+  it->function = bfun;
+}
+
+/* See btrace.h.  */
+
+void
+btrace_call_end (struct btrace_call_iterator *it,
+		 const struct btrace_thread_info *btinfo)
+{
+  const struct btrace_function *bfun;
+
+  bfun = btinfo->end;
+  if (bfun == NULL)
+    error (_("No trace."));
+
+  it->btinfo = btinfo;
+  it->function = NULL;
+}
+
+/* See btrace.h.  */
+
+unsigned int
+btrace_call_next (struct btrace_call_iterator *it, unsigned int stride)
+{
+  const struct btrace_function *bfun;
+  unsigned int steps;
+
+  bfun = it->function;
+  steps = 0;
+  while (bfun != NULL)
+    {
+      const struct btrace_function *next;
+      unsigned int insns;
+
+      next = bfun->flow.next;
+      if (next == NULL)
+	{
+	  /* Ignore the last function if it only contains a single
+	     (i.e. the current) instruction.  */
+	  insns = VEC_length (btrace_insn_s, bfun->insn);
+	  if (insns == 1)
+	    steps -= 1;
+	}
+
+      if (stride == steps)
+	break;
+
+      bfun = next;
+      steps += 1;
+    }
+
+  it->function = bfun;
+  return steps;
+}
+
+/* See btrace.h.  */
+
+unsigned int
+btrace_call_prev (struct btrace_call_iterator *it, unsigned int stride)
+{
+  const struct btrace_thread_info *btinfo;
+  const struct btrace_function *bfun;
+  unsigned int steps;
+
+  bfun = it->function;
+  steps = 0;
+
+  if (bfun == NULL)
+    {
+      unsigned int insns;
+
+      btinfo = it->btinfo;
+      bfun = btinfo->end;
+      if (bfun == NULL)
+	return 0;
+
+      /* Ignore the last function if it only contains a single
+	 (i.e. the current) instruction.  */
+      insns = VEC_length (btrace_insn_s, bfun->insn);
+      if (insns == 1)
+	bfun = bfun->flow.prev;
+
+      if (bfun == NULL)
+	return 0;
+
+      steps += 1;
+    }
+
+  while (steps < stride)
+    {
+      const struct btrace_function *prev;
+
+      prev = bfun->flow.prev;
+      if (prev == NULL)
+	break;
+
+      bfun = prev;
+      steps += 1;
+    }
+
+  it->function = bfun;
+  return steps;
+}
+
+/* See btrace.h.  */
+
+int
+btrace_call_cmp (const struct btrace_call_iterator *lhs,
+		 const struct btrace_call_iterator *rhs)
+{
+  unsigned int lnum, rnum;
+
+  lnum = btrace_call_number (lhs);
+  rnum = btrace_call_number (rhs);
+
+  return (int) (lnum - rnum);
+}
+
+/* See btrace.h.  */
+
+int
+btrace_find_call_by_number (struct btrace_call_iterator *it,
+			    const struct btrace_thread_info *btinfo,
+			    unsigned int number)
+{
+  const struct btrace_function *bfun;
+
+  for (bfun = btinfo->end; bfun != NULL; bfun = bfun->flow.prev)
+    {
+      unsigned int bnum;
+
+      bnum = bfun->number;
+      if (number == bnum)
+	{
+	  it->btinfo = btinfo;
+	  it->function = bfun;
+	  return 1;
+	}
+
+      /* Functions are ordered and numbered consecutively.  We could bail out
+	 earlier.  On the other hand, it is very unlikely that we search for
+	 a nonexistent function.  */
+  }
+
+  return 0;
+}
+
+/* See btrace.h.  */
+
+void
+btrace_set_insn_history (struct btrace_thread_info *btinfo,
+			 const struct btrace_insn_iterator *begin,
+			 const struct btrace_insn_iterator *end)
+{
+  if (btinfo->insn_history == NULL)
+    btinfo->insn_history = xzalloc (sizeof (*btinfo->insn_history));
+
+  btinfo->insn_history->begin = *begin;
+  btinfo->insn_history->end = *end;
+}
+
+/* See btrace.h.  */
+
+void
+btrace_set_call_history (struct btrace_thread_info *btinfo,
+			 const struct btrace_call_iterator *begin,
+			 const struct btrace_call_iterator *end)
+{
+  gdb_assert (begin->btinfo == end->btinfo);
+
+  if (btinfo->call_history == NULL)
+    btinfo->call_history = xzalloc (sizeof (*btinfo->call_history));
+
+  btinfo->call_history->begin = *begin;
+  btinfo->call_history->end = *end;
+}
diff --git a/gdb/btrace.h b/gdb/btrace.h
index 9bd7176..d219f69 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -29,63 +29,128 @@
 #include "btrace-common.h"
 
 struct thread_info;
+struct btrace_function;
 
 /* A branch trace instruction.
 
    This represents a single instruction in a branch trace.  */
-struct btrace_inst
+struct btrace_insn
 {
   /* The address of this instruction.  */
   CORE_ADDR pc;
 };
 
-/* A branch trace function.
+/* A vector of branch trace instructions.  */
+typedef struct btrace_insn btrace_insn_s;
+DEF_VEC_O (btrace_insn_s);
+
+/* A doubly-linked list of branch trace function segments.  */
+struct btrace_func_link
+{
+  struct btrace_function *prev;
+  struct btrace_function *next;
+};
+
+/* Flags for btrace function segments.  */
+enum btrace_function_flag
+{
+  /* The 'up' link interpretation.
+     If set, it points to the function segment we returned to.
+     If clear, it points to the function segment we called from.  */
+  BFUN_UP_LINKS_TO_RET = (1 << 0),
+
+  /* The 'up' link points to a tail call.  This obviously only makes sense
+     if bfun_up_links_to_ret is clear.  */
+  BFUN_UP_LINKS_TO_TAILCALL = (1 << 1)
+};
+
+/* A branch trace function segment.
 
    This represents a function segment in a branch trace, i.e. a consecutive
-   number of instructions belonging to the same function.  */
-struct btrace_func
+   number of instructions belonging to the same function.
+
+   We do not allow function segments without any instructions.  */
+struct btrace_function
 {
-  /* The full and minimal symbol for the function.  One of them may be NULL.  */
+  /* The full and minimal symbol for the function.  Both may be NULL.  */
   struct minimal_symbol *msym;
   struct symbol *sym;
 
+  /* The previous and next segment belonging to the same function.
+     If a function calls another function, the former will have at least
+     two segments: one before the call and another after the return.  */
+  struct btrace_func_link segment;
+
+  /* The previous and next function in control flow order.  */
+  struct btrace_func_link flow;
+
+  /* The directly preceding function segment in a (fake) call stack.  */
+  struct btrace_function *up;
+
+  /* The instructions in this function segment.
+     The instruction vector will never be empty.  */
+  VEC (btrace_insn_s) *insn;
+
+  /* The instruction number offset for the first instruction in this
+     function segment.  */
+  unsigned int insn_offset;
+
+  /* The function number in control-flow order.  */
+  unsigned int number;
+
+  /* The function level in a back trace across the entire branch trace.
+     A caller's level is one lower than the level of its callee.
+
+     Levels can be negative if we see returns for which we have not seen
+     the corresponding calls.  The branch trace thread information provides
+     a fixup to normalize function levels so the smallest level is zero.  */
+  int level;
+
   /* The source line range of this function segment (both inclusive).  */
   int lbegin, lend;
 
-  /* The instruction number range in the instruction trace corresponding
-     to this function segment (both inclusive).  */
-  unsigned int ibegin, iend;
+  /* A bit-vector of btrace_function_flag.  */
+  enum btrace_function_flag flags;
 };
 
-/* Branch trace may also be represented as a vector of:
+/* A branch trace instruction iterator.  */
+struct btrace_insn_iterator
+{
+  /* The branch trace function segment containing the instruction.
+     Will never be NULL.  */
+  const struct btrace_function *function;
 
-   - branch trace instructions starting with the oldest instruction.
-   - branch trace functions starting with the oldest function.  */
-typedef struct btrace_inst btrace_inst_s;
-typedef struct btrace_func btrace_func_s;
+  /* The index into the function segment's instruction vector.  */
+  unsigned int index;
+};
 
-/* Define functions operating on branch trace vectors.  */
-DEF_VEC_O (btrace_inst_s);
-DEF_VEC_O (btrace_func_s);
+/* A branch trace function call iterator.  */
+struct btrace_call_iterator
+{
+  /* The branch trace information for this thread.  Will never be NULL.  */
+  const struct btrace_thread_info *btinfo;
+
+  /* The branch trace function segment.
+     This will be NULL for the iterator pointing to the end of the trace.  */
+  const struct btrace_function *function;
+};
 
 /* Branch trace iteration state for "record instruction-history".  */
-struct btrace_insn_iterator
+struct btrace_insn_history
 {
-  /* The instruction index range from begin (inclusive) to end (exclusive)
-     that has been covered last time.
-     If end < begin, the branch trace has just been updated.  */
-  unsigned int begin;
-  unsigned int end;
+  /* The branch trace instruction range from BEGIN (inclusive) to
+     END (exclusive) that has been covered last time.  */
+  struct btrace_insn_iterator begin;
+  struct btrace_insn_iterator end;
 };
 
 /* Branch trace iteration state for "record function-call-history".  */
-struct btrace_func_iterator
+struct btrace_call_history
 {
-  /* The function index range from begin (inclusive) to end (exclusive)
-     that has been covered last time.
-     If end < begin, the branch trace has just been updated.  */
-  unsigned int begin;
-  unsigned int end;
+  /* The branch trace function range from BEGIN (inclusive) to END (exclusive)
+     that has been covered last time.  */
+  struct btrace_call_iterator begin;
+  struct btrace_call_iterator end;
 };
 
 /* Branch trace information per thread.
@@ -103,16 +168,25 @@ struct btrace_thread_info
      the underlying architecture.  */
   struct btrace_target_info *target;
 
-  /* The current branch trace for this thread.  */
-  VEC (btrace_block_s) *btrace;
-  VEC (btrace_inst_s) *itrace;
-  VEC (btrace_func_s) *ftrace;
+  /* The current branch trace for this thread (both inclusive).
+
+     The last instruction of END is the current instruction, which is not
+     part of the execution history.
+     Both will be NULL if there is no branch trace available.  If there is
+     branch trace available, both will be non-NULL.  */
+  struct btrace_function *begin;
+  struct btrace_function *end;
+
+  /* The function level offset.  When added to each function's LEVEL,
+     this normalizes the function levels such that the smallest level
+     becomes zero.  */
+  int level;
 
   /* The instruction history iterator.  */
-  struct btrace_insn_iterator insn_iterator;
+  struct btrace_insn_history *insn_history;
 
   /* The function call history iterator.  */
-  struct btrace_func_iterator func_iterator;
+  struct btrace_call_history *call_history;
 };
 
 /* Enable branch tracing for a thread.  */
@@ -139,4 +213,99 @@ extern void btrace_free_objfile (struct objfile *);
 /* Parse a branch trace xml document into a block vector.  */
 extern VEC (btrace_block_s) *parse_xml_btrace (const char*);
 
+/* Dereference a branch trace instruction iterator.  Return a pointer to the
+   instruction the iterator points to.  */
+extern const struct btrace_insn *
+  btrace_insn_get (const struct btrace_insn_iterator *);
+
+/* Return the instruction number for a branch trace iterator.
+   Returns one past the maximum instruction number for the end iterator.
+   Returns zero if the iterator does not point to a valid instruction.  */
+extern unsigned int btrace_insn_number (const struct btrace_insn_iterator *);
+
+/* Initialize a branch trace instruction iterator to point to the begin/end of
+   the branch trace.  Throws an error if there is no branch trace.  */
+extern void btrace_insn_begin (struct btrace_insn_iterator *,
+			       const struct btrace_thread_info *);
+extern void btrace_insn_end (struct btrace_insn_iterator *,
+			     const struct btrace_thread_info *);
+
+/* Increment/decrement a branch trace instruction iterator by at most STRIDE
+   instructions.  Return the number of instructions by which the instruction
+   iterator has been advanced.
+   Returns zero, if the operation failed or STRIDE had been zero.  */
+extern unsigned int btrace_insn_next (struct btrace_insn_iterator *,
+				      unsigned int stride);
+extern unsigned int btrace_insn_prev (struct btrace_insn_iterator *,
+				      unsigned int stride);
+
+/* Compare two branch trace instruction iterators.
+   Return a negative number if LHS < RHS.
+   Return zero if LHS == RHS.
+   Return a positive number if LHS > RHS.  */
+extern int btrace_insn_cmp (const struct btrace_insn_iterator *lhs,
+			    const struct btrace_insn_iterator *rhs);
+
+/* Find an instruction in the function branch trace by its number.
+   If the instruction is found, initialize the branch trace instruction
+   iterator to point to this instruction and return non-zero.
+   Return zero otherwise.  */
+extern int btrace_find_insn_by_number (struct btrace_insn_iterator *,
+				       const struct btrace_thread_info *,
+				       unsigned int number);
+
+/* Dereference a branch trace call iterator.  Return a pointer to the
+   function the iterator points to or NULL if the interator points past
+   the end of the branch trace.  */
+extern const struct btrace_function *
+  btrace_call_get (const struct btrace_call_iterator *);
+
+/* Return the function number for a branch trace call iterator.
+   Returns one past the maximum function number for the end iterator.
+   Returns zero if the iterator does not point to a valid function.  */
+extern unsigned int btrace_call_number (const struct btrace_call_iterator *);
+
+/* Initialize a branch trace call iterator to point to the begin/end of
+   the branch trace.  Throws an error if there is no branch trace.  */
+extern void btrace_call_begin (struct btrace_call_iterator *,
+			       const struct btrace_thread_info *);
+extern void btrace_call_end (struct btrace_call_iterator *,
+			     const struct btrace_thread_info *);
+
+/* Increment/decrement a branch trace call iterator by at most STRIDE function
+   segments.  Return the number of function segments by which the call
+   iterator has been advanced.
+   Returns zero, if the operation failed or STRIDE had been zero.  */
+extern unsigned int btrace_call_next (struct btrace_call_iterator *,
+				      unsigned int stride);
+extern unsigned int btrace_call_prev (struct btrace_call_iterator *,
+				      unsigned int stride);
+
+/* Compare two branch trace call iterators.
+   Return a negative number if LHS < RHS.
+   Return zero if LHS == RHS.
+   Return a positive number if LHS > RHS.  */
+extern int btrace_call_cmp (const struct btrace_call_iterator *lhs,
+			    const struct btrace_call_iterator *rhs);
+
+/* Find a function in the function branch trace by its NUMBER.
+   If the function is found, initialize the branch trace call
+   iterator to point to this function and return non-zero.
+   Return zero otherwise.  */
+extern int btrace_find_call_by_number (struct btrace_call_iterator *,
+				       const struct btrace_thread_info *,
+				       unsigned int number);
+
+/* Set the branch trace instruction history from BEGIN (inclusive) to
+   END (exclusive).  */
+extern void btrace_set_insn_history (struct btrace_thread_info *,
+				     const struct btrace_insn_iterator *begin,
+				     const struct btrace_insn_iterator *end);
+
+/* Set the branch trace function call history from BEGIN (inclusive) to
+   END (exclusive).  */
+extern void btrace_set_call_history (struct btrace_thread_info *,
+				     const struct btrace_call_iterator *begin,
+				     const struct btrace_call_iterator *end);
+
 #endif /* BTRACE_H */
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 5fd26e2..f4af346 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 7ee4e66..bf2458b 100644
--- a/gdb/testsuite/gdb.btrace/function_call_history.exp
+++ b/gdb/testsuite/gdb.btrace/function_call_history.exp
@@ -187,16 +187,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 "recursive"
-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" [join [list \
+  "20\tmain" \
+  "21\tfib" \
+  "22\tfib" \
+  "23\tfib" \
+  "24\tfib" \
+  "25\tfib" \
+  "26\tfib" \
+  "27\tfib" \
+  "28\tfib" \
+  "29\tfib" \
+  "30\tmain"] "\r\n"] "recursive"
diff --git a/gdb/testsuite/gdb.btrace/instruction_history.exp b/gdb/testsuite/gdb.btrace/instruction_history.exp
index 6048ba1..c6f6500 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
@@ -147,6 +147,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 } {
@@ -168,6 +170,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 } {
@@ -176,6 +180,8 @@ if { $lines != $history_size } {
     pass $message
 }
 
+set history_size 2
+gdb_test_no_output "set record instruction-history-size $history_size"
 set message "browse history backward middle"
 set lines [test_lines_length "record instruction-history -" $message]
 if { $lines != $history_size } {
-- 
1.8.3.1

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

* [PATCH v10 22/28] record-btrace: provide target_find_new_threads method
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (22 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 13/28] Add target_ops argument to to_prepare_to_store Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-14  8:05 ` [PATCH v10 15/28] frame, backtrace: allow targets to supply a frame unwinder Markus Metzger
                   ` (4 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +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.

2014-01-14  Markus Metzger  <markus.t.metzger@intel.com>

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


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

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 7701fc4..ba89bf4 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -1051,6 +1051,41 @@ 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;
+      }
+}
+
+/* The to_thread_alive method of target record-btrace.  */
+
+static int
+record_btrace_thread_alive (struct target_ops *ops, ptid_t ptid)
+{
+  /* We don't add or remove threads during replay.  */
+  if (record_btrace_is_replaying ())
+    return find_thread_ptid (ptid) != NULL;
+
+  /* Forward the request.  */
+  for (ops = ops->beneath; ops != NULL; ops = ops->beneath)
+    if (ops->to_thread_alive != NULL)
+      return ops->to_thread_alive (ops, ptid);
+
+  return 0;
+}
+
 /* Initialize the record-btrace target ops.  */
 
 static void
@@ -1087,6 +1122,8 @@ 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_thread_alive = record_btrace_thread_alive;
   ops->to_stratum = record_stratum;
   ops->to_magic = OPS_MAGIC;
 }
-- 
1.8.3.1

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

* [PATCH v10 19/28] target, breakpoint: allow insert/remove breakpoint to be forwarded
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (10 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 16/28] frame, cfa: check unwind stop reason first Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-15 15:52   ` Pedro Alves
  2014-01-14  8:05 ` [PATCH v10 02/28] btrace, linux: fix memory leak when reading branch trace Markus Metzger
                   ` (16 subsequent siblings)
  28 siblings, 1 reply; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

2014-01-14  Markus Metzger  <markus.t.metzger@intel.com>

	* target.h (target_ops) <to_insert_breakpoint>
	<to_remove_breakpoint>: Add target_ops parameter.
	(forward_target_insert_breakpoint): New.
	(forward_target_remove_breakpoint): New.
	(memory_remove_breakpoint, memory_insert_breakpoint):
	Add target_ops parameter.
	* target.c (target_insert_breakpoint): Split into this and ...
	(forward_target_insert_breakpoint): ... this.
	(target_remove_breakpoint): Split into this and ...
	(forward_target_remove_breakpoint): ... this.
	(debug_to_insert_breakpoint): Add target_ops parameter.
	Call forward_target_insert_breakpoint.
	(debug_to_remove_breakpoint): Add target_ops parameter.
	Call forward_target_remove_breakpoint.
	(update_current_target): Do not inherit or default to_insert_breakpoint
	and to_remove_breakpoint.
	* corelow.c (ignore): Add target_ops parameter.
	* exec.c (ignore): Add target_ops parameter.
	* mem-break.c (memory_insert_breakpoint, memory_remove_breakpoint):
	Add target_ops parameter.
	* monitor.c (monitor_insert_breakpoint, monitor_remove_breakpoint):
	Add target_ops parameter.
	* nto-procfs.c (procfs_insert_breakpoint, procfs_remove_breakpoint):
	Add target_ops parameter.
	* record-full.c (record_full_beneath_to_insert_breakpoint)
	(record_full_beneath_to_remove_breakpoint, tmp_to_insert_breakpoint)
	(tmp_to_remove_breakpoint, record_full_insert_breakpoint)
	(record_full_remove_breakpoint, record_full_core_insert_breakpoint)
	(record_full_core_remove_breakpoint): Add target_ops parameter.
	Update users.
	(record_full_beneath_to_insert_breakpoint_ops)
	(record_full_beneath_to_remove_breakpoint_ops)
	(tmp_to_insert_breakpoint_ops, tmp_to_remove_breakpoint_ops): New.
	(record_full_open): Initialize tmp_to_insert_breakpoint_ops,
	tmp_to_remove_breakpoint_ops,
	record_full_beneath_to_insert_breakpoint_ops, and
	record_full_beneath_to_remove_breakpoint_ops.
	* m32r-sdi.c (m32r_insert_breakpoint, m32r_remove_breakpoint):
	Add target_ops parameter.
	* remote-mips.c (mips_insert_breakpoint, mips_remove_breakpoint):
	Add target_ops parameter.
	* remote.c (remote_insert_breakpoint, remote_remove_breakpoint):
	Add target_ops parameter.


---
 gdb/corelow.c         |  3 ++-
 gdb/exec.c            |  3 ++-
 gdb/mem-break.c       |  4 ++--
 gdb/monitor.c         |  4 ++--
 gdb/nto-procfs.c      |  4 ++--
 gdb/record-full.c     | 48 ++++++++++++++++++++++++++++++-----------
 gdb/remote-m32r-sdi.c |  6 ++++--
 gdb/remote-mips.c     |  8 +++----
 gdb/remote.c          | 10 +++++----
 gdb/target.c          | 60 +++++++++++++++++++++++++++++++++++++--------------
 gdb/target.h          | 27 +++++++++++++++++++----
 11 files changed, 127 insertions(+), 50 deletions(-)

diff --git a/gdb/corelow.c b/gdb/corelow.c
index 8b20d3f..57a3b42 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -842,7 +842,8 @@ core_xfer_partial (struct target_ops *ops, enum target_object object,
    breakpoint_init_inferior).  */
 
 static int
-ignore (struct gdbarch *gdbarch, struct bp_target_info *bp_tgt)
+ignore (struct target_ops *ops, struct gdbarch *gdbarch,
+	struct bp_target_info *bp_tgt)
 {
   return 0;
 }
diff --git a/gdb/exec.c b/gdb/exec.c
index 2d2949f..1fe6031 100644
--- a/gdb/exec.c
+++ b/gdb/exec.c
@@ -801,7 +801,8 @@ exec_set_section_address (const char *filename, int index, CORE_ADDR address)
    breakpoint_init_inferior).  */
 
 static int
-ignore (struct gdbarch *gdbarch, struct bp_target_info *bp_tgt)
+ignore (struct target_ops *ops, struct gdbarch *gdbarch,
+	struct bp_target_info *bp_tgt)
 {
   return 0;
 }
diff --git a/gdb/mem-break.c b/gdb/mem-break.c
index c206687..1a057df 100644
--- a/gdb/mem-break.c
+++ b/gdb/mem-break.c
@@ -77,14 +77,14 @@ default_memory_remove_breakpoint (struct gdbarch *gdbarch,
 
 
 int
-memory_insert_breakpoint (struct gdbarch *gdbarch,
+memory_insert_breakpoint (struct target_ops *ops, struct gdbarch *gdbarch,
 			  struct bp_target_info *bp_tgt)
 {
   return gdbarch_memory_insert_breakpoint (gdbarch, bp_tgt);
 }
 
 int
-memory_remove_breakpoint (struct gdbarch *gdbarch,
+memory_remove_breakpoint (struct target_ops *ops, struct gdbarch *gdbarch,
 			  struct bp_target_info *bp_tgt)
 {
   return gdbarch_memory_remove_breakpoint (gdbarch, bp_tgt);
diff --git a/gdb/monitor.c b/gdb/monitor.c
index bbb06c6..ad1fd3e 100644
--- a/gdb/monitor.c
+++ b/gdb/monitor.c
@@ -2095,7 +2095,7 @@ monitor_mourn_inferior (struct target_ops *ops)
 /* Tell the monitor to add a breakpoint.  */
 
 static int
-monitor_insert_breakpoint (struct gdbarch *gdbarch,
+monitor_insert_breakpoint (struct target_ops *ops, struct gdbarch *gdbarch,
 			   struct bp_target_info *bp_tgt)
 {
   CORE_ADDR addr = bp_tgt->placed_address;
@@ -2132,7 +2132,7 @@ monitor_insert_breakpoint (struct gdbarch *gdbarch,
 /* Tell the monitor to remove a breakpoint.  */
 
 static int
-monitor_remove_breakpoint (struct gdbarch *gdbarch,
+monitor_remove_breakpoint (struct target_ops *ops, struct gdbarch *gdbarch,
 			   struct bp_target_info *bp_tgt)
 {
   CORE_ADDR addr = bp_tgt->placed_address;
diff --git a/gdb/nto-procfs.c b/gdb/nto-procfs.c
index c2d0d30..4669345 100644
--- a/gdb/nto-procfs.c
+++ b/gdb/nto-procfs.c
@@ -922,14 +922,14 @@ procfs_breakpoint (CORE_ADDR addr, int type, int size)
 }
 
 static int
-procfs_insert_breakpoint (struct gdbarch *gdbarch,
+procfs_insert_breakpoint (struct target_ops *ops, struct gdbarch *gdbarch,
 			  struct bp_target_info *bp_tgt)
 {
   return procfs_breakpoint (bp_tgt->placed_address, _DEBUG_BREAK_EXEC, 0);
 }
 
 static int
-procfs_remove_breakpoint (struct gdbarch *gdbarch,
+procfs_remove_breakpoint (struct target_ops *ops, struct gdbarch *gdbarch,
 			  struct bp_target_info *bp_tgt)
 {
   return procfs_breakpoint (bp_tgt->placed_address, _DEBUG_BREAK_EXEC, -1);
diff --git a/gdb/record-full.c b/gdb/record-full.c
index ce0504e..aef7665 100644
--- a/gdb/record-full.c
+++ b/gdb/record-full.c
@@ -230,11 +230,15 @@ static void (*record_full_beneath_to_store_registers) (struct target_ops *,
 static struct target_ops *record_full_beneath_to_xfer_partial_ops;
 static target_xfer_partial_ftype *record_full_beneath_to_xfer_partial;
 static int
-  (*record_full_beneath_to_insert_breakpoint) (struct gdbarch *,
+  (*record_full_beneath_to_insert_breakpoint) (struct target_ops *,
+					       struct gdbarch *,
 					       struct bp_target_info *);
+static struct target_ops *record_full_beneath_to_insert_breakpoint_ops;
 static int
-  (*record_full_beneath_to_remove_breakpoint) (struct gdbarch *,
+  (*record_full_beneath_to_remove_breakpoint) (struct target_ops *,
+					       struct gdbarch *,
 					       struct bp_target_info *);
+static struct target_ops *record_full_beneath_to_remove_breakpoint_ops;
 static int (*record_full_beneath_to_stopped_by_watchpoint) (void);
 static int (*record_full_beneath_to_stopped_data_address) (struct target_ops *,
 							   CORE_ADDR *);
@@ -801,10 +805,12 @@ static void (*tmp_to_store_registers) (struct target_ops *,
 				       int regno);
 static struct target_ops *tmp_to_xfer_partial_ops;
 static target_xfer_partial_ftype *tmp_to_xfer_partial;
-static int (*tmp_to_insert_breakpoint) (struct gdbarch *,
+static int (*tmp_to_insert_breakpoint) (struct target_ops *, struct gdbarch *,
 					struct bp_target_info *);
-static int (*tmp_to_remove_breakpoint) (struct gdbarch *,
+static struct target_ops *tmp_to_insert_breakpoint_ops;
+static int (*tmp_to_remove_breakpoint) (struct target_ops *, struct gdbarch *,
 					struct bp_target_info *);
+static struct target_ops *tmp_to_remove_breakpoint_ops;
 static int (*tmp_to_stopped_by_watchpoint) (void);
 static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *);
 static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *);
@@ -926,6 +932,8 @@ record_full_open (char *name, int from_tty)
   tmp_to_stopped_by_watchpoint = NULL;
   tmp_to_stopped_data_address = NULL;
   tmp_to_async = NULL;
+  tmp_to_insert_breakpoint_ops = NULL;
+  tmp_to_remove_breakpoint_ops = NULL;
 
   /* Set the beneath function pointers.  */
   for (t = current_target.beneath; t != NULL; t = t->beneath)
@@ -951,9 +959,15 @@ record_full_open (char *name, int from_tty)
 	  tmp_to_xfer_partial_ops = t;
         }
       if (!tmp_to_insert_breakpoint)
-	tmp_to_insert_breakpoint = t->to_insert_breakpoint;
+	{
+	  tmp_to_insert_breakpoint = t->to_insert_breakpoint;
+	  tmp_to_insert_breakpoint_ops = t;
+	}
       if (!tmp_to_remove_breakpoint)
-	tmp_to_remove_breakpoint = t->to_remove_breakpoint;
+	{
+	  tmp_to_remove_breakpoint = t->to_remove_breakpoint;
+	  tmp_to_remove_breakpoint_ops = t;
+	}
       if (!tmp_to_stopped_by_watchpoint)
 	tmp_to_stopped_by_watchpoint = t->to_stopped_by_watchpoint;
       if (!tmp_to_stopped_data_address)
@@ -980,7 +994,9 @@ record_full_open (char *name, int from_tty)
   record_full_beneath_to_xfer_partial_ops = tmp_to_xfer_partial_ops;
   record_full_beneath_to_xfer_partial = tmp_to_xfer_partial;
   record_full_beneath_to_insert_breakpoint = tmp_to_insert_breakpoint;
+  record_full_beneath_to_insert_breakpoint_ops = tmp_to_insert_breakpoint_ops;
   record_full_beneath_to_remove_breakpoint = tmp_to_remove_breakpoint;
+  record_full_beneath_to_remove_breakpoint_ops = tmp_to_remove_breakpoint_ops;
   record_full_beneath_to_stopped_by_watchpoint = tmp_to_stopped_by_watchpoint;
   record_full_beneath_to_stopped_data_address = tmp_to_stopped_data_address;
   record_full_beneath_to_async = tmp_to_async;
@@ -1745,7 +1761,8 @@ record_full_init_record_breakpoints (void)
    when recording.  */
 
 static int
-record_full_insert_breakpoint (struct gdbarch *gdbarch,
+record_full_insert_breakpoint (struct target_ops *ops,
+			       struct gdbarch *gdbarch,
 			       struct bp_target_info *bp_tgt)
 {
   struct record_full_breakpoint *bp;
@@ -1762,7 +1779,9 @@ record_full_insert_breakpoint (struct gdbarch *gdbarch,
       int ret;
 
       old_cleanups = record_full_gdb_operation_disable_set ();
-      ret = record_full_beneath_to_insert_breakpoint (gdbarch, bp_tgt);
+      ops = record_full_beneath_to_insert_breakpoint_ops;
+      ret = record_full_beneath_to_insert_breakpoint (ops, gdbarch,
+						      bp_tgt);
       do_cleanups (old_cleanups);
 
       if (ret != 0)
@@ -1782,7 +1801,8 @@ record_full_insert_breakpoint (struct gdbarch *gdbarch,
 /* "to_remove_breakpoint" method for process record target.  */
 
 static int
-record_full_remove_breakpoint (struct gdbarch *gdbarch,
+record_full_remove_breakpoint (struct target_ops *ops,
+			       struct gdbarch *gdbarch,
 			       struct bp_target_info *bp_tgt)
 {
   struct record_full_breakpoint *bp;
@@ -1802,7 +1822,9 @@ record_full_remove_breakpoint (struct gdbarch *gdbarch,
 	      int ret;
 
 	      old_cleanups = record_full_gdb_operation_disable_set ();
-	      ret = record_full_beneath_to_remove_breakpoint (gdbarch, bp_tgt);
+	      ops = record_full_beneath_to_remove_breakpoint_ops;
+	      ret = record_full_beneath_to_remove_breakpoint (ops, gdbarch,
+							      bp_tgt);
 	      do_cleanups (old_cleanups);
 
 	      if (ret != 0)
@@ -2252,7 +2274,8 @@ record_full_core_xfer_partial (struct target_ops *ops,
 /* "to_insert_breakpoint" method for prec over corefile.  */
 
 static int
-record_full_core_insert_breakpoint (struct gdbarch *gdbarch,
+record_full_core_insert_breakpoint (struct target_ops *ops,
+				    struct gdbarch *gdbarch,
 				    struct bp_target_info *bp_tgt)
 {
   return 0;
@@ -2261,7 +2284,8 @@ record_full_core_insert_breakpoint (struct gdbarch *gdbarch,
 /* "to_remove_breakpoint" method for prec over corefile.  */
 
 static int
-record_full_core_remove_breakpoint (struct gdbarch *gdbarch,
+record_full_core_remove_breakpoint (struct target_ops *ops,
+				    struct gdbarch *gdbarch,
 				    struct bp_target_info *bp_tgt)
 {
   return 0;
diff --git a/gdb/remote-m32r-sdi.c b/gdb/remote-m32r-sdi.c
index 9364b5f..d722876 100644
--- a/gdb/remote-m32r-sdi.c
+++ b/gdb/remote-m32r-sdi.c
@@ -1144,7 +1144,8 @@ m32r_mourn_inferior (struct target_ops *ops)
 }
 
 static int
-m32r_insert_breakpoint (struct gdbarch *gdbarch,
+m32r_insert_breakpoint (struct target_ops *ops,
+			struct gdbarch *gdbarch,
 			struct bp_target_info *bp_tgt)
 {
   CORE_ADDR addr = bp_tgt->placed_address;
@@ -1188,7 +1189,8 @@ m32r_insert_breakpoint (struct gdbarch *gdbarch,
 }
 
 static int
-m32r_remove_breakpoint (struct gdbarch *gdbarch,
+m32r_remove_breakpoint (struct target_ops *ops,
+			struct gdbarch *gdbarch,
 			struct bp_target_info *bp_tgt)
 {
   CORE_ADDR addr = bp_tgt->placed_address;
diff --git a/gdb/remote-mips.c b/gdb/remote-mips.c
index adf2643..6e7d956 100644
--- a/gdb/remote-mips.c
+++ b/gdb/remote-mips.c
@@ -2363,27 +2363,27 @@ mips_mourn_inferior (struct target_ops *ops)
    target contents.  */
 
 static int
-mips_insert_breakpoint (struct gdbarch *gdbarch,
+mips_insert_breakpoint (struct target_ops *ops, struct gdbarch *gdbarch,
 			struct bp_target_info *bp_tgt)
 {
   if (monitor_supports_breakpoints)
     return mips_set_breakpoint (bp_tgt->placed_address, MIPS_INSN32_SIZE,
 				BREAK_FETCH);
   else
-    return memory_insert_breakpoint (gdbarch, bp_tgt);
+    return memory_insert_breakpoint (ops, gdbarch, bp_tgt);
 }
 
 /* Remove a breakpoint.  */
 
 static int
-mips_remove_breakpoint (struct gdbarch *gdbarch,
+mips_remove_breakpoint (struct target_ops *ops, struct gdbarch *gdbarch,
 			struct bp_target_info *bp_tgt)
 {
   if (monitor_supports_breakpoints)
     return mips_clear_breakpoint (bp_tgt->placed_address, MIPS_INSN32_SIZE,
 				  BREAK_FETCH);
   else
-    return memory_remove_breakpoint (gdbarch, bp_tgt);
+    return memory_remove_breakpoint (ops, gdbarch, bp_tgt);
 }
 
 /* Tell whether this target can support a hardware breakpoint.  CNT
diff --git a/gdb/remote.c b/gdb/remote.c
index 4370f6d..0eda325 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -8258,7 +8258,8 @@ remote_add_target_side_commands (struct gdbarch *gdbarch,
    which don't, we insert a traditional memory breakpoint.  */
 
 static int
-remote_insert_breakpoint (struct gdbarch *gdbarch,
+remote_insert_breakpoint (struct target_ops *ops,
+			  struct gdbarch *gdbarch,
 			  struct bp_target_info *bp_tgt)
 {
   /* Try the "Z" s/w breakpoint packet if it is not already disabled.
@@ -8320,11 +8321,12 @@ remote_insert_breakpoint (struct gdbarch *gdbarch,
     throw_error (NOT_SUPPORTED_ERROR, _("\
 Target doesn't support breakpoints that have target side commands."));
 
-  return memory_insert_breakpoint (gdbarch, bp_tgt);
+  return memory_insert_breakpoint (ops, gdbarch, bp_tgt);
 }
 
 static int
-remote_remove_breakpoint (struct gdbarch *gdbarch,
+remote_remove_breakpoint (struct target_ops *ops,
+			  struct gdbarch *gdbarch,
 			  struct bp_target_info *bp_tgt)
 {
   CORE_ADDR addr = bp_tgt->placed_address;
@@ -8354,7 +8356,7 @@ remote_remove_breakpoint (struct gdbarch *gdbarch,
       return (rs->buf[0] == 'E');
     }
 
-  return memory_remove_breakpoint (gdbarch, bp_tgt);
+  return memory_remove_breakpoint (ops, gdbarch, bp_tgt);
 }
 
 static int
diff --git a/gdb/target.c b/gdb/target.c
index 1a4b156..36e23aa 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -90,10 +90,10 @@ static void debug_to_prepare_to_store (struct target_ops *self,
 
 static void debug_to_files_info (struct target_ops *);
 
-static int debug_to_insert_breakpoint (struct gdbarch *,
+static int debug_to_insert_breakpoint (struct target_ops *, struct gdbarch *,
 				       struct bp_target_info *);
 
-static int debug_to_remove_breakpoint (struct gdbarch *,
+static int debug_to_remove_breakpoint (struct target_ops *, struct gdbarch *,
 				       struct bp_target_info *);
 
 static int debug_to_can_use_hw_breakpoint (int, int, int);
@@ -585,8 +585,8 @@ update_current_target (void)
       INHERIT (to_prepare_to_store, t);
       INHERIT (deprecated_xfer_memory, t);
       INHERIT (to_files_info, t);
-      INHERIT (to_insert_breakpoint, t);
-      INHERIT (to_remove_breakpoint, t);
+      /* Do not inherit to_insert_breakpoint.  */
+      /* Do not inherit to_remove_breakpoint.  */
       INHERIT (to_can_use_hw_breakpoint, t);
       INHERIT (to_insert_hw_breakpoint, t);
       INHERIT (to_remove_hw_breakpoint, t);
@@ -726,10 +726,6 @@ update_current_target (void)
   de_fault (to_files_info,
 	    (void (*) (struct target_ops *))
 	    target_ignore);
-  de_fault (to_insert_breakpoint,
-	    memory_insert_breakpoint);
-  de_fault (to_remove_breakpoint,
-	    memory_remove_breakpoint);
   de_fault (to_can_use_hw_breakpoint,
 	    (int (*) (int, int, int))
 	    return_zero);
@@ -2457,6 +2453,22 @@ get_target_memory_unsigned (struct target_ops *ops, CORE_ADDR addr,
   return extract_unsigned_integer (buf, len, byte_order);
 }
 
+/* See target.h.  */
+
+int
+forward_target_insert_breakpoint (struct target_ops *ops,
+				  struct gdbarch *gdbarch,
+				  struct bp_target_info *bp_tgt)
+{
+  for (; ops != NULL; ops = ops->beneath)
+    if (ops->to_insert_breakpoint != NULL)
+      return ops->to_insert_breakpoint (ops, gdbarch, bp_tgt);
+
+  return memory_insert_breakpoint (ops, gdbarch, bp_tgt);
+}
+
+/* See target.h.  */
+
 int
 target_insert_breakpoint (struct gdbarch *gdbarch,
 			  struct bp_target_info *bp_tgt)
@@ -2467,12 +2479,15 @@ target_insert_breakpoint (struct gdbarch *gdbarch,
       return 1;
     }
 
-  return (*current_target.to_insert_breakpoint) (gdbarch, bp_tgt);
+  return forward_target_insert_breakpoint (&current_target, gdbarch, bp_tgt);
 }
 
+/* See target.h.  */
+
 int
-target_remove_breakpoint (struct gdbarch *gdbarch,
-			  struct bp_target_info *bp_tgt)
+forward_target_remove_breakpoint (struct target_ops *ops,
+				  struct gdbarch *gdbarch,
+				  struct bp_target_info *bp_tgt)
 {
   /* This is kind of a weird case to handle, but the permission might
      have been changed after breakpoints were inserted - in which case
@@ -2484,7 +2499,20 @@ target_remove_breakpoint (struct gdbarch *gdbarch,
       return 1;
     }
 
-  return (*current_target.to_remove_breakpoint) (gdbarch, bp_tgt);
+  for (; ops != NULL; ops = ops->beneath)
+    if (ops->to_remove_breakpoint != NULL)
+      return ops->to_remove_breakpoint (ops, gdbarch, bp_tgt);
+
+  return memory_remove_breakpoint (ops, gdbarch, bp_tgt);
+}
+
+/* See target.h.  */
+
+int
+target_remove_breakpoint (struct gdbarch *gdbarch,
+			  struct bp_target_info *bp_tgt)
+{
+  return forward_target_remove_breakpoint (&current_target, gdbarch, bp_tgt);
 }
 
 static void
@@ -4549,12 +4577,12 @@ debug_to_files_info (struct target_ops *target)
 }
 
 static int
-debug_to_insert_breakpoint (struct gdbarch *gdbarch,
+debug_to_insert_breakpoint (struct target_ops *ops, struct gdbarch *gdbarch,
 			    struct bp_target_info *bp_tgt)
 {
   int retval;
 
-  retval = debug_target.to_insert_breakpoint (gdbarch, bp_tgt);
+  retval = forward_target_insert_breakpoint (&debug_target, gdbarch, bp_tgt);
 
   fprintf_unfiltered (gdb_stdlog,
 		      "target_insert_breakpoint (%s, xxx) = %ld\n",
@@ -4564,12 +4592,12 @@ debug_to_insert_breakpoint (struct gdbarch *gdbarch,
 }
 
 static int
-debug_to_remove_breakpoint (struct gdbarch *gdbarch,
+debug_to_remove_breakpoint (struct target_ops *ops, struct gdbarch *gdbarch,
 			    struct bp_target_info *bp_tgt)
 {
   int retval;
 
-  retval = debug_target.to_remove_breakpoint (gdbarch, bp_tgt);
+  retval = forward_target_remove_breakpoint (&debug_target, gdbarch, bp_tgt);
 
   fprintf_unfiltered (gdb_stdlog,
 		      "target_remove_breakpoint (%s, xxx) = %ld\n",
diff --git a/gdb/target.h b/gdb/target.h
index 47eedeb..224a575 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -403,8 +403,10 @@ struct target_ops
 				   struct target_ops *target);
 
     void (*to_files_info) (struct target_ops *);
-    int (*to_insert_breakpoint) (struct gdbarch *, struct bp_target_info *);
-    int (*to_remove_breakpoint) (struct gdbarch *, struct bp_target_info *);
+    int (*to_insert_breakpoint) (struct target_ops *, struct gdbarch *,
+				 struct bp_target_info *);
+    int (*to_remove_breakpoint) (struct target_ops *, struct gdbarch *,
+				 struct bp_target_info *);
     int (*to_can_use_hw_breakpoint) (int, int, int);
     int (*to_ranged_break_num_registers) (struct target_ops *);
     int (*to_insert_hw_breakpoint) (struct gdbarch *, struct bp_target_info *);
@@ -1135,12 +1137,29 @@ int target_write_memory_blocks (VEC(memory_write_request_s) *requests,
 /* Insert a hardware breakpoint at address BP_TGT->placed_address in
    the target machine.  Returns 0 for success, and returns non-zero or
    throws an error (with a detailed failure reason error code and
+   message) otherwise.
+   Start the target search at OPS.  */
+
+extern int forward_target_insert_breakpoint (struct target_ops *ops,
+					     struct gdbarch *gdbarch,
+					     struct bp_target_info *bp_tgt);
+
+/* Insert a hardware breakpoint at address BP_TGT->placed_address in
+   the target machine.  Returns 0 for success, and returns non-zero or
+   throws an error (with a detailed failure reason error code and
    message) otherwise.  */
 
 extern int target_insert_breakpoint (struct gdbarch *gdbarch,
 				     struct bp_target_info *bp_tgt);
 
 /* Remove a breakpoint at address BP_TGT->placed_address in the target
+   machine.  Result is 0 for success, non-zero for error.
+   Start the target search at OPS.  */
+
+extern int forward_target_remove_breakpoint (struct target_ops *ops,
+					     struct gdbarch *gdbarch,
+					     struct bp_target_info *bp_tgt);
+/* Remove a breakpoint at address BP_TGT->placed_address in the target
    machine.  Result is 0 for success, non-zero for error.  */
 
 extern int target_remove_breakpoint (struct gdbarch *gdbarch,
@@ -1892,10 +1911,10 @@ extern struct target_section_table *target_get_section_table
 
 /* From mem-break.c */
 
-extern int memory_remove_breakpoint (struct gdbarch *,
+extern int memory_remove_breakpoint (struct target_ops *, struct gdbarch *,
 				     struct bp_target_info *);
 
-extern int memory_insert_breakpoint (struct gdbarch *,
+extern int memory_insert_breakpoint (struct target_ops *, struct gdbarch *,
 				     struct bp_target_info *);
 
 extern int default_memory_remove_breakpoint (struct gdbarch *,
-- 
1.8.3.1

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

* [PATCH v10 23/28] record-btrace: add record goto target methods
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (14 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 11/28] record-btrace: make ranges include begin and end Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-14  8:05 ` [PATCH v10 17/28] frame: do not assume unwinding will succeed Markus Metzger
                   ` (12 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

Reviewed-by: Eli Zaretskii

2014-01-14  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   | 170 ++++++++++++++
 gdb/testsuite/gdb.btrace/x86-record_goto.S | 355 +++++++++++++++++++++++++++++
 6 files changed, 699 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 94e7a98..a031aba 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -113,6 +113,8 @@ info exceptions REGEXP
   debugged.  If provided, only the exceptions whose names match REGEXP
   are listed.
 
+* The btrace record target now supports the 'record goto' command.
+
 * New options
 
 set debug symfile off|on
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index ba89bf4..ce62cd5 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -58,13 +58,13 @@ static int record_btrace_allow_memory_access;
 
 
 /* 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;
@@ -82,7 +82,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.  */
@@ -1086,6 +1102,103 @@ record_btrace_thread_alive (struct target_ops *ops, ptid_t ptid)
   return 0;
 }
 
+/* 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
@@ -1124,6 +1237,9 @@ init_record_btrace_ops (void)
   ops->to_wait = record_btrace_wait;
   ops->to_find_new_threads = record_btrace_find_new_threads;
   ops->to_thread_alive = record_btrace_thread_alive;
+  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 606de6e..8311cba 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 unknown_functions
+  exception unknown_functions 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..eb07b9b
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/record_goto.exp
@@ -0,0 +1,170 @@
+# 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" [join [list \
+  "1\tfun4\tinst 1,3" \
+  "2\t  fun1\tinst 4,7" \
+  "3\tfun4\tinst 8,8" \
+  "4\t  fun2\tinst 9,11" \
+  "5\t    fun1\tinst 12,15" \
+  "6\t  fun2\tinst 16,17" \
+  "7\tfun4\tinst 18,18" \
+  "8\t  fun3\tinst 19,21" \
+  "9\t    fun1\tinst 22,25" \
+  "10\t  fun3\tinst 26,26" \
+  "11\t    fun2\tinst 27,29" \
+  "12\t      fun1\tinst 30,33" \
+  "13\t    fun2\tinst 34,35" \
+  "14\t  fun3\tinst 36,37" \
+  "15\tfun4\tinst 38,39" \
+  ] "\r\n"]
+
+# let's see if we can go back in history
+gdb_test "record goto 18" ".*fun4 \\(\\) at record_goto.c:43.*"
+
+# the function call history should start at the new location
+gdb_test "record function-call-history /ci" [join [list \
+  "7\tfun4\tinst 18,18" \
+  "8\t  fun3\tinst 19,21" \
+  "9\t    fun1\tinst 22,25" \
+  ] "\r\n"] "function-call-history from 18 forwards"
+
+# the instruction history should start at the new location
+gdb_test "record instruction-history" [join [list \
+  "18.*" \
+  "19.*" \
+  "20.*" \
+  ] "\r\n"] "instruction-history from 18 forwards"
+
+# let's go to another place in the history
+gdb_test "record goto 26" ".*fun3 \\(\\) at record_goto.c:35.*"
+
+# the function call history should start at the new location
+gdb_test "record function-call-history /ci -" [join [list \
+  "8\t  fun3\tinst 19,21" \
+  "9\t    fun1\tinst 22,25" \
+  "10\t  fun3\tinst 26,26\r" \
+  ] "\r\n"] "function-call-history from 26 backwards"
+
+# the instruction history should start at the new location
+gdb_test "record instruction-history -" [join [list \
+  "24.*" \
+  "25.*" \
+  "26.*\r" \
+  ] "\r\n"] "instruction-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.*"
+
+# check that we're filling up the context correctly
+gdb_test "record function-call-history /ci -" [join [list \
+  "1\tfun4\tinst 1,3" \
+  "2\t  fun1\tinst 4,7" \
+  "3\tfun4\tinst 8,8" \
+  ] "\r\n"] "function-call-history from begin backwards"
+
+# check that we're filling up the context correctly
+gdb_test "record instruction-history -" [join [list \
+  "1.*" \
+  "2.*" \
+  "3.*" \
+  ] "\r\n"] "instruction-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.*"
+
+# check that we're filling up the context correctly
+gdb_test "record function-call-history /ci -" [join [list \
+  "1\tfun4\tinst 1,3" \
+  "2\t  fun1\tinst 4,7" \
+  "3\tfun4\tinst 8,8" \
+  ] "\r\n"] "function-call-history from 2 backwards"
+
+# check that we're filling up the context correctly
+gdb_test "record instruction-history -" [join [list \
+  "1.*" \
+  "2.*" \
+  "3.*" \
+  ] "\r\n"] "instruction-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.*"
+
+# check that we're filling up the context correctly
+gdb_test "record function-call-history /ci" [join [list \
+  "13\t    fun2\tinst 34,35" \
+  "14\t  fun3\tinst 36,37" \
+  "15\tfun4\tinst 38,39" \
+  ] "\r\n"] "function-call-history from end forwards"
+
+# check that we're filling up the context correctly
+gdb_test "record instruction-history" [join [list \
+  "37.*" \
+  "38.*" \
+  "39.*" \
+  ] "\r\n"] "instruction-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.*"
+
+# check that we're filling up the context correctly
+gdb_test "record function-call-history /ci" [join [list \
+  "13\t    fun2\tinst 34,35" \
+  "14\t  fun3\tinst 36,37" \
+  "15\tfun4\tinst 38,39" \
+  ] "\r\n"] "function-call-history from 38 forwards"
+
+# check that we're filling up the context correctly
+gdb_test "record instruction-history" [join [list \
+  "37.*" \
+  "38.*" \
+  "39.*" \
+  ] "\r\n"] "instruction-history from 38 forwards"
diff --git a/gdb/testsuite/gdb.btrace/x86-record_goto.S b/gdb/testsuite/gdb.btrace/x86-record_goto.S
new file mode 100644
index 0000000..08d1ce7
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/x86-record_goto.S
@@ -0,0 +1,355 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2013 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <markus.t.metzger@intel.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+   This file has been generated using:
+   gcc -S -dA -g record_goto.c -o x86-record_goto.S  */
+
+	.file	"record_goto.c"
+	.section	.debug_abbrev,"",@progbits
+.Ldebug_abbrev0:
+	.section	.debug_info,"",@progbits
+.Ldebug_info0:
+	.section	.debug_line,"",@progbits
+.Ldebug_line0:
+	.text
+.Ltext0:
+.globl fun1
+	.type	fun1, @function
+fun1:
+.LFB0:
+	.file 1 "record_goto.c"
+	# record_goto.c:22
+	.loc 1 22 0
+	.cfi_startproc
+	# basic block 2
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	movq	%rsp, %rbp
+	.cfi_offset 6, -16
+	.cfi_def_cfa_register 6
+	# record_goto.c:23
+	.loc 1 23 0
+	leave
+	.cfi_def_cfa 7, 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	fun1, .-fun1
+.globl fun2
+	.type	fun2, @function
+fun2:
+.LFB1:
+	# record_goto.c:27
+	.loc 1 27 0
+	.cfi_startproc
+	# basic block 2
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	movq	%rsp, %rbp
+	.cfi_offset 6, -16
+	.cfi_def_cfa_register 6
+	# record_goto.c:28
+	.loc 1 28 0
+	call	fun1
+	# record_goto.c:29
+	.loc 1 29 0
+	leave
+	.cfi_def_cfa 7, 8
+	ret
+	.cfi_endproc
+.LFE1:
+	.size	fun2, .-fun2
+.globl fun3
+	.type	fun3, @function
+fun3:
+.LFB2:
+	# record_goto.c:33
+	.loc 1 33 0
+	.cfi_startproc
+	# basic block 2
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	movq	%rsp, %rbp
+	.cfi_offset 6, -16
+	.cfi_def_cfa_register 6
+	# record_goto.c:34
+	.loc 1 34 0
+	call	fun1
+	# record_goto.c:35
+	.loc 1 35 0
+	call	fun2
+	# record_goto.c:36
+	.loc 1 36 0
+	leave
+	.cfi_def_cfa 7, 8
+	ret
+	.cfi_endproc
+.LFE2:
+	.size	fun3, .-fun3
+.globl fun4
+	.type	fun4, @function
+fun4:
+.LFB3:
+	# record_goto.c:40
+	.loc 1 40 0
+	.cfi_startproc
+	# basic block 2
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	movq	%rsp, %rbp
+	.cfi_offset 6, -16
+	.cfi_def_cfa_register 6
+	# record_goto.c:41
+	.loc 1 41 0
+	call	fun1
+	# record_goto.c:42
+	.loc 1 42 0
+	call	fun2
+	# record_goto.c:43
+	.loc 1 43 0
+	call	fun3
+	# record_goto.c:44
+	.loc 1 44 0
+	leave
+	.cfi_def_cfa 7, 8
+	ret
+	.cfi_endproc
+.LFE3:
+	.size	fun4, .-fun4
+.globl main
+	.type	main, @function
+main:
+.LFB4:
+	# record_goto.c:48
+	.loc 1 48 0
+	.cfi_startproc
+	# basic block 2
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	movq	%rsp, %rbp
+	.cfi_offset 6, -16
+	.cfi_def_cfa_register 6
+	# record_goto.c:49
+	.loc 1 49 0
+	call	fun4
+	# record_goto.c:50
+	.loc 1 50 0
+	movl	$0, %eax
+	# record_goto.c:51
+	.loc 1 51 0
+	leave
+	.cfi_def_cfa 7, 8
+	ret
+	.cfi_endproc
+.LFE4:
+	.size	main, .-main
+.Letext0:
+	.section	.debug_info
+	.long	0xbc	# Length of Compilation Unit Info
+	.value	0x3	# DWARF version number
+	.long	.Ldebug_abbrev0	# Offset Into Abbrev. Section
+	.byte	0x8	# Pointer Size (in bytes)
+	.uleb128 0x1	# (DIE (0xb) DW_TAG_compile_unit)
+	.long	.LASF4	# DW_AT_producer: "GNU C 4.4.4 20100726 (Red Hat 4.4.4-13)"
+	.byte	0x1	# DW_AT_language
+	.long	.LASF5	# DW_AT_name: "record_goto.c"
+	.long	.LASF6	# DW_AT_comp_dir: ""
+	.quad	.Ltext0	# DW_AT_low_pc
+	.quad	.Letext0	# DW_AT_high_pc
+	.long	.Ldebug_line0	# DW_AT_stmt_list
+	.uleb128 0x2	# (DIE (0x2d) DW_TAG_subprogram)
+	.byte	0x1	# DW_AT_external
+	.long	.LASF0	# DW_AT_name: "fun1"
+	.byte	0x1	# DW_AT_decl_file (record_goto.c)
+	.byte	0x15	# DW_AT_decl_line
+	.byte	0x1	# DW_AT_prototyped
+	.quad	.LFB0	# DW_AT_low_pc
+	.quad	.LFE0	# DW_AT_high_pc
+	.byte	0x1	# DW_AT_frame_base
+	.byte	0x9c	# DW_OP_call_frame_cfa
+	.uleb128 0x2	# (DIE (0x48) DW_TAG_subprogram)
+	.byte	0x1	# DW_AT_external
+	.long	.LASF1	# DW_AT_name: "fun2"
+	.byte	0x1	# DW_AT_decl_file (record_goto.c)
+	.byte	0x1a	# DW_AT_decl_line
+	.byte	0x1	# DW_AT_prototyped
+	.quad	.LFB1	# DW_AT_low_pc
+	.quad	.LFE1	# DW_AT_high_pc
+	.byte	0x1	# DW_AT_frame_base
+	.byte	0x9c	# DW_OP_call_frame_cfa
+	.uleb128 0x2	# (DIE (0x63) DW_TAG_subprogram)
+	.byte	0x1	# DW_AT_external
+	.long	.LASF2	# DW_AT_name: "fun3"
+	.byte	0x1	# DW_AT_decl_file (record_goto.c)
+	.byte	0x20	# DW_AT_decl_line
+	.byte	0x1	# DW_AT_prototyped
+	.quad	.LFB2	# DW_AT_low_pc
+	.quad	.LFE2	# DW_AT_high_pc
+	.byte	0x1	# DW_AT_frame_base
+	.byte	0x9c	# DW_OP_call_frame_cfa
+	.uleb128 0x2	# (DIE (0x7e) DW_TAG_subprogram)
+	.byte	0x1	# DW_AT_external
+	.long	.LASF3	# DW_AT_name: "fun4"
+	.byte	0x1	# DW_AT_decl_file (record_goto.c)
+	.byte	0x27	# DW_AT_decl_line
+	.byte	0x1	# DW_AT_prototyped
+	.quad	.LFB3	# DW_AT_low_pc
+	.quad	.LFE3	# DW_AT_high_pc
+	.byte	0x1	# DW_AT_frame_base
+	.byte	0x9c	# DW_OP_call_frame_cfa
+	.uleb128 0x3	# (DIE (0x99) DW_TAG_subprogram)
+	.byte	0x1	# DW_AT_external
+	.long	.LASF7	# DW_AT_name: "main"
+	.byte	0x1	# DW_AT_decl_file (record_goto.c)
+	.byte	0x2f	# DW_AT_decl_line
+	.byte	0x1	# DW_AT_prototyped
+	.long	0xb8	# DW_AT_type
+	.quad	.LFB4	# DW_AT_low_pc
+	.quad	.LFE4	# DW_AT_high_pc
+	.byte	0x1	# DW_AT_frame_base
+	.byte	0x9c	# DW_OP_call_frame_cfa
+	.uleb128 0x4	# (DIE (0xb8) DW_TAG_base_type)
+	.byte	0x4	# DW_AT_byte_size
+	.byte	0x5	# DW_AT_encoding
+	.ascii "int\0"	# DW_AT_name
+	.byte	0x0	# end of children of DIE 0xb
+	.section	.debug_abbrev
+	.uleb128 0x1	# (abbrev code)
+	.uleb128 0x11	# (TAG: DW_TAG_compile_unit)
+	.byte	0x1	# DW_children_yes
+	.uleb128 0x25	# (DW_AT_producer)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x13	# (DW_AT_language)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x1b	# (DW_AT_comp_dir)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x11	# (DW_AT_low_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x12	# (DW_AT_high_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x10	# (DW_AT_stmt_list)
+	.uleb128 0x6	# (DW_FORM_data4)
+	.byte	0x0
+	.byte	0x0
+	.uleb128 0x2	# (abbrev code)
+	.uleb128 0x2e	# (TAG: DW_TAG_subprogram)
+	.byte	0x0	# DW_children_no
+	.uleb128 0x3f	# (DW_AT_external)
+	.uleb128 0xc	# (DW_FORM_flag)
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x3a	# (DW_AT_decl_file)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3b	# (DW_AT_decl_line)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x27	# (DW_AT_prototyped)
+	.uleb128 0xc	# (DW_FORM_flag)
+	.uleb128 0x11	# (DW_AT_low_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x12	# (DW_AT_high_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x40	# (DW_AT_frame_base)
+	.uleb128 0xa	# (DW_FORM_block1)
+	.byte	0x0
+	.byte	0x0
+	.uleb128 0x3	# (abbrev code)
+	.uleb128 0x2e	# (TAG: DW_TAG_subprogram)
+	.byte	0x0	# DW_children_no
+	.uleb128 0x3f	# (DW_AT_external)
+	.uleb128 0xc	# (DW_FORM_flag)
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0xe	# (DW_FORM_strp)
+	.uleb128 0x3a	# (DW_AT_decl_file)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3b	# (DW_AT_decl_line)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x27	# (DW_AT_prototyped)
+	.uleb128 0xc	# (DW_FORM_flag)
+	.uleb128 0x49	# (DW_AT_type)
+	.uleb128 0x13	# (DW_FORM_ref4)
+	.uleb128 0x11	# (DW_AT_low_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x12	# (DW_AT_high_pc)
+	.uleb128 0x1	# (DW_FORM_addr)
+	.uleb128 0x40	# (DW_AT_frame_base)
+	.uleb128 0xa	# (DW_FORM_block1)
+	.byte	0x0
+	.byte	0x0
+	.uleb128 0x4	# (abbrev code)
+	.uleb128 0x24	# (TAG: DW_TAG_base_type)
+	.byte	0x0	# DW_children_no
+	.uleb128 0xb	# (DW_AT_byte_size)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3e	# (DW_AT_encoding)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0x8	# (DW_FORM_string)
+	.byte	0x0
+	.byte	0x0
+	.byte	0x0
+	.section	.debug_pubnames,"",@progbits
+	.long	0x3b	# Length of Public Names Info
+	.value	0x2	# DWARF Version
+	.long	.Ldebug_info0	# Offset of Compilation Unit Info
+	.long	0xc0	# Compilation Unit Length
+	.long	0x2d	# DIE offset
+	.ascii "fun1\0"	# external name
+	.long	0x48	# DIE offset
+	.ascii "fun2\0"	# external name
+	.long	0x63	# DIE offset
+	.ascii "fun3\0"	# external name
+	.long	0x7e	# DIE offset
+	.ascii "fun4\0"	# external name
+	.long	0x99	# DIE offset
+	.ascii "main\0"	# external name
+	.long	0x0
+	.section	.debug_aranges,"",@progbits
+	.long	0x2c	# Length of Address Ranges Info
+	.value	0x2	# DWARF Version
+	.long	.Ldebug_info0	# Offset of Compilation Unit Info
+	.byte	0x8	# Size of Address
+	.byte	0x0	# Size of Segment Descriptor
+	.value	0x0	# Pad to 16 byte boundary
+	.value	0x0
+	.quad	.Ltext0	# Address
+	.quad	.Letext0-.Ltext0	# Length
+	.quad	0x0
+	.quad	0x0
+	.section	.debug_str,"MS",@progbits,1
+.LASF3:
+	.string	"fun4"
+.LASF5:
+	.string	"record_goto.c"
+.LASF4:
+	.string	"GNU C 4.4.4 20100726 (Red Hat 4.4.4-13)"
+.LASF7:
+	.string	"main"
+.LASF1:
+	.string	"fun2"
+.LASF0:
+	.string	"fun1"
+.LASF6:
+	.string	""
+.LASF2:
+	.string	"fun3"
+	.ident	"GCC: (GNU) 4.4.4 20100726 (Red Hat 4.4.4-13)"
+	.section	.note.GNU-stack,"",@progbits
-- 
1.8.3.1

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

* [PATCH v10 11/28] record-btrace: make ranges include begin and end
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (13 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 04/28] gdbarch: add instruction predicate methods Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-14  8:05 ` [PATCH v10 23/28] record-btrace: add record goto target methods Markus Metzger
                   ` (13 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

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

Reviewed-by: Eli Zaretskii

2014-01-14  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/NEWS                                           |  3 ++
 gdb/doc/gdb.texinfo                                |  5 ++-
 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   |  8 +++--
 6 files changed, 43 insertions(+), 20 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 8f0e6e6..94e7a98 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -51,6 +51,9 @@
   Both ranges are now printed as '<from>, <to>' to allow copy&paste to the
   "record instruction-history" and "list" commands.
 
+* The ranges given as arguments to the 'record function-call-history' and
+  'record instruction-history' commands are now inclusive.
+  
 * Python scripting
 
   ** Frame filters and frame decorators have been added.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 3e76cca..4367028 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6465,7 +6465,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.
+number @var{end} is included.
 @end table
 
 This command may not be available for all recording methods.
@@ -6536,8 +6536,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}.  The function number @var{end} is included.
 @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 7fe7cb2..5c390b8 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 ab985e4..7e14075 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -873,7 +873,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.
@@ -888,7 +888,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 f7a9233..935fc22 100644
--- a/gdb/testsuite/gdb.btrace/function_call_history.exp
+++ b/gdb/testsuite/gdb.btrace/function_call_history.exp
@@ -183,9 +183,10 @@ set expected_range [join [list \
   "10\tinc"] "\r\n"]
 
 # show functions in instruction range
-gdb_test "record function-call-history 4,11" $expected_range
+gdb_test "record function-call-history 4,10" $expected_range
 gdb_test "record function-call-history 4,+7" $expected_range
-gdb_test "record function-call-history 11,-7" $expected_range
+gdb_test "record function-call-history 10,-7" $expected_range
+gdb_test "record function-call-history 4,4" "4\tinc\r"
 
 # 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 46ef65e..5769345 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" [join [list \
+gdb_test "record instruction-history 2,6" [join [list \
   "2\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
   "3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax" \
   "4\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
@@ -81,7 +81,7 @@ gdb_test "record instruction-history /f 2,+5" [join [list \
   "6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
   ] "\r\n"]
 
-gdb_test "record instruction-history /p 7,-5" [join [list \
+gdb_test "record instruction-history /p 6,-5" [join [list \
   "2\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
   "3\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax" \
   "4\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
@@ -89,7 +89,7 @@ gdb_test "record instruction-history /p 7,-5" [join [list \
   "6\t0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
   ] "\r\n"]
 
-gdb_test "record instruction-history /pf 2,7" [join [list \
+gdb_test "record instruction-history /pf 2,6" [join [list \
   "2\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
   "3\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax" \
   "4\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
@@ -97,6 +97,8 @@ gdb_test "record instruction-history /pf 2,7" [join [list \
   "6\t0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
   ] "\r\n"]
 
+gdb_test "record instruction-history 2,2" "2\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
+
 # the following tests are checking the iterators
 # to avoid lots of regexps, we just check the number of lines that
 # were printed during command execution.
-- 
1.8.3.1

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

* [PATCH v10 07/28] record-btrace: fix insn range in function call history
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (24 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 15/28] frame, backtrace: allow targets to supply a frame unwinder Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-14  8:05 ` [PATCH v10 28/28] record-btrace: add (reverse-)stepping support Markus Metzger
                   ` (2 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +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].

2014-01-14  Markus Metzger  <markus.t.metzger@intel.com>

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


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

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

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

* [PATCH v10 16/28] frame, cfa: check unwind stop reason first
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (9 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 25/28] btrace, gdbserver: read branch trace incrementally Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-14  8:05 ` [PATCH v10 19/28] target, breakpoint: allow insert/remove breakpoint to be forwarded Markus Metzger
                   ` (17 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

Swap the unwind stop reason check and the unwinder check to allow
non-dwarf2 frame types to fail with a recoverable error.

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

	* dwarf2-frame.c (dwarf2_frame_cfa): Move UNWIND_UNAVAILABLE check
	earlier.


---
 gdb/dwarf2-frame.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/gdb/dwarf2-frame.c b/gdb/dwarf2-frame.c
index d6d1bf7..772de56 100644
--- a/gdb/dwarf2-frame.c
+++ b/gdb/dwarf2-frame.c
@@ -1512,16 +1512,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
-- 
1.8.3.1

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

* [PATCH v10 04/28] gdbarch: add instruction predicate methods
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (12 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 02/28] btrace, linux: fix memory leak when reading branch trace Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-14  8:05 ` [PATCH v10 11/28] record-btrace: make ranges include begin and end Markus Metzger
                   ` (14 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

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

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

2014-01-14  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    | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/gdbarch.h    | 18 ++++++++++++++
 gdb/gdbarch.sh   |  9 +++++++
 gdb/i386-tdep.c  | 59 ++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 244 insertions(+)

diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 2055a3b..72d748e 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -1345,6 +1345,24 @@ amd64_absolute_jmp_p (const struct amd64_insn *details)
   return 0;
 }
 
+/* Return non-zero if the instruction DETAILS is a jump, zero otherwise.  */
+
+static int
+amd64_jmp_p (const struct amd64_insn *details)
+{
+  const gdb_byte *insn = &details->raw_insn[details->opcode_offset];
+
+  /* jump short, relative.  */
+  if (insn[0] == 0xeb)
+    return 1;
+
+  /* jump near, relative.  */
+  if (insn[0] == 0xe9)
+    return 1;
+
+  return amd64_absolute_jmp_p (details);
+}
+
 static int
 amd64_absolute_call_p (const struct amd64_insn *details)
 {
@@ -1416,6 +1434,52 @@ amd64_syscall_p (const struct amd64_insn *details, int *lengthp)
   return 0;
 }
 
+/* Classify the instruction at ADDR using PRED.
+   Throw an error if the memory can't be read.  */
+
+static int
+amd64_classify_insn_at (struct gdbarch *gdbarch, CORE_ADDR addr,
+			int (*pred) (const struct amd64_insn *))
+{
+  struct amd64_insn details;
+  gdb_byte *buf;
+  int len, classification;
+
+  len = gdbarch_max_insn_length (gdbarch);
+  buf = alloca (len);
+
+  read_code (addr, buf, len);
+  amd64_get_insn_details (buf, &details);
+
+  classification = pred (&details);
+
+  return classification;
+}
+
+/* The gdbarch insn_is_call method.  */
+
+static int
+amd64_insn_is_call (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  return amd64_classify_insn_at (gdbarch, addr, amd64_call_p);
+}
+
+/* The gdbarch insn_is_ret method.  */
+
+static int
+amd64_insn_is_ret (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  return amd64_classify_insn_at (gdbarch, addr, amd64_ret_p);
+}
+
+/* The gdbarch insn_is_jump method.  */
+
+static int
+amd64_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  return amd64_classify_insn_at (gdbarch, addr, amd64_jmp_p);
+}
+
 /* Fix up the state of registers and memory after having single-stepped
    a displaced instruction.  */
 
@@ -2966,6 +3030,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 66cebd7..bff1a10 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 08ad4f9..46d1573 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 694fc0d..e16b8d0 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -317,6 +317,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;
 };
 
 
@@ -490,6 +493,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() */
 };
 
@@ -581,6 +587,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;
@@ -795,6 +804,9 @@ 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 */
+  /* Skip verify of insn_is_call, invalid_p == 0 */
+  /* Skip verify of insn_is_ret, invalid_p == 0 */
+  /* Skip verify of insn_is_jump, invalid_p == 0 */
   buf = ui_file_xstrdup (log, &length);
   make_cleanup (xfree, buf);
   if (length > 0)
@@ -1128,6 +1140,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,
@@ -4451,6 +4472,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 ccb7e96..c4b5bac 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -1267,6 +1267,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 ec54d6f..57bf0e7 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -994,6 +994,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::0
+
+# 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::0
+
+# 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::0
 EOF
 }
 
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 4a5f652..57f18cb 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -530,6 +530,22 @@ i386_absolute_jmp_p (const gdb_byte *insn)
   return 0;
 }
 
+/* Return non-zero if INSN is a jump, zero otherwise.  */
+
+static int
+i386_jmp_p (const gdb_byte *insn)
+{
+  /* jump short, relative.  */
+  if (insn[0] == 0xeb)
+    return 1;
+
+  /* jump near, relative.  */
+  if (insn[0] == 0xe9)
+    return 1;
+
+  return i386_absolute_jmp_p (insn);
+}
+
 static int
 i386_absolute_call_p (const gdb_byte *insn)
 {
@@ -601,6 +617,45 @@ i386_syscall_p (const gdb_byte *insn, int *lengthp)
   return 0;
 }
 
+/* The gdbarch insn_is_call method.  */
+
+static int
+i386_insn_is_call (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  gdb_byte buf[I386_MAX_INSN_LEN], *insn;
+
+  read_code (addr, buf, I386_MAX_INSN_LEN);
+  insn = i386_skip_prefixes (buf, I386_MAX_INSN_LEN);
+
+  return i386_call_p (insn);
+}
+
+/* The gdbarch insn_is_ret method.  */
+
+static int
+i386_insn_is_ret (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  gdb_byte buf[I386_MAX_INSN_LEN], *insn;
+
+  read_code (addr, buf, I386_MAX_INSN_LEN);
+  insn = i386_skip_prefixes (buf, I386_MAX_INSN_LEN);
+
+  return i386_ret_p (insn);
+}
+
+/* The gdbarch insn_is_jump method.  */
+
+static int
+i386_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  gdb_byte buf[I386_MAX_INSN_LEN], *insn;
+
+  read_code (addr, buf, I386_MAX_INSN_LEN);
+  insn = i386_skip_prefixes (buf, I386_MAX_INSN_LEN);
+
+  return i386_jmp_p (insn);
+}
+
 /* Some kernels may run one past a syscall insn, so we have to cope.
    Otherwise this is just simple_displaced_step_copy_insn.  */
 
@@ -8017,6 +8072,10 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 
   set_gdbarch_gen_return_address (gdbarch, i386_gen_return_address);
 
+  set_gdbarch_insn_is_call (gdbarch, i386_insn_is_call);
+  set_gdbarch_insn_is_ret (gdbarch, i386_insn_is_ret);
+  set_gdbarch_insn_is_jump (gdbarch, i386_insn_is_jump);
+
   /* Hook in ABI-specific overrides, if they have been registered.  */
   info.tdep_info = (void *) tdesc_data;
   gdbarch_init_osabi (info, gdbarch);
-- 
1.8.3.1

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

* [PATCH v10 00/28] record-btrace: reverse
@ 2014-01-14  8:05 Markus Metzger
  2014-01-14  8:04 ` [PATCH v10 01/28] btrace, test: fix multi-line btrace tests Markus Metzger
                   ` (28 more replies)
  0 siblings, 29 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

This is a smaller update addressing Pedro's feedback.

The "target: add ops parameter to to_prepare_to_store method" patch has
been replaced with Tom's "Add target_ops argument to to_prepare_to_store"
patch.

In "record-btrace: provide xfer_partial target method" xfer_partial now
returns an error code instead of throwing an error.

The "frame:  add frame_is_tailcall function" patch has been dropped since
it is no longer needed.  Btrace frames use NORMAL_FRAME and TAILCALL_FRAME
instead of new BTRACE_ variants.

An indentation error where the indentation in "record function-call-history"
had been off by two spaces in some cases has been fixed.

Markus Metzger (28):
  btrace, test: fix multi-line btrace tests
  btrace, linux: fix memory leak when reading branch trace
  btrace: uppercase btrace_read_type
  gdbarch: add instruction predicate methods
  frame: add frame_id_build_unavailable_stack_special
  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
  Add target_ops argument to to_prepare_to_store
  record-btrace: supply register target methods
  frame, backtrace: allow targets to supply a frame unwinder
  frame, cfa: check unwind stop reason first
  frame: do not assume unwinding will succeed
  record-btrace, frame: supply target-specific unwinder
  target, breakpoint: allow insert/remove breakpoint to be forwarded
  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
  target: allow decr_pc_after_break to be defined by the target
  record-btrace: add (reverse-)stepping support

 gdb/NEWS                                           |   27 +
 gdb/aix-thread.c                                   |    2 +-
 gdb/amd64-linux-nat.c                              |    6 +-
 gdb/amd64-tdep.c                                   |   67 +
 gdb/arch-utils.c                                   |   15 +
 gdb/arch-utils.h                                   |    4 +
 gdb/btrace.c                                       | 1627 +++++++++++++++----
 gdb/btrace.h                                       |  269 +++-
 gdb/common/btrace-common.h                         |   29 +-
 gdb/common/linux-btrace.c                          |  125 +-
 gdb/common/linux-btrace.h                          |   15 +-
 gdb/corelow.c                                      |    3 +-
 gdb/darwin-nat.c                                   |    4 +-
 gdb/doc/gdb.texinfo                                |   32 +-
 gdb/dwarf2-frame.c                                 |   14 +-
 gdb/exec.c                                         |    3 +-
 gdb/frame-unwind.c                                 |   86 +-
 gdb/frame.c                                        |   25 +-
 gdb/frame.h                                        |    8 +
 gdb/gdbarch.c                                      |   72 +
 gdb/gdbarch.h                                      |   18 +
 gdb/gdbarch.sh                                     |    9 +
 gdb/gdbserver/linux-low.c                          |   36 +-
 gdb/gdbserver/server.c                             |   15 +-
 gdb/gdbserver/target.h                             |    9 +-
 gdb/go32-nat.c                                     |    5 +-
 gdb/i386-linux-nat.c                               |    6 +-
 gdb/i386-tdep.c                                    |   59 +
 gdb/inf-child.c                                    |    3 +-
 gdb/infrun.c                                       |    9 +-
 gdb/linux-nat.c                                    |    4 +-
 gdb/linux-thread-db.c                              |    2 +-
 gdb/mem-break.c                                    |    4 +-
 gdb/monitor.c                                      |    6 +-
 gdb/nto-procfs.c                                   |    6 +-
 gdb/ravenscar-thread.c                             |    8 +-
 gdb/record-btrace.c                                | 1652 +++++++++++++++++---
 gdb/record-full.c                                  |   57 +-
 gdb/record.c                                       |    4 +
 gdb/record.h                                       |    7 +
 gdb/remote-m32r-sdi.c                              |    8 +-
 gdb/remote-mips.c                                  |   13 +-
 gdb/remote-sim.c                                   |    5 +-
 gdb/remote.c                                       |   42 +-
 gdb/target.c                                       |  131 +-
 gdb/target.h                                       |   74 +-
 gdb/testsuite/gdb.btrace/Makefile.in               |    4 +-
 gdb/testsuite/gdb.btrace/data.c                    |   36 +
 gdb/testsuite/gdb.btrace/data.exp                  |   45 +
 gdb/testsuite/gdb.btrace/delta.exp                 |   83 +
 gdb/testsuite/gdb.btrace/exception.cc              |   56 +
 gdb/testsuite/gdb.btrace/exception.exp             |   70 +
 gdb/testsuite/gdb.btrace/finish.exp                |   59 +
 gdb/testsuite/gdb.btrace/function_call_history.exp |  322 ++--
 gdb/testsuite/gdb.btrace/instruction_history.exp   |   80 +-
 gdb/testsuite/gdb.btrace/multi-thread-step.c       |   53 +
 gdb/testsuite/gdb.btrace/multi-thread-step.exp     |  135 ++
 gdb/testsuite/gdb.btrace/next.exp                  |   76 +
 gdb/testsuite/gdb.btrace/nexti.exp                 |   76 +
 gdb/testsuite/gdb.btrace/record_goto.c             |   51 +
 gdb/testsuite/gdb.btrace/record_goto.exp           |  183 +++
 gdb/testsuite/gdb.btrace/rn-dl-bind.c              |   37 +
 gdb/testsuite/gdb.btrace/rn-dl-bind.exp            |   52 +
 gdb/testsuite/gdb.btrace/step.exp                  |   89 ++
 gdb/testsuite/gdb.btrace/stepi.exp                 |   93 ++
 gdb/testsuite/gdb.btrace/tailcall.exp              |   92 ++
 gdb/testsuite/gdb.btrace/unknown_functions.c       |   45 +
 gdb/testsuite/gdb.btrace/unknown_functions.exp     |   62 +
 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 +
 gdb/windows-nat.c                                  |    2 +-
 72 files changed, 6127 insertions(+), 972 deletions(-)
 create mode 100644 gdb/testsuite/gdb.btrace/data.c
 create mode 100644 gdb/testsuite/gdb.btrace/data.exp
 create mode 100644 gdb/testsuite/gdb.btrace/delta.exp
 create mode 100644 gdb/testsuite/gdb.btrace/exception.cc
 create mode 100755 gdb/testsuite/gdb.btrace/exception.exp
 create mode 100644 gdb/testsuite/gdb.btrace/finish.exp
 create mode 100644 gdb/testsuite/gdb.btrace/multi-thread-step.c
 create mode 100644 gdb/testsuite/gdb.btrace/multi-thread-step.exp
 create mode 100644 gdb/testsuite/gdb.btrace/next.exp
 create mode 100644 gdb/testsuite/gdb.btrace/nexti.exp
 create mode 100644 gdb/testsuite/gdb.btrace/record_goto.c
 create mode 100644 gdb/testsuite/gdb.btrace/record_goto.exp
 create mode 100644 gdb/testsuite/gdb.btrace/rn-dl-bind.c
 create mode 100644 gdb/testsuite/gdb.btrace/rn-dl-bind.exp
 create mode 100644 gdb/testsuite/gdb.btrace/step.exp
 create mode 100644 gdb/testsuite/gdb.btrace/stepi.exp
 create mode 100644 gdb/testsuite/gdb.btrace/tailcall.exp
 create mode 100644 gdb/testsuite/gdb.btrace/unknown_functions.c
 create mode 100644 gdb/testsuite/gdb.btrace/unknown_functions.exp
 create mode 100644 gdb/testsuite/gdb.btrace/x86-record_goto.S
 create mode 100644 gdb/testsuite/gdb.btrace/x86-tailcall.S
 create mode 100644 gdb/testsuite/gdb.btrace/x86-tailcall.c

-- 
1.8.3.1

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

* [PATCH v10 15/28] frame, backtrace: allow targets to supply a frame unwinder
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (23 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 22/28] record-btrace: provide target_find_new_threads method Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-14  8:05 ` [PATCH v10 07/28] record-btrace: fix insn range in function call history Markus Metzger
                   ` (3 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +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.

The original patch has been split into this and the next two patches.

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

	* 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.
	* 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/frame-unwind.c | 86 ++++++++++++++++++++++++++++++++++++------------------
 gdb/target.c       | 28 ++++++++++++++++++
 gdb/target.h       | 11 +++++++
 3 files changed, 97 insertions(+), 28 deletions(-)

diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
index f17cda9..fdfea6e 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/target.c b/gdb/target.c
index 36064ca..1a4b156 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4471,6 +4471,34 @@ debug_to_prepare_to_store (struct target_ops *self, struct regcache *regcache)
   fprintf_unfiltered (gdb_stdlog, "target_prepare_to_store ()\n");
 }
 
+/* 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 757a927..47eedeb 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -895,6 +895,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?
      */
@@ -1786,6 +1791,12 @@ extern char *target_fileio_read_stralloc (const char *filename);
 
 extern int target_core_of_thread (ptid_t ptid);
 
+/* See to_get_unwinder in struct target_ops.  */
+extern const struct frame_unwind *target_get_unwinder (void);
+
+/* See to_get_tailcall_unwinder in struct target_ops.  */
+extern const struct frame_unwind *target_get_tailcall_unwinder (void);
+
 /* Verify that the memory in the [MEMADDR, MEMADDR+SIZE) range matches
    the contents of [DATA,DATA+SIZE).  Returns 1 if there's a match, 0
    if there's a mismatch, and -1 if an error is encountered while
-- 
1.8.3.1

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

* [PATCH v10 17/28] frame: do not assume unwinding will succeed
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (15 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 23/28] record-btrace: add record goto target methods Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-14  8:05 ` [PATCH v10 12/28] btrace: add replay position to btrace thread info Markus Metzger
                   ` (11 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

In get_frame_unwind_stop_reason, remove the assumption that further frame
unwinding will succeed.

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

	* frame.c (get_frame_unwind_stop_reason): Unconditionally call
	get_prev_frame_1.


---
 gdb/frame.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/gdb/frame.c b/gdb/frame.c
index b72d5e4..8cd607b 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -2522,13 +2522,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;
 }
 
-- 
1.8.3.1

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

* [PATCH v10 12/28] btrace: add replay position to btrace thread info
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (16 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 17/28] frame: do not assume unwinding will succeed Markus Metzger
@ 2014-01-14  8:05 ` Markus Metzger
  2014-01-14  8:05 ` [PATCH v10 24/28] record-btrace: extend unwinder Markus Metzger
                   ` (10 subsequent siblings)
  28 siblings, 0 replies; 58+ messages in thread
From: Markus Metzger @ 2014-01-14  8:05 UTC (permalink / raw)
  To: jan.kratochvil, palves; +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.

2014-01-14  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 1d060d3e..632ebe1 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -781,9 +781,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.  */
@@ -1339,3 +1341,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 d219f69..93e6940 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 5c390b8..aa0272e 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -237,6 +237,10 @@ record_btrace_info (void)
   printf_unfiltered (_("Recorded %u instructions in %u functions for thread "
 		       "%d (%s).\n"), insns, calls, tp->num,
 		     target_pid_to_str (tp->ptid));
+
+  if (btrace_is_replaying (tp))
+    printf_unfiltered (_("Replay in progress.  At instruction %u.\n"),
+		       btrace_insn_number (btinfo->replay));
 }
 
 /* Print an unsigned int.  */
@@ -301,13 +305,34 @@ record_btrace_insn_history (int size, int flags)
   history = btinfo->insn_history;
   if (history == NULL)
     {
-      /* No matter the direction, we start with the tail of the trace.  */
-      btrace_insn_end (&begin, btinfo);
-      end = begin;
+      struct btrace_insn_iterator *replay;
 
       DEBUG ("insn-history (0x%x): %d", flags, size);
 
-      covered = btrace_insn_prev (&begin, context);
+      /* If we're replaying, we start at the replay position.  Otherwise, we
+	 start at the tail of the trace.  */
+      replay = btinfo->replay;
+      if (replay != NULL)
+	begin = *replay;
+      else
+	btrace_insn_end (&begin, btinfo);
+
+      /* We start from here and expand in the requested direction.  Then we
+	 expand in the other direction, as well, to fill up any remaining
+	 context.  */
+      end = begin;
+      if (size < 0)
+	{
+	  /* We want the current position covered, as well.  */
+	  covered = btrace_insn_next (&end, 1);
+	  covered += btrace_insn_prev (&begin, context - covered);
+	  covered += btrace_insn_next (&end, context - covered);
+	}
+      else
+	{
+	  covered = btrace_insn_next (&end, context);
+	  covered += btrace_insn_prev (&begin, context - covered);
+	}
     }
   else
     {
@@ -561,13 +586,37 @@ record_btrace_call_history (int size, int flags)
   history = btinfo->call_history;
   if (history == NULL)
     {
-      /* No matter the direction, we start with the tail of the trace.  */
-      btrace_call_end (&begin, btinfo);
-      end = begin;
+      struct btrace_insn_iterator *replay;
 
       DEBUG ("call-history (0x%x): %d", flags, size);
 
-      covered = btrace_call_prev (&begin, context);
+      /* If we're replaying, we start at the replay position.  Otherwise, we
+	 start at the tail of the trace.  */
+      replay = btinfo->replay;
+      if (replay != NULL)
+	{
+	  begin.function = replay->function;
+	  begin.btinfo = btinfo;
+	}
+      else
+	btrace_call_end (&begin, btinfo);
+
+      /* We start from here and expand in the requested direction.  Then we
+	 expand in the other direction, as well, to fill up any remaining
+	 context.  */
+      end = begin;
+      if (size < 0)
+	{
+	  /* We want the current position covered, as well.  */
+	  covered = btrace_call_next (&end, 1);
+	  covered += btrace_call_prev (&begin, context - covered);
+	  covered += btrace_call_next (&end, context - covered);
+	}
+      else
+	{
+	  covered = btrace_call_next (&end, context);
+	  covered += btrace_call_prev (&begin, context- covered);
+	}
     }
   else
     {
@@ -688,6 +737,20 @@ record_btrace_call_history_from (ULONGEST from, int size, int flags)
   record_btrace_call_history_range (begin, end, flags);
 }
 
+/* The to_record_is_replaying method of target record-btrace.  */
+
+static int
+record_btrace_is_replaying (void)
+{
+  struct thread_info *tp;
+
+  ALL_THREADS (tp)
+    if (btrace_is_replaying (tp))
+      return 1;
+
+  return 0;
+}
+
 /* Initialize the record-btrace target ops.  */
 
 static void
@@ -714,6 +777,7 @@ init_record_btrace_ops (void)
   ops->to_call_history = record_btrace_call_history;
   ops->to_call_history_from = record_btrace_call_history_from;
   ops->to_call_history_range = record_btrace_call_history_range;
+  ops->to_record_is_replaying = record_btrace_is_replaying;
   ops->to_stratum = record_stratum;
   ops->to_magic = OPS_MAGIC;
 }
-- 
1.8.3.1

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

* Re: [PATCH v10 10/28] record-btrace: optionally indent function call history
  2014-01-14  8:04 ` [PATCH v10 10/28] record-btrace: optionally indent function call history Markus Metzger
@ 2014-01-14 16:07   ` Eli Zaretskii
  0 siblings, 0 replies; 58+ messages in thread
From: Eli Zaretskii @ 2014-01-14 16:07 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, palves, gdb-patches

> From: Markus Metzger <markus.t.metzger@intel.com>
> Cc: gdb-patches@sourceware.org, Eli Zaretskii <eliz@gnu.org>
> Date: Tue, 14 Jan 2014 09:04:20 +0100
> 
> 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.

Thanks.

> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -6490,7 +6490,10 @@ 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.  The @code{/l}, @code{/i}, and @code{/c} modifiers can be
> +given at the same time.
         ^^^^^^^^^^^^^^^^
"Together" sounds better than "at the same time".

The documentation parts are OK with that change.

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

* Re: [PATCH v10 20/28] record-btrace: provide xfer_partial target method
  2014-01-14  8:04 ` [PATCH v10 20/28] record-btrace: provide xfer_partial target method Markus Metzger
@ 2014-01-15 15:51   ` Pedro Alves
  0 siblings, 0 replies; 58+ messages in thread
From: Pedro Alves @ 2014-01-15 15:51 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 01/14/2014 08:04 AM, Markus Metzger wrote:
> @@ -1406,6 +1406,10 @@ raw_memory_xfer_partial (struct target_ops *ops, void *readbuf,
>        if (res > 0)
>  	break;
>  
> +      /* Stop if the target reports that the memory is not available. */

Double space after period.

> +      if (res == TARGET_XFER_E_UNAVAILABLE)
> +	break;

-- 
Pedro Alves

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

* Re: [PATCH v10 19/28] target, breakpoint: allow insert/remove breakpoint to be forwarded
  2014-01-14  8:05 ` [PATCH v10 19/28] target, breakpoint: allow insert/remove breakpoint to be forwarded Markus Metzger
@ 2014-01-15 15:52   ` Pedro Alves
  0 siblings, 0 replies; 58+ messages in thread
From: Pedro Alves @ 2014-01-15 15:52 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 01/14/2014 08:04 AM, Markus Metzger wrote:
> 	* m32r-sdi.c (m32r_insert_breakpoint, m32r_remove_breakpoint):
> 	Add target_ops parameter.

It's remote-m32r-sdi.c.

-- 
Pedro Alves

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

* Re: [PATCH v10 00/28] record-btrace: reverse
  2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
                   ` (27 preceding siblings ...)
  2014-01-14  8:05 ` [PATCH v10 27/28] target: allow decr_pc_after_break to be defined by the target Markus Metzger
@ 2014-01-15 15:54 ` Pedro Alves
  2014-01-16 12:01   ` Metzger, Markus T
  28 siblings, 1 reply; 58+ messages in thread
From: Pedro Alves @ 2014-01-15 15:54 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 01/14/2014 08:04 AM, Markus Metzger wrote:
> This is a smaller update addressing Pedro's feedback.

Thank you.

> 
> The "target: add ops parameter to to_prepare_to_store method" patch has
> been replaced with Tom's "Add target_ops argument to to_prepare_to_store"
> patch.
> 
> In "record-btrace: provide xfer_partial target method" xfer_partial now
> returns an error code instead of throwing an error.
> 
> The "frame:  add frame_is_tailcall function" patch has been dropped since
> it is no longer needed.  Btrace frames use NORMAL_FRAME and TAILCALL_FRAME
> instead of new BTRACE_ variants.
> 
> An indentation error where the indentation in "record function-call-history"
> had been off by two spaces in some cases has been fixed.

I read through the series, and apart from the tiny nits in
two patches I sent out, I have no further comments.  This looks
good to go to me.  Congratulations!  Quite a neat piece of work.

Thanks!
-- 
Pedro Alves

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

* RE: [PATCH v10 00/28] record-btrace: reverse
  2014-01-15 15:54 ` [PATCH v10 00/28] record-btrace: reverse Pedro Alves
@ 2014-01-16 12:01   ` Metzger, Markus T
  2014-01-16 12:37     ` Pedro Alves
  2014-01-16 14:35     ` Tom Tromey
  0 siblings, 2 replies; 58+ messages in thread
From: Metzger, Markus T @ 2014-01-16 12:01 UTC (permalink / raw)
  To: Pedro Alves, jan.kratochvil; +Cc: gdb-patches

> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Wednesday, January 15, 2014 4:54 PM


> I read through the series, and apart from the tiny nits in
> two patches I sent out, I have no further comments.  This looks
> good to go to me.  Congratulations!  Quite a neat piece of work.

Thanks a lot, Jan and Pedro, for your thorough reviews and your
good feedback.

I am moving the NEWS entries to "Changes since 7.7" as we narrowly
missed the 7.7 branch.  I trust this does not require another round
of review.

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

* Re: [PATCH v10 00/28] record-btrace: reverse
  2014-01-16 12:01   ` Metzger, Markus T
@ 2014-01-16 12:37     ` Pedro Alves
  2014-01-16 14:35     ` Tom Tromey
  1 sibling, 0 replies; 58+ messages in thread
From: Pedro Alves @ 2014-01-16 12:37 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches

On 01/16/2014 12:00 PM, Metzger, Markus T wrote:
>> -----Original Message-----
>> From: Pedro Alves [mailto:palves@redhat.com]
>> Sent: Wednesday, January 15, 2014 4:54 PM
> 
> 
>> I read through the series, and apart from the tiny nits in
>> two patches I sent out, I have no further comments.  This looks
>> good to go to me.  Congratulations!  Quite a neat piece of work.
> 
> Thanks a lot, Jan and Pedro, for your thorough reviews and your
> good feedback.

My pleasure.

> I am moving the NEWS entries to "Changes since 7.7" as we narrowly
> missed the 7.7 branch.  I trust this does not require another round
> of review.

Yes, should be obvious.

Thanks,
-- 
Pedro Alves

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

* Re: [PATCH v10 00/28] record-btrace: reverse
  2014-01-16 12:01   ` Metzger, Markus T
  2014-01-16 12:37     ` Pedro Alves
@ 2014-01-16 14:35     ` Tom Tromey
  1 sibling, 0 replies; 58+ messages in thread
From: Tom Tromey @ 2014-01-16 14:35 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: Pedro Alves, jan.kratochvil, gdb-patches

>>>>> "Markus" == Metzger, Markus T <markus.t.metzger@intel.com> writes:

>> I read through the series, and apart from the tiny nits in
>> two patches I sent out, I have no further comments.  This looks
>> good to go to me.  Congratulations!  Quite a neat piece of work.

Markus> Thanks a lot, Jan and Pedro, for your thorough reviews and your
Markus> good feedback.

Congratulations, Markus.

Tom

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

* Re: [PATCH v10 25/28] btrace, gdbserver: read branch trace incrementally
  2014-01-14  8:05 ` [PATCH v10 25/28] btrace, gdbserver: read branch trace incrementally Markus Metzger
@ 2014-01-16 17:57   ` Tom Tromey
  2014-01-17  8:28     ` Metzger, Markus T
  0 siblings, 1 reply; 58+ messages in thread
From: Tom Tromey @ 2014-01-16 17:57 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, palves, gdb-patches

>>>>> "Markus" == Markus Metzger <markus.t.metzger@intel.com> writes:

Hi Markus.

Markus> -VEC (btrace_block_s) *
Markus> -target_read_btrace (struct btrace_target_info *btinfo,
Markus> +enum btrace_error
Markus> +target_read_btrace (VEC (btrace_block_s) **btrace,
Markus> +		    struct btrace_target_info *btinfo,
Markus>  		    enum btrace_read_type type)
Markus>  {
Markus>    struct target_ops *t;
 
Markus>    for (t = current_target.beneath; t != NULL; t = t->beneath)
Markus>      if (t->to_read_btrace != NULL)
Markus> -      return t->to_read_btrace (btinfo, type);
Markus> +      return t->to_read_btrace (btrace, btinfo, type);
 
Markus>    tcomplain ();
Markus> -  return NULL;
Markus> +  return BTRACE_ERR_NOT_SUPPORTED;
Markus>  }

While rebasing my branch I noticed this code.

tcomplain doesn't return, so the final "return" here will never be
executed.

Given the function's history I assume this is no big deal -- just a
small oversight, compounded by the compiler not bothering to tell us
that it knows about this :)

However I wanted to point it out in case you were expecting an error
return here.

If it is not important, then please just ignore it.  I have fixed it on
my branch already.

Tom

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

* RE: [PATCH v10 25/28] btrace, gdbserver: read branch trace incrementally
  2014-01-16 17:57   ` Tom Tromey
@ 2014-01-17  8:28     ` Metzger, Markus T
  2014-01-20  5:44       ` Tom Tromey
  0 siblings, 1 reply; 58+ messages in thread
From: Metzger, Markus T @ 2014-01-17  8:28 UTC (permalink / raw)
  To: Tom Tromey; +Cc: jan.kratochvil, palves, gdb-patches

> -----Original Message-----
> From: Tom Tromey [mailto:tromey@redhat.com]
> Sent: Thursday, January 16, 2014 6:57 PM


> Markus> -VEC (btrace_block_s) *
> Markus> -target_read_btrace (struct btrace_target_info *btinfo,
> Markus> +enum btrace_error
> Markus> +target_read_btrace (VEC (btrace_block_s) **btrace,
> Markus> +		    struct btrace_target_info *btinfo,
> Markus>  		    enum btrace_read_type type)
> Markus>  {
> Markus>    struct target_ops *t;
> 
> Markus>    for (t = current_target.beneath; t != NULL; t = t->beneath)
> Markus>      if (t->to_read_btrace != NULL)
> Markus> -      return t->to_read_btrace (btinfo, type);
> Markus> +      return t->to_read_btrace (btrace, btinfo, type);
> 
> Markus>    tcomplain ();
> Markus> -  return NULL;
> Markus> +  return BTRACE_ERR_NOT_SUPPORTED;
> Markus>  }
> 
> While rebasing my branch I noticed this code.
> 
> tcomplain doesn't return, so the final "return" here will never be
> executed.
> 
> Given the function's history I assume this is no big deal -- just a
> small oversight, compounded by the compiler not bothering to tell us
> that it knows about this :)
> 
> However I wanted to point it out in case you were expecting an error
> return here.

I am expecting the exception from tcomplain ().

The return BTRACE_ERR_NOT_SUPPORTED is to avoid warnings from
compilers that can't figure out that tcomplain () does not return.

I used to return NULL before and I'm using the same pattern in
target_enable_btrace.  There's another instance in dummy_get_bookmark.

If this turns out to be unnecessary, I can send a patch to remove
those extra returns.

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

* Re: [PATCH v10 25/28] btrace, gdbserver: read branch trace incrementally
  2014-01-17  8:28     ` Metzger, Markus T
@ 2014-01-20  5:44       ` Tom Tromey
  0 siblings, 0 replies; 58+ messages in thread
From: Tom Tromey @ 2014-01-20  5:44 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, palves, gdb-patches

>>>>> "Markus" == Metzger, Markus T <markus.t.metzger@intel.com> writes:

Markus> The return BTRACE_ERR_NOT_SUPPORTED is to avoid warnings from
Markus> compilers that can't figure out that tcomplain () does not
Markus> return.

I think we ignore those now :)

Markus> If this turns out to be unnecessary, I can send a patch to
Markus> remove those extra returns.

It isn't of major importance.
If you want to do it, it's fine as an obvious change.

Tom

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

* x86_64-m32 internal error for multi-thread-step.exp  [Re: [PATCH v10 06/28] btrace: change branch trace data structure]
  2014-01-14  8:05 ` [PATCH v10 06/28] btrace: change branch trace data structure Markus Metzger
@ 2015-01-08 20:49   ` Jan Kratochvil
  2015-01-20 15:19     ` Metzger, Markus T
  0 siblings, 1 reply; 58+ messages in thread
From: Jan Kratochvil @ 2015-01-08 20:49 UTC (permalink / raw)
  To: Markus Metzger; +Cc: palves, gdb-patches

On Tue, 14 Jan 2014 09:04:16 +0100, Markus Metzger wrote:
[...]
> +/* 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);

(gdb) PASS: gdb.btrace/multi-thread-step.exp: navigate: thread 1: record goto begin
info record^M
Active record target: record-btrace^M
btrace.c:220: internal-error: ftrace_new_function: Assertion `prev->flow.next == NULL' failed.^M

This occasionally happens on x86_64 system with -m32 inferior:
	runtest CC_FOR_TARGET="gcc -m32" CXX_FOR_TARGET="g++ -m32" gdb.btrace/multi-thread-step.exp

cpu family	: 6
model		: 63
model name	: Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz
stepping	: 2
microcode	: 0x2a


> +      prev->flow.next = bfun;
>  
> -  return (filename_cmp (bfile, filename) != 0);
> +      bfun->number = prev->number + 1;
> +      bfun->insn_offset = (prev->insn_offset
> +			   + VEC_length (btrace_insn_s, prev->insn));
> +    }
> +
> +  return bfun;
>  }

Although for -m32 inferior there are 100% reproducible more FAILs:
	FAIL: gdb.btrace/rn-dl-bind.exp: reverse-next
	FAIL: gdb.btrace/rn-dl-bind.exp: reverse-step
	FAIL: gdb.btrace/rn-dl-bind.exp: reverse-step
	FAIL: gdb.btrace/rn-dl-bind.exp: reverse-next
	FAIL: gdb.btrace/rn-dl-bind.exp: next (the program exited)
	FAIL: gdb.btrace/exception.exp: flat (pattern 1)
	FAIL: gdb.btrace/exception.exp: indented (pattern 1) 


So I do not think the assert may be such a critical issue as IIUC btrace is
probably not used for 32-bit inferiors.


Regards,
Jan

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

* RE: x86_64-m32 internal error for multi-thread-step.exp  [Re: [PATCH v10 06/28] btrace: change branch trace data structure]
  2015-01-08 20:49   ` x86_64-m32 internal error for multi-thread-step.exp [Re: [PATCH v10 06/28] btrace: change branch trace data structure] Jan Kratochvil
@ 2015-01-20 15:19     ` Metzger, Markus T
  2015-01-22 12:30       ` Metzger, Markus T
  0 siblings, 1 reply; 58+ messages in thread
From: Metzger, Markus T @ 2015-01-20 15:19 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: palves, gdb-patches

> -----Original Message-----
> From: Jan Kratochvil [mailto:jan.kratochvil@redhat.com]
> Sent: Thursday, January 8, 2015 9:50 PM
> To: Metzger, Markus T
> Cc: palves@redhat.com; gdb-patches@sourceware.org
> Subject: x86_64-m32 internal error for multi-thread-step.exp [Re: [PATCH
> v10 06/28] btrace: change branch trace data structure]

Thanks, Jan,

> Although for -m32 inferior there are 100% reproducible more FAILs:
> 	FAIL: gdb.btrace/rn-dl-bind.exp: reverse-next
> 	FAIL: gdb.btrace/rn-dl-bind.exp: reverse-step
> 	FAIL: gdb.btrace/rn-dl-bind.exp: reverse-step
> 	FAIL: gdb.btrace/rn-dl-bind.exp: reverse-next
> 	FAIL: gdb.btrace/rn-dl-bind.exp: next (the program exited)
> 	FAIL: gdb.btrace/exception.exp: flat (pattern 1)
> 	FAIL: gdb.btrace/exception.exp: indented (pattern 1)

I can reproduce those fails and will look into them.


> btrace.c:220: internal-error: ftrace_new_function: Assertion `prev-
> >flow.next == NULL' failed.^M
> 
> This occasionally happens on x86_64 system with -m32 inferior:
> 	runtest CC_FOR_TARGET="gcc -m32" CXX_FOR_TARGET="g++ -m32"
> gdb.btrace/multi-thread-step.exp

I can't reproduce this fail; I don't get that far.  This test fails for me with

	FAIL: gdb.btrace/multi-thread-step.exp: continue to breakpoint: cont to multi-thread-step.c:34 (timeout)

This fail can be reproduced also with 64-bit inferiors and it looks like a regression
to end of last year.  The test passed when I sent the patch series.  I'll look into
this first.

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

* RE: x86_64-m32 internal error for multi-thread-step.exp  [Re: [PATCH v10 06/28] btrace: change branch trace data structure]
  2015-01-20 15:19     ` Metzger, Markus T
@ 2015-01-22 12:30       ` Metzger, Markus T
  2015-01-22 13:36         ` Pedro Alves
                           ` (2 more replies)
  0 siblings, 3 replies; 58+ messages in thread
From: Metzger, Markus T @ 2015-01-22 12:30 UTC (permalink / raw)
  To: Jan Kratochvil, Patrick Palka; +Cc: palves, gdb-patches

> -----Original Message-----
> From: Metzger, Markus T
> Sent: Tuesday, January 20, 2015 4:08 PM
> To: Jan Kratochvil
> Cc: palves@redhat.com; gdb-patches@sourceware.org


> I can't reproduce this fail; I don't get that far.  This test fails for me with
> 
> 	FAIL: gdb.btrace/multi-thread-step.exp: continue to breakpoint: cont
> to multi-thread-step.c:34 (timeout)

This fail seems to be caused by 588dcc3edbde19f90e76de969dbfa7ab3e17951a
"Consolidate the custom TUI query hook with the default query hook".  It is not
related to btrace.

The failing test program looks like this:

    pthread_barrier_wait (&barrier);
    global = 42; /* bp.1 */
    pthread_barrier_wait (&barrier);

There are two threads, both are at bp.1 between the two barriers.  When I now
delete all breakpoints like this:

    (gdb) del
    Delete all breakpoints? (y or n) y

and then continue the inferior, only the current thread is resumed.  The other
thread remains at its current location.  The resumed thread waits at the barrier
and the test runs into a timeout.

Here's a complete debug session:

    (gdb) b 30
    Breakpoint 1 at 0x400776: file gdb.btrace/multi-thread-step.c, line 30.
    (gdb) r
    Starting program: gdb.btrace/multi-thread-step 
    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/lib64/libthread_db.so.1".
    [New Thread 0x7ffff7fce700 (LWP 22156)]

    Breakpoint 1, test (arg=0x0) at gdb.btrace/multi-thread-step.c:30
    30	  global = 42; /* bp.1 */
    (gdb) del
    Delete all breakpoints? (y or n) y
     (gdb) info thr
      Id   Target Id         Frame 
      2    Thread 0x7ffff7fce700 (LWP 22156) "multi-thread-st" test (arg=0x0) at gdb.btrace/multi-thread-step.c:30
    * 1    Thread 0x7ffff7fcf740 (LWP 22152) "multi-thread-st" test (arg=0x0) at gdb.btrace/multi-thread-step.c:30
    (gdb) c
    Continuing.
    ^C
    Program received signal SIGINT, Interrupt.
    0x000000384380c20c in pthread_barrier_wait () from /lib64/libpthread.so.0
    (gdb) info thr
      Id   Target Id         Frame 
      2    Thread 0x7ffff7fce700 (LWP 22156) "multi-thread-st" test (arg=0x0) at gdb.btrace/multi-thread-step.c:30
    * 1    Thread 0x7ffff7fcf740 (LWP 22152) "multi-thread-st" 0x000000384380c20c in pthread_barrier_wait () from     /lib64/libpthread.so.0

When I set debug infrun, I get the this:

    (gdb) del
    Delete all breakpoints? (y or n) y
     (gdb)
    infrun: target_wait (-1, status) =
    infrun:   -1 [process -1],
    infrun:   status->kind = no-resumed
    infrun: TARGET_WAITKIND_NO_RESUMED (ignoring)
    infrun: prepare_to_wait

I don't see this with the old query behaviour or when I remove breakpoints like this

    (gdb) del 1

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

* Re: x86_64-m32 internal error for multi-thread-step.exp  [Re: [PATCH v10 06/28] btrace: change branch trace data structure]
  2015-01-22 12:30       ` Metzger, Markus T
@ 2015-01-22 13:36         ` Pedro Alves
  2015-01-22 17:37           ` Linux: make target_is_async_p return false when async is off Pedro Alves
  2015-01-22 16:37         ` x86_64-m32 internal error for multi-thread-step.exp [Re: [PATCH v10 06/28] btrace: change branch trace data structure] Jan Kratochvil
  2015-01-23 12:55         ` x86_64-m32 internal error for multi-thread-step.exp [Re: [PATCH v10 06/28] btrace: change branch trace data structure] Patrick Palka
  2 siblings, 1 reply; 58+ messages in thread
From: Pedro Alves @ 2015-01-22 13:36 UTC (permalink / raw)
  To: Metzger, Markus T, Jan Kratochvil, Patrick Palka; +Cc: gdb-patches

On 01/22/2015 12:29 PM, Metzger, Markus T wrote:
>> -----Original Message-----
>> From: Metzger, Markus T
>> Sent: Tuesday, January 20, 2015 4:08 PM
>> To: Jan Kratochvil
>> Cc: palves@redhat.com; gdb-patches@sourceware.org
> 
> 
>> I can't reproduce this fail; I don't get that far.  This test fails for me with
>>
>> 	FAIL: gdb.btrace/multi-thread-step.exp: continue to breakpoint: cont
>> to multi-thread-step.c:34 (timeout)
> 
> This fail seems to be caused by 588dcc3edbde19f90e76de969dbfa7ab3e17951a
> "Consolidate the custom TUI query hook with the default query hook".  It is not
> related to btrace.
> 
> The failing test program looks like this:
> 
>     pthread_barrier_wait (&barrier);
>     global = 42; /* bp.1 */
>     pthread_barrier_wait (&barrier);
> 
> There are two threads, both are at bp.1 between the two barriers.  When I now
> delete all breakpoints like this:
> 
>     (gdb) del
>     Delete all breakpoints? (y or n) y
> 
> and then continue the inferior, only the current thread is resumed.  The other
> thread remains at its current location.  The resumed thread waits at the barrier
> and the test runs into a timeout.
> 
> Here's a complete debug session:
> 
>     (gdb) b 30
>     Breakpoint 1 at 0x400776: file gdb.btrace/multi-thread-step.c, line 30.
>     (gdb) r
>     Starting program: gdb.btrace/multi-thread-step 
>     [Thread debugging using libthread_db enabled]
>     Using host libthread_db library "/lib64/libthread_db.so.1".
>     [New Thread 0x7ffff7fce700 (LWP 22156)]
> 
>     Breakpoint 1, test (arg=0x0) at gdb.btrace/multi-thread-step.c:30
>     30	  global = 42; /* bp.1 */
>     (gdb) del
>     Delete all breakpoints? (y or n) y
>      (gdb) info thr
>       Id   Target Id         Frame 
>       2    Thread 0x7ffff7fce700 (LWP 22156) "multi-thread-st" test (arg=0x0) at gdb.btrace/multi-thread-step.c:30
>     * 1    Thread 0x7ffff7fcf740 (LWP 22152) "multi-thread-st" test (arg=0x0) at gdb.btrace/multi-thread-step.c:30
>     (gdb) c
>     Continuing.
>     ^C
>     Program received signal SIGINT, Interrupt.
>     0x000000384380c20c in pthread_barrier_wait () from /lib64/libpthread.so.0
>     (gdb) info thr
>       Id   Target Id         Frame 
>       2    Thread 0x7ffff7fce700 (LWP 22156) "multi-thread-st" test (arg=0x0) at gdb.btrace/multi-thread-step.c:30
>     * 1    Thread 0x7ffff7fcf740 (LWP 22152) "multi-thread-st" 0x000000384380c20c in pthread_barrier_wait () from     /lib64/libpthread.so.0
> 
> When I set debug infrun, I get the this:
> 
>     (gdb) del
>     Delete all breakpoints? (y or n) y
>      (gdb)
>     infrun: target_wait (-1, status) =
>     infrun:   -1 [process -1],
>     infrun:   status->kind = no-resumed
>     infrun: TARGET_WAITKIND_NO_RESUMED (ignoring)
>     infrun: prepare_to_wait
> 
> I don't see this with the old query behaviour or when I remove breakpoints like this
> 
>     (gdb) del 1

Hmm, gdb_readline_wrapper believes the target was async to begin
with.  That seems to be an issue with linux_nat_is_async_p.  And
then, gdb_readline_wrapper_cleanup sets the target async again,
which triggers the target_wait call.  It's normal that only
one thread is resumed, because the other thread has an event
pending already.  Normally that works because at the very end of
linux_nat_resume, we'll re-enable async, which, if we were
sync before, tells the event loop to poll them.  But in this
case, we're reaching linux_nat_resume already async, do nothing
wakes up the event loop, and so the pending event is never
collected and handled by infrun.

Let me see if I can come up with a fix.

Thanks,
Pedro Alves

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

* Re: x86_64-m32 internal error for multi-thread-step.exp  [Re: [PATCH v10 06/28] btrace: change branch trace data structure]
  2015-01-22 12:30       ` Metzger, Markus T
  2015-01-22 13:36         ` Pedro Alves
@ 2015-01-22 16:37         ` Jan Kratochvil
  2015-01-23  7:56           ` Metzger, Markus T
  2015-01-23 12:55         ` x86_64-m32 internal error for multi-thread-step.exp [Re: [PATCH v10 06/28] btrace: change branch trace data structure] Patrick Palka
  2 siblings, 1 reply; 58+ messages in thread
From: Jan Kratochvil @ 2015-01-22 16:37 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: Patrick Palka, palves, gdb-patches

On Thu, 22 Jan 2015 13:29:55 +0100, Metzger, Markus T wrote:
> This fail seems to be caused by 588dcc3edbde19f90e76de969dbfa7ab3e17951a
> "Consolidate the custom TUI query hook with the default query hook".  It is not
> related to btrace.

I am sorry I did not retest the commits back to the initial btrace check-in.
As I only now have a hardware capable of btrace I have assumed the FAILs are
there since the initial btrace check-in.


Jan

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

* Linux: make target_is_async_p return false when async is off
  2015-01-22 13:36         ` Pedro Alves
@ 2015-01-22 17:37           ` Pedro Alves
  2015-01-23 10:39             ` Metzger, Markus T
  0 siblings, 1 reply; 58+ messages in thread
From: Pedro Alves @ 2015-01-22 17:37 UTC (permalink / raw)
  To: Metzger, Markus T, Jan Kratochvil, Patrick Palka; +Cc: gdb-patches

On 01/22/2015 01:36 PM, Pedro Alves wrote:
> On 01/22/2015 12:29 PM, Metzger, Markus T wrote:
>>> -----Original Message-----
>>> From: Metzger, Markus T
>>> Sent: Tuesday, January 20, 2015 4:08 PM
>>> To: Jan Kratochvil
>>> Cc: palves@redhat.com; gdb-patches@sourceware.org
>>
>>
>>> I can't reproduce this fail; I don't get that far.  This test fails for me with
>>>
>>> 	FAIL: gdb.btrace/multi-thread-step.exp: continue to breakpoint: cont
>>> to multi-thread-step.c:34 (timeout)
>>
>> This fail seems to be caused by 588dcc3edbde19f90e76de969dbfa7ab3e17951a
>> "Consolidate the custom TUI query hook with the default query hook".  It is not
>> related to btrace.
>>
>> The failing test program looks like this:
>>
>>     pthread_barrier_wait (&barrier);
>>     global = 42; /* bp.1 */
>>     pthread_barrier_wait (&barrier);
>>
>> There are two threads, both are at bp.1 between the two barriers.  When I now
>> delete all breakpoints like this:
>>
>>     (gdb) del
>>     Delete all breakpoints? (y or n) y
>>
>> and then continue the inferior, only the current thread is resumed.  The other
>> thread remains at its current location.  The resumed thread waits at the barrier
>> and the test runs into a timeout.
>>
>> Here's a complete debug session:
>>
>>     (gdb) b 30
>>     Breakpoint 1 at 0x400776: file gdb.btrace/multi-thread-step.c, line 30.
>>     (gdb) r
>>     Starting program: gdb.btrace/multi-thread-step 
>>     [Thread debugging using libthread_db enabled]
>>     Using host libthread_db library "/lib64/libthread_db.so.1".
>>     [New Thread 0x7ffff7fce700 (LWP 22156)]
>>
>>     Breakpoint 1, test (arg=0x0) at gdb.btrace/multi-thread-step.c:30
>>     30	  global = 42; /* bp.1 */
>>     (gdb) del
>>     Delete all breakpoints? (y or n) y
>>      (gdb) info thr
>>       Id   Target Id         Frame 
>>       2    Thread 0x7ffff7fce700 (LWP 22156) "multi-thread-st" test (arg=0x0) at gdb.btrace/multi-thread-step.c:30
>>     * 1    Thread 0x7ffff7fcf740 (LWP 22152) "multi-thread-st" test (arg=0x0) at gdb.btrace/multi-thread-step.c:30
>>     (gdb) c
>>     Continuing.
>>     ^C
>>     Program received signal SIGINT, Interrupt.
>>     0x000000384380c20c in pthread_barrier_wait () from /lib64/libpthread.so.0
>>     (gdb) info thr
>>       Id   Target Id         Frame 
>>       2    Thread 0x7ffff7fce700 (LWP 22156) "multi-thread-st" test (arg=0x0) at gdb.btrace/multi-thread-step.c:30
>>     * 1    Thread 0x7ffff7fcf740 (LWP 22152) "multi-thread-st" 0x000000384380c20c in pthread_barrier_wait () from     /lib64/libpthread.so.0
>>
>> When I set debug infrun, I get the this:
>>
>>     (gdb) del
>>     Delete all breakpoints? (y or n) y
>>      (gdb)
>>     infrun: target_wait (-1, status) =
>>     infrun:   -1 [process -1],
>>     infrun:   status->kind = no-resumed
>>     infrun: TARGET_WAITKIND_NO_RESUMED (ignoring)
>>     infrun: prepare_to_wait
>>
>> I don't see this with the old query behaviour or when I remove breakpoints like this
>>
>>     (gdb) del 1
> 
> Hmm, gdb_readline_wrapper believes the target was async to begin
> with.  That seems to be an issue with linux_nat_is_async_p.  And
> then, gdb_readline_wrapper_cleanup sets the target async again,
> which triggers the target_wait call.  It's normal that only
> one thread is resumed, because the other thread has an event
> pending already.  Normally that works because at the very end of
> linux_nat_resume, we'll re-enable async, which, if we were
> sync before, tells the event loop to poll them.  But in this
> case, we're reaching linux_nat_resume already async, do nothing
> wakes up the event loop, and so the pending event is never
> collected and handled by infrun.
> 
> Let me see if I can come up with a fix.

Here it is.  I added a new test based on gdb.btrace/multi-thread-step.c
that does not depend on btrace.  My machine doesn't do btrace, so I can't
check that one.

------------
From: Pedro Alves <palves@redhat.com>
Subject: [PATCH] Linux: make target_is_async_p return false when async is off

linux_nat_is_async_p currently always returns true, even when the
target is _not_ async.  That confuses
gdb_readline_wrapper/gdb_readline_wrapper_cleanup, which
force-disables target-async while the secondary prompt is active.  As
a result, when gdb_readline_wrapper returns, the target is left async,
even through it was sync to begin with.

That can result in weird bugs, like the one the test added by this
commit exposes.

Ref: https://sourceware.org/ml/gdb-patches/2015-01/msg00592.html

gdb/ChangeLog:
2015-01-22  Pedro Alves  <palves@redhat.com>

	* linux-nat.c (linux_is_async_p): New macro.
	(linux_nat_is_async_p):
	(linux_nat_terminal_inferior): Check whether the target can async
	instead of whether it is already async.
	(linux_nat_terminal_ours): Don't check whether the target is
	async.
	(linux_async_pipe): Use linux_is_async_p.

gdb/testsuite/ChangeLog:
2015-01-22  Pedro Alves  <palves@redhat.com>

	* gdb.threads/continue-pending-after-query.c: New file.
	* gdb.threads/continue-pending-after-query.exp: New file.
---
 gdb/linux-nat.c                                    | 23 +++---
 .../gdb.threads/continue-pending-after-query.c     | 48 ++++++++++++
 .../gdb.threads/continue-pending-after-query.exp   | 90 ++++++++++++++++++++++
 3 files changed, 148 insertions(+), 13 deletions(-)
 create mode 100644 gdb/testsuite/gdb.threads/continue-pending-after-query.c
 create mode 100644 gdb/testsuite/gdb.threads/continue-pending-after-query.exp

diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index be52470..b49cd57 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -219,6 +219,9 @@ struct simple_pid_list *stopped_pids;
    event loop.  */
 static int linux_nat_event_pipe[2] = { -1, -1 };

+/* True if we're currently in async mode.  */
+#define linux_is_async_p() (linux_nat_event_pipe[0] != -1)
+
 /* Flush the event pipe.  */

 static void
@@ -4302,10 +4305,7 @@ linux_trad_target (CORE_ADDR (*register_u_offset)(struct gdbarch *, int, int))
 static int
 linux_nat_is_async_p (struct target_ops *ops)
 {
-  /* NOTE: palves 2008-03-21: We're only async when the user requests
-     it explicitly with the "set target-async" command.
-     Someday, linux will always be async.  */
-  return target_async_permitted;
+  return linux_is_async_p ();
 }

 /* target_can_async_p implementation.  */
@@ -4355,7 +4355,11 @@ static int async_terminal_is_ours = 1;
 static void
 linux_nat_terminal_inferior (struct target_ops *self)
 {
-  if (!target_is_async_p ())
+  /* Like target_terminal_inferior, use target_can_async_p, not
+     target_is_async_p, since at this point the target is not async
+     yet.  If it can async, then we know it will become async prior to
+     resume.  */
+  if (!target_can_async_p ())
     {
       /* Async mode is disabled.  */
       child_terminal_inferior (self);
@@ -4385,13 +4389,6 @@ linux_nat_terminal_inferior (struct target_ops *self)
 static void
 linux_nat_terminal_ours (struct target_ops *self)
 {
-  if (!target_is_async_p ())
-    {
-      /* Async mode is disabled.  */
-      child_terminal_ours (self);
-      return;
-    }
-
   /* GDB should never give the terminal to the inferior if the
      inferior is running in the background (run&, continue&, etc.),
      but claiming it sure should.  */
@@ -4444,7 +4441,7 @@ handle_target_event (int error, gdb_client_data client_data)
 static int
 linux_async_pipe (int enable)
 {
-  int previous = (linux_nat_event_pipe[0] != -1);
+  int previous = linux_is_async_p ();

   if (previous != enable)
     {
diff --git a/gdb/testsuite/gdb.threads/continue-pending-after-query.c b/gdb/testsuite/gdb.threads/continue-pending-after-query.c
new file mode 100644
index 0000000..9510ce8
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/continue-pending-after-query.c
@@ -0,0 +1,48 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2013-2015 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <pthread.h>
+
+static int global;
+
+static void
+break_function (void)
+{
+  global = 42; /* set break here */
+}
+
+static void *
+thread_function (void *arg)
+{
+  break_function ();
+
+  return arg;
+}
+
+int
+main (void)
+{
+  pthread_t th;
+
+  pthread_create (&th, NULL, thread_function, NULL);
+
+  break_function ();
+
+  pthread_join (th, NULL);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.threads/continue-pending-after-query.exp b/gdb/testsuite/gdb.threads/continue-pending-after-query.exp
new file mode 100644
index 0000000..d4d50c9
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/continue-pending-after-query.exp
@@ -0,0 +1,90 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013-2015 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Regression test for a bug that would go like this:
+#
+# - Run to a breakpoint that is hit by two threads (A and B)
+#   simultaneously.
+#
+# - One of the breakpoint hits is processed (e.g., thread A) and
+#   causes a user-visible stop.  The other (thread B) is left pending.
+#
+# - The user deletes the breakpoint with "del", which causes a
+#   confirmation query.
+#
+# - By mistake, that would result in the target being left with async
+#   enabled, even though it wasn't to begin with.
+#
+# - GDB reacts to target async enablement by polling for target
+#   events.  As no thread is resumed the target replies
+#   TARGET_WAITKIND_NO_RESUMED.
+#
+# - The user continues the program, expecting it to exit.  The thread
+#   that has an event pending (thread B) is not really resumed.
+#
+# - But, nothing signals the event loop that there's a pending event
+#   waiting to be collected for thread B, so that event is never
+#   processed, thread B is never resumed and the program never exits.
+#
+# Ref: https://sourceware.org/ml/gdb-patches/2015-01/msg00592.html
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug pthreads}] == -1} {
+    return -1
+}
+
+proc test {} {
+    global srcfile gdb_prompt
+
+    if ![runto_main] {
+	return -1
+    }
+
+    delete_breakpoints
+
+    set bp_line [gdb_get_line_number "set break here" $srcfile]
+
+    gdb_breakpoint "break_function"
+    gdb_continue_to_breakpoint "cont to break_function" ".*$srcfile:$bp_line\r\n.*"
+
+    # Do something that causes a query/secondary prompt.
+
+    set test "delete breakpoints, answer prompt"
+    set saw_prompt 0
+    gdb_test_multiple "delete breakpoints" $test {
+	-re "Delete all breakpoints.*y or n.*$" {
+	    set saw_prompt 1
+	    send_gdb "y\n"
+	    exp_continue
+	}
+	-re "$gdb_prompt $" {
+	    gdb_assert $saw_prompt $test
+	}
+    }
+
+    gdb_continue_to_end "" "continue" 1
+}
+
+# Test a few times to make sure an event is left pending.  At the time
+# of writing, the bug always triggers, but that might naturally depend
+# on machine.
+for {set i 1} {$i <= 10} {incr i} {
+    with_test_prefix "iter $i" {
+	test
+    }
+}
-- 
1.9.3


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

* RE: x86_64-m32 internal error for multi-thread-step.exp  [Re: [PATCH v10 06/28] btrace: change branch trace data structure]
  2015-01-22 16:37         ` x86_64-m32 internal error for multi-thread-step.exp [Re: [PATCH v10 06/28] btrace: change branch trace data structure] Jan Kratochvil
@ 2015-01-23  7:56           ` Metzger, Markus T
  2015-01-23 16:01             ` Metzger, Markus T
                               ` (2 more replies)
  0 siblings, 3 replies; 58+ messages in thread
From: Metzger, Markus T @ 2015-01-23  7:56 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: Patrick Palka, palves, gdb-patches

> -----Original Message-----
> From: gdb-patches-owner@sourceware.org [mailto:gdb-patches-
> owner@sourceware.org] On Behalf Of Jan Kratochvil
> Sent: Thursday, January 22, 2015 5:38 PM
> To: Metzger, Markus T
> Cc: Patrick Palka; palves@redhat.com; gdb-patches@sourceware.org
> Subject: Re: x86_64-m32 internal error for multi-thread-step.exp [Re:
> [PATCH v10 06/28] btrace: change branch trace data structure]
> 
> On Thu, 22 Jan 2015 13:29:55 +0100, Metzger, Markus T wrote:
> > This fail seems to be caused by
> 588dcc3edbde19f90e76de969dbfa7ab3e17951a
> > "Consolidate the custom TUI query hook with the default query hook".  It is
> not
> > related to btrace.
> 
> I am sorry I did not retest the commits back to the initial btrace check-in.
> As I only now have a hardware capable of btrace I have assumed the FAILs
> are
> there since the initial btrace check-in.

I didn't mean to complain; I just wanted to point out that this fail is more
generic and affects debugging  multi-threaded inferiors in general.

The other fails you reported do seem to be there since the beginning.

It looks like the is_kernel_addr heuristic gets it wrong for 32bit inferiors
on 64-bit systems.  The heuristic checks whether the most significant bit
is set.  On 64-bit systems, the kernel is loaded into the high 64-bit area
leaving the entire 32-bit address space to 32-bit processes.  GDB checks
bit 31 since it is a 32-bit inferior.  It should still check bit 63 since it is a 64-bit
kernel.  Does GDB even know that the 32-bit inferior is running on a 64-bit
system?

There also seem to be some issues in reverse/replay stepping.  The
reverse-next command keeps going when it reached the previous
source line; same for replay next.  I need to investigate why this is.


> As I only now have a hardware capable of btrace

That's good.  I hope this feature is useful for your own debugging work.
Let me know how you like it.

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

* RE: Linux: make target_is_async_p return false when async is off
  2015-01-22 17:37           ` Linux: make target_is_async_p return false when async is off Pedro Alves
@ 2015-01-23 10:39             ` Metzger, Markus T
  2015-01-23 12:34               ` Pedro Alves
  0 siblings, 1 reply; 58+ messages in thread
From: Metzger, Markus T @ 2015-01-23 10:39 UTC (permalink / raw)
  To: Pedro Alves, Jan Kratochvil, Patrick Palka; +Cc: gdb-patches

> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Thursday, January 22, 2015 6:38 PM
> To: Metzger, Markus T; Jan Kratochvil; Patrick Palka
> Cc: gdb-patches@sourceware.org
> Subject: Linux: make target_is_async_p return false when async is off


> > Let me see if I can come up with a fix.
> 
> Here it is.  I added a new test based on gdb.btrace/multi-thread-step.c
> that does not depend on btrace.  My machine doesn't do btrace, so I can't
> check that one.

It also fixes the btrace test fail.  Thanks!

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

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

* Re: Linux: make target_is_async_p return false when async is off
  2015-01-23 10:39             ` Metzger, Markus T
@ 2015-01-23 12:34               ` Pedro Alves
  0 siblings, 0 replies; 58+ messages in thread
From: Pedro Alves @ 2015-01-23 12:34 UTC (permalink / raw)
  To: Metzger, Markus T, Jan Kratochvil, Patrick Palka; +Cc: gdb-patches

On 01/23/2015 07:56 AM, Metzger, Markus T wrote:
>> From: Pedro Alves [mailto:palves@redhat.com]
>>> Let me see if I can come up with a fix.
>>
>> Here it is.  I added a new test based on gdb.btrace/multi-thread-step.c
>> that does not depend on btrace.  My machine doesn't do btrace, so I can't
>> check that one.
> 
> It also fixes the btrace test fail.  Thanks!

Alright, pushed to both master and 7.9 branch.

Thanks,
Pedro Alves

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

* Re: x86_64-m32 internal error for multi-thread-step.exp [Re: [PATCH v10 06/28] btrace: change branch trace data structure]
  2015-01-22 12:30       ` Metzger, Markus T
  2015-01-22 13:36         ` Pedro Alves
  2015-01-22 16:37         ` x86_64-m32 internal error for multi-thread-step.exp [Re: [PATCH v10 06/28] btrace: change branch trace data structure] Jan Kratochvil
@ 2015-01-23 12:55         ` Patrick Palka
  2 siblings, 0 replies; 58+ messages in thread
From: Patrick Palka @ 2015-01-23 12:55 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: Jan Kratochvil, palves, gdb-patches

On Thu, Jan 22, 2015 at 7:29 AM, Metzger, Markus T
<markus.t.metzger@intel.com> wrote:
>> -----Original Message-----
>> From: Metzger, Markus T
>> Sent: Tuesday, January 20, 2015 4:08 PM
>> To: Jan Kratochvil
>> Cc: palves@redhat.com; gdb-patches@sourceware.org
>
>
>> I can't reproduce this fail; I don't get that far.  This test fails for me with
>>
>>       FAIL: gdb.btrace/multi-thread-step.exp: continue to breakpoint: cont
>> to multi-thread-step.c:34 (timeout)
>
> This fail seems to be caused by 588dcc3edbde19f90e76de969dbfa7ab3e17951a
> "Consolidate the custom TUI query hook with the default query hook".  It is not
> related to btrace.
>
> The failing test program looks like this:
>
>     pthread_barrier_wait (&barrier);
>     global = 42; /* bp.1 */
>     pthread_barrier_wait (&barrier);
>
> There are two threads, both are at bp.1 between the two barriers.  When I now
> delete all breakpoints like this:
>
>     (gdb) del
>     Delete all breakpoints? (y or n) y
>
> and then continue the inferior, only the current thread is resumed.  The other
> thread remains at its current location.  The resumed thread waits at the barrier
> and the test runs into a timeout.
>
> Here's a complete debug session:
>
>     (gdb) b 30
>     Breakpoint 1 at 0x400776: file gdb.btrace/multi-thread-step.c, line 30.
>     (gdb) r
>     Starting program: gdb.btrace/multi-thread-step
>     [Thread debugging using libthread_db enabled]
>     Using host libthread_db library "/lib64/libthread_db.so.1".
>     [New Thread 0x7ffff7fce700 (LWP 22156)]
>
>     Breakpoint 1, test (arg=0x0) at gdb.btrace/multi-thread-step.c:30
>     30    global = 42; /* bp.1 */
>     (gdb) del
>     Delete all breakpoints? (y or n) y
>      (gdb) info thr
>       Id   Target Id         Frame
>       2    Thread 0x7ffff7fce700 (LWP 22156) "multi-thread-st" test (arg=0x0) at gdb.btrace/multi-thread-step.c:30
>     * 1    Thread 0x7ffff7fcf740 (LWP 22152) "multi-thread-st" test (arg=0x0) at gdb.btrace/multi-thread-step.c:30
>     (gdb) c
>     Continuing.
>     ^C
>     Program received signal SIGINT, Interrupt.
>     0x000000384380c20c in pthread_barrier_wait () from /lib64/libpthread.so.0
>     (gdb) info thr
>       Id   Target Id         Frame
>       2    Thread 0x7ffff7fce700 (LWP 22156) "multi-thread-st" test (arg=0x0) at gdb.btrace/multi-thread-step.c:30
>     * 1    Thread 0x7ffff7fcf740 (LWP 22152) "multi-thread-st" 0x000000384380c20c in pthread_barrier_wait () from     /lib64/libpthread.so.0
>
> When I set debug infrun, I get the this:
>
>     (gdb) del
>     Delete all breakpoints? (y or n) y
>      (gdb)
>     infrun: target_wait (-1, status) =
>     infrun:   -1 [process -1],
>     infrun:   status->kind = no-resumed
>     infrun: TARGET_WAITKIND_NO_RESUMED (ignoring)
>     infrun: prepare_to_wait
>
> I don't see this with the old query behaviour or when I remove breakpoints like this
>
>     (gdb) del 1

Sorry for the breakage.  I had noticed that multi-thread-step.exp
sometimes fails but I did not realize the non-deterministic failure
was introduced by my patch.  I thought it was a preexisting failure,
but of course I should have empirically confirmed this belief.

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

* RE: x86_64-m32 internal error for multi-thread-step.exp  [Re: [PATCH v10 06/28] btrace: change branch trace data structure]
  2015-01-23  7:56           ` Metzger, Markus T
@ 2015-01-23 16:01             ` Metzger, Markus T
  2015-01-23 16:33             ` Metzger, Markus T
  2015-01-25 19:56             ` record btrace experience [Re: x86_64-m32 internal error for multi-thread-step.exp [Re: [PATCH v10 06/28] btrace: change branch trace data structure]] Jan Kratochvil
  2 siblings, 0 replies; 58+ messages in thread
From: Metzger, Markus T @ 2015-01-23 16:01 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: palves, gdb-patches

> -----Original Message-----
> From: Metzger, Markus T
> Sent: Friday, January 23, 2015 8:46 AM


> There also seem to be some issues in reverse/replay stepping.  The
> reverse-next command keeps going when it reached the previous
> source line; same for replay next.  I need to investigate why this is.

The 32-bit _dl_runtime_resolve returns to the resolved function.
The 64-bit _dl_runtime_resolve jumps to the resolved function.

The return causes btrace to search for the function in the current
stack back trace.  Since it doesn't find it, it assumes that the return
goes to some outer function that has not been recorded.

When we continue processing the trace, we build a new stack
back trace with the same function names but different frame id's.
They will look the same when using the bt command but stepping
won't be able to detect when stepping into a subroutine.

My current thinking is that I'd add a special case for this (see below).
I can't think of a general case where you would use a return instruction
to transfer control to a function that didn't call you.  If you know of
more such cases, please let me know.


diff --git a/gdb/btrace.c b/gdb/btrace.c
index fd543ef..db31788 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -524,7 +524,24 @@ ftrace_update_function (struct btrace_function *bfun, CORE_ADDR pc)
       switch (last->iclass)
        {
        case BTRACE_INSN_RETURN:
-         return ftrace_new_return (bfun, mfun, fun);
+         {
+           const char *fname;
+
+           /* On some systems, _dl_runtime_resolve returns to the resolved
+              function instead of jumping to it.  From our perspective,
+              however, this is a tailcall.
+              If we treated it as return, we wouldn't be able to find the
+              resolved function in our stack back trace.  Hence, we would
+              lose the current stack back trace and start anew with an empty
+              back trace.  When the resolved function returns, we would then
+              create a stack back trace with the same function names but
+              different frame id's.  This will confuse stepping.  */
+           fname = ftrace_print_function_name (bfun);
+           if (strcmp (fname, "_dl_runtime_resolve") == 0)
+             return ftrace_new_tailcall (bfun, mfun, fun);
+
+           return ftrace_new_return (bfun, mfun, fun);
+         }
 
        case BTRACE_INSN_CALL:
          /* Ignore calls to the next instruction.  They are used for PIC.  */

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

* RE: x86_64-m32 internal error for multi-thread-step.exp  [Re: [PATCH v10 06/28] btrace: change branch trace data structure]
  2015-01-23  7:56           ` Metzger, Markus T
  2015-01-23 16:01             ` Metzger, Markus T
@ 2015-01-23 16:33             ` Metzger, Markus T
  2015-01-27 18:05               ` Pedro Alves
  2015-01-25 19:56             ` record btrace experience [Re: x86_64-m32 internal error for multi-thread-step.exp [Re: [PATCH v10 06/28] btrace: change branch trace data structure]] Jan Kratochvil
  2 siblings, 1 reply; 58+ messages in thread
From: Metzger, Markus T @ 2015-01-23 16:33 UTC (permalink / raw)
  To: Jan Kratochvil, palves; +Cc: gdb-patches

> -----Original Message-----
> From: Metzger, Markus T
> Sent: Friday, January 23, 2015 8:46 AM
> To: Jan Kratochvil


> It looks like the is_kernel_addr heuristic gets it wrong for 32bit inferiors
> on 64-bit systems.  The heuristic checks whether the most significant bit
> is set.  On 64-bit systems, the kernel is loaded into the high 64-bit area
> leaving the entire 32-bit address space to 32-bit processes.  GDB checks
> bit 31 since it is a 32-bit inferior.  It should still check bit 63 since it is a 64-bit
> kernel.  Does GDB even know that the 32-bit inferior is running on a 64-bit
> system?

If we can fix that heuristic, it's just a matter of increasing the trace buffer
size - the trace is bigger on 32-bit systems.

I get the pointer size of the inferior via gdbarch_ptr_bit().  Does GDB also
know the pointer size of the kernel that runs the inferior?

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

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

* record btrace experience  [Re: x86_64-m32 internal error for multi-thread-step.exp  [Re: [PATCH v10 06/28] btrace: change branch trace data structure]]
  2015-01-23  7:56           ` Metzger, Markus T
  2015-01-23 16:01             ` Metzger, Markus T
  2015-01-23 16:33             ` Metzger, Markus T
@ 2015-01-25 19:56             ` Jan Kratochvil
  2015-01-26 12:41               ` Metzger, Markus T
  2 siblings, 1 reply; 58+ messages in thread
From: Jan Kratochvil @ 2015-01-25 19:56 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: gdb-patches

On Fri, 23 Jan 2015 08:45:31 +0100, Metzger, Markus T wrote:
> > As I only now have a hardware capable of btrace
> 
> That's good.  I hope this feature is useful for your own debugging work.
> Let me know how you like it.

The functionality itself is really great, for example in this case - where did
kickoff() got called from?

------------------------------------------------------------------------------
gdb -ex 'tb main' -ex r -ex 'record btrace' -ex 'b *kickoff' -ex c --args docker -d 
[...]
Breakpoint 2, kickoff () at ../../../gcchead/libgo/runtime/proc.c:204
204		if(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0)
(gdb) bt
#0  kickoff () at ../../../gcchead/libgo/runtime/proc.c:204
#1  0x00007ffff6e0a510 in ?? () from /lib64/libc.so.6
#2  0x0000000000000000 in ?? ()
(gdb) reverse-stepi
setcontext () at ../sysdeps/unix/sysv/linux/x86_64/setcontext.S:99
99		ret
(gdb) bt
#0  setcontext () at ../sysdeps/unix/sysv/linux/x86_64/setcontext.S:99
#1  0x00007ffff7df00e7 in _dl_runtime_resolve () at ../sysdeps/x86_64/dl-trampoline.S:115
#2  0x0000000000738f8c in ?? ()
#3  0x0000000000739470 in setcontext@plt ()
#4  0x0000000000a62447 in runtime_gogo (newg=newg@entry=<unavailable>) at ../../../gcchead/libgo/runtime/proc.c:226
#5  0x0000000000a624ea in execute (gp=gp@entry=<unavailable>) at ../../../gcchead/libgo/runtime/proc.c:1545
#6  0x0000000000a63f84 in schedule () at ../../../gcchead/libgo/runtime/proc.c:1752
#7  0x0000000000a642ca in runtime_mstart (mp=<optimized out>) at ../../../gcchead/libgo/runtime/proc.c:1003
#8  0x000000000073a79a in main (argc=<optimized out>, argv=<optimized out>) at ../../../gcchead/libgo/runtime/go-main.c:43
Backtrace stopped: not enough registers or memory available to unwind further
(gdb) _
------------------------------------------------------------------------------

Plain 'record' I have stopped after 2 minutes of run with no result:
gdb -ex 'set record insn-number-max 10000000' -ex 'tb main' -ex r -ex 'record' -ex 'b *kickoff' -ex c --args docker -d 

Although one cannot leave the history too long, despite it is set to
'unlimited'.  I have no idea why now, for example:

------------------------------------------------------------------------------
gdb -ex 'set record instruction-history-size unlimited' -ex 'set record function-call-history-size unlimited' -ex 'tb main' -ex r -ex 'record btrace' -ex 'catch fork' -ex c --args docker -d 
GNU gdb (GDB) 7.9.50.20150124-cvs
[...]
Catchpoint 2 (forked process 13346), syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38
38		cmpq $-4095, %rax	/* Check %rax for error.  */
(gdb) bt
#0  syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38
#1  0x0000000000b117f3 in syscall.RawSyscall6 (trap=trap@entry=56, a1=<optimized out>, a2=a2@entry=0, a3=a3@entry=0, a4=a4@entry=0, a5=0, a6=0) at ../../../gcchead/libgo/go/syscall/syscall_unix.go:92
#2  0x0000000000b1e122 in syscall.forkAndExecInChild (pipe=7, sys=0x1410fa0 <syscall.zeroSysProcAttr>, attr=<optimized out>, dir=0x0, chroot=0x0, param=..., param=..., argv0=0xc20808e740 "/usr/sbin/iptables") at ../../../gcchead/libgo/go/syscall/exec_linux.go:72
#3  syscall.forkExec (argv0=..., argv=..., attr=<optimized out>) at ../../../gcchead/libgo/go/syscall/exec_unix.go:227
#4  0x0000000000b1eaa9 in syscall.StartProcess (argv0=..., argv=..., attr=attr@entry=0xc208013090) at ../../../gcchead/libgo/go/syscall/exec_unix.go:273
#5  0x0000000000ac6a80 in os.startProcess (attr=<optimized out>, argv=..., name=...) at ../../../gcchead/libgo/go/os/exec_posix.go:45
#6  os.StartProcess (name=..., argv=..., attr=<optimized out>) at ../../../gcchead/libgo/go/os/doc.go:24
#7  0x0000000000c2462b in os_exec.Start.pN11_os_exec.Cmd (param=param@entry=0xc208037180) at ../../../gcchead/libgo/go/os/exec/exec.go:304
#8  0x0000000000c247e4 in os_exec.Run.pN11_os_exec.Cmd (c=<optimized out>) at ../../../gcchead/libgo/go/os/exec/exec.go:236
#9  0x000000000080c021 in iptables.$init0 () at /home/jkratoch/redhat/fedora/docker-io/master/docker-1.4.1/_build/src/github.com/docker/docker/pkg/iptables/iptables.go:43
#10 0x0000000000810764 in github_com_docker_docker_pkg_iptables..import () at /home/jkratoch/redhat/fedora/docker-io/master/docker-1.4.1/_build/src/github.com/docker/docker/pkg/iptables/iptables.go:42
#11 0x000000000073f648 in main.init () at /home/jkratoch/redhat/fedora/docker-io/master/docker-1.4.1/docker/daemon.go:3
#12 0x0000000000a65034 in runtime_main (dummy=<optimized out>) at ../../../gcchead/libgo/runtime/proc.c:514
#13 0x0000000000a631bf in kickoff () at ../../../gcchead/libgo/runtime/proc.c:211
#14 0x00007ffff6e0a510 in ?? () from /lib64/libc.so.6
#15 0x0000000000000000 in ?? ()
(gdb) b *kickoff
Breakpoint 3 at 0xa63190: file ../../../gcchead/libgo/runtime/proc.c, line 204.
(gdb) reverse-continue 
Continuing.

No more reverse-execution history.
syscall.BytePtrFromString (s=...) at ../../../gcchead/libgo/go/syscall/syscall.go:57
57	// If s contains a NUL byte this function panics instead of
(gdb) show record instruction-history-size 
Number of instructions to print in "record instruction-history" is unlimited.
(gdb) show record function-call-history-size 
Number of functions to print in "record function-call-history" is unlimited.
(gdb) bt
#0  syscall.BytePtrFromString (s=...) at ../../../gcchead/libgo/go/syscall/syscall.go:57
#1  0x0000000000b0f536 in syscall.SlicePtrFromStrings (ss=...) at ../../../gcchead/libgo/go/syscall/exec_unix.go:121
#2  0x0000000000b1db8d in syscall.forkExec (argv0=..., argv=..., 
    attr=<error reading variable: Registers are not available in btrace record history>)
    at ../../../gcchead/libgo/go/syscall/exec_unix.go:192
Backtrace stopped: not enough registers or memory available to unwind further
(gdb) _
------------------------------------------------------------------------------

BTW these defaults sometimes did limit my use of btrace but right now I do not
have a reproducer where it works with setting them unlimited while it does not
while leaving them as they are:
	(gdb) show record instruction-history-size
	Number of instructions to print in "record instruction-history" is 10.
	(gdb) show record function-call-history-size
	Number of functions to print in "record function-call-history" is 10.


The problem I see is how to transparently enable btrace on some host.

Instead of
	-ex r
one has to type
	-ex start -ex 'set record instruction-history-size unlimited' -ex 'set record function-call-history-size unlimited' -ex 'record btrace' -ex c
which is really not convenient.  Also "start" will stop at the high-level
language main-like function while we may want to start recording earlier, so
one may have to do:
	-ex 'tb main' -ex r -ex 'set record instruction-history-size unlimited' -ex 'set record function-call-history-size unlimited' -ex 'record btrace' -ex c


I have tried to enable btrace transparently from ~/.gdbinit which is possible
for the 'start' command but I failed how to transparently wrap
the 'run' command:

	set record instruction-history-size   unlimited
	set record function-call-history-size unlimited
	define hookpost-start
		record btrace
	end

	# internal-error: go_symbol_package_name: Assertion `SYMBOL_LANGUAGE (sym) == language_go' failed.
	# https://sourceware.org/bugzilla/show_bug.cgi?id=17876
	#define hook-run
	#       tb *_start
	#end
	#define hookpost-run
	#       continue
	#end

	# This breaks cases like: -ex 'b constructor' -ex run
	## Really redefine built-in command "run"? (y or n) [answered Y; input not from terminal]
	#set confirm no
	#define run
	#       start $arg0 $arg1 $arg2 $arg3 $arg4 $arg5 $arg6 $arg7 $arg8 $arg9
	#       continue
	#end
	#set confirm yes

Obviously it would be nice to be able to do just:
	echo 'record btrace' >>~/.gdbinit


Thanks,
Jan

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

* RE: record btrace experience  [Re: x86_64-m32 internal error for multi-thread-step.exp  [Re: [PATCH v10 06/28] btrace: change branch trace data structure]]
  2015-01-25 19:56             ` record btrace experience [Re: x86_64-m32 internal error for multi-thread-step.exp [Re: [PATCH v10 06/28] btrace: change branch trace data structure]] Jan Kratochvil
@ 2015-01-26 12:41               ` Metzger, Markus T
  2015-01-27  8:07                 ` Jan Kratochvil
  0 siblings, 1 reply; 58+ messages in thread
From: Metzger, Markus T @ 2015-01-26 12:41 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

> -----Original Message-----
> From: Jan Kratochvil [mailto:jan.kratochvil@redhat.com]
> Sent: Sunday, January 25, 2015 8:13 PM

Thanks a lot for the feedback.

> BTW these defaults sometimes did limit my use of btrace but right now I do
> not
> have a reproducer where it works with setting them unlimited while it does
> not
> while leaving them as they are:
> 	(gdb) show record instruction-history-size
> 	Number of instructions to print in "record instruction-history" is 10.
> 	(gdb) show record function-call-history-size
> 	Number of functions to print in "record function-call-history" is 10.

Regarding instruction-history-size and function-call-history-size,
they only affect the number of lines displayed in the "record
instruction-history" and "record function-call-history" commands.  They
should have no impact on recording or on the backtrace command.

I think what you are looking for is a command to set the buffer size.
See https://sourceware.org/ml/gdb-patches/2014-11/msg00462.html.


> The problem I see is how to transparently enable btrace on some host.
> 
> Instead of
> 	-ex r
> one has to type
> 	-ex start -ex 'set record instruction-history-size unlimited' -ex 'set
> record function-call-history-size unlimited' -ex 'record btrace' -ex c
> which is really not convenient.  
[...]
> Obviously it would be nice to be able to do just:
> 	echo 'record btrace' >>~/.gdbinit

Agreed.

I guess you know the record architecture better than I do.  The record
command pushes a new record target onto the target stack. This requires
a running inferior.

We could try to instead install a hook to delay pushing the record target
until we have an inferior.  The hook will remain installed when the inferior
terminates or is rerun; "record stop" will remove the hook.

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

* Re: record btrace experience  [Re: x86_64-m32 internal error for multi-thread-step.exp  [Re: [PATCH v10 06/28] btrace: change branch trace data structure]]
  2015-01-26 12:41               ` Metzger, Markus T
@ 2015-01-27  8:07                 ` Jan Kratochvil
  2015-01-27 15:52                   ` Pedro Alves
  2015-01-29 19:28                   ` Metzger, Markus T
  0 siblings, 2 replies; 58+ messages in thread
From: Jan Kratochvil @ 2015-01-27  8:07 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: gdb-patches

On Mon, 26 Jan 2015 08:59:59 +0100, Metzger, Markus T wrote:
> I think what you are looking for is a command to set the buffer size.
> See https://sourceware.org/ml/gdb-patches/2014-11/msg00462.html.

Yes, definitely.  But I see the patch is still pending/unapproved.


Jan

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

* Re: record btrace experience  [Re: x86_64-m32 internal error for multi-thread-step.exp  [Re: [PATCH v10 06/28] btrace: change branch trace data structure]]
  2015-01-27  8:07                 ` Jan Kratochvil
@ 2015-01-27 15:52                   ` Pedro Alves
  2015-01-29 19:28                   ` Metzger, Markus T
  1 sibling, 0 replies; 58+ messages in thread
From: Pedro Alves @ 2015-01-27 15:52 UTC (permalink / raw)
  To: Jan Kratochvil, Metzger, Markus T; +Cc: gdb-patches

On 01/26/2015 09:55 PM, Jan Kratochvil wrote:
> On Mon, 26 Jan 2015 08:59:59 +0100, Metzger, Markus T wrote:
>> I think what you are looking for is a command to set the buffer size.
>> See https://sourceware.org/ml/gdb-patches/2014-11/msg00462.html.
> 
> Yes, definitely.  But I see the patch is still pending/unapproved.

Sorry, I had lost track of that.  I reviewed v2 of the series now.
It's mostly good to go, except a few minor details.

Thanks,
Pedro Alves

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

* Re: x86_64-m32 internal error for multi-thread-step.exp  [Re: [PATCH v10 06/28] btrace: change branch trace data structure]
  2015-01-23 16:33             ` Metzger, Markus T
@ 2015-01-27 18:05               ` Pedro Alves
  2015-01-29 16:28                 ` Metzger, Markus T
  0 siblings, 1 reply; 58+ messages in thread
From: Pedro Alves @ 2015-01-27 18:05 UTC (permalink / raw)
  To: Metzger, Markus T, Jan Kratochvil; +Cc: gdb-patches

On 01/23/2015 02:35 PM, Metzger, Markus T wrote:
> I get the pointer size of the inferior via gdbarch_ptr_bit().  Does GDB also
> know the pointer size of the kernel that runs the inferior?

It doesn't.  I don't know offhand how to get at it.  Maybe
something in /proc tells you.

Thanks,
Pedro Alves

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

* RE: x86_64-m32 internal error for multi-thread-step.exp  [Re: [PATCH v10 06/28] btrace: change branch trace data structure]
  2015-01-27 18:05               ` Pedro Alves
@ 2015-01-29 16:28                 ` Metzger, Markus T
  0 siblings, 0 replies; 58+ messages in thread
From: Metzger, Markus T @ 2015-01-29 16:28 UTC (permalink / raw)
  To: Pedro Alves, Jan Kratochvil; +Cc: gdb-patches

> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Tuesday, January 27, 2015 12:29 PM
> To: Metzger, Markus T; Jan Kratochvil
> Cc: gdb-patches@sourceware.org
> Subject: Re: x86_64-m32 internal error for multi-thread-step.exp [Re:
> [PATCH v10 06/28] btrace: change branch trace data structure]
> 
> On 01/23/2015 02:35 PM, Metzger, Markus T wrote:
> > I get the pointer size of the inferior via gdbarch_ptr_bit().  Does GDB also
> > know the pointer size of the kernel that runs the inferior?
> 
> It doesn't.  I don't know offhand how to get at it.  Maybe
> something in /proc tells you.

I'm using uname's machine string.  This seems to work.

I've put the fixes into the series; I'll send an updated version later today.

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

* RE: record btrace experience  [Re: x86_64-m32 internal error for multi-thread-step.exp  [Re: [PATCH v10 06/28] btrace: change branch trace data structure]]
  2015-01-27  8:07                 ` Jan Kratochvil
  2015-01-27 15:52                   ` Pedro Alves
@ 2015-01-29 19:28                   ` Metzger, Markus T
  1 sibling, 0 replies; 58+ messages in thread
From: Metzger, Markus T @ 2015-01-29 19:28 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: Monday, January 26, 2015 10:55 PM
> To: Metzger, Markus T
> Cc: gdb-patches@sourceware.org
> Subject: Re: record btrace experience [Re: x86_64-m32 internal error for
> multi-thread-step.exp [Re: [PATCH v10 06/28] btrace: change branch trace
> data structure]]
> 
> On Mon, 26 Jan 2015 08:59:59 +0100, Metzger, Markus T wrote:
> > I think what you are looking for is a command to set the buffer size.
> > See https://sourceware.org/ml/gdb-patches/2014-11/msg00462.html.
> 
> Yes, definitely.  But I see the patch is still pending/unapproved.

Even with this patch, "unlimited" is not advisable.  An unlimited buffer size
corresponds to 4MB and can easily hold 1Minsn.  It'll take some time to
process the recorded trace.

Here's an old patch to improve this somewhat:
https://www.sourceware.org/ml/gdb-patches/2014-06/msg00848.html.
I'll rebase and resend it after the current series has been merged.

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

end of thread, other threads:[~2015-01-29 16:33 UTC | newest]

Thread overview: 58+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-01-14  8:05 [PATCH v10 00/28] record-btrace: reverse Markus Metzger
2014-01-14  8:04 ` [PATCH v10 01/28] btrace, test: fix multi-line btrace tests Markus Metzger
2014-01-14  8:04 ` [PATCH v10 14/28] record-btrace: supply register target methods Markus Metzger
2014-01-14  8:04 ` [PATCH v10 18/28] record-btrace, frame: supply target-specific unwinder Markus Metzger
2014-01-14  8:04 ` [PATCH v10 20/28] record-btrace: provide xfer_partial target method Markus Metzger
2014-01-15 15:51   ` Pedro Alves
2014-01-14  8:04 ` [PATCH v10 26/28] record-btrace: show trace from enable location Markus Metzger
2014-01-14  8:04 ` [PATCH v10 10/28] record-btrace: optionally indent function call history Markus Metzger
2014-01-14 16:07   ` Eli Zaretskii
2014-01-14  8:04 ` [PATCH v10 05/28] frame: add frame_id_build_unavailable_stack_special Markus Metzger
2014-01-14  8:04 ` [PATCH v10 03/28] btrace: uppercase btrace_read_type Markus Metzger
2014-01-14  8:05 ` [PATCH v10 08/28] record-btrace: start counting at one Markus Metzger
2014-01-14  8:05 ` [PATCH v10 25/28] btrace, gdbserver: read branch trace incrementally Markus Metzger
2014-01-16 17:57   ` Tom Tromey
2014-01-17  8:28     ` Metzger, Markus T
2014-01-20  5:44       ` Tom Tromey
2014-01-14  8:05 ` [PATCH v10 16/28] frame, cfa: check unwind stop reason first Markus Metzger
2014-01-14  8:05 ` [PATCH v10 19/28] target, breakpoint: allow insert/remove breakpoint to be forwarded Markus Metzger
2014-01-15 15:52   ` Pedro Alves
2014-01-14  8:05 ` [PATCH v10 02/28] btrace, linux: fix memory leak when reading branch trace Markus Metzger
2014-01-14  8:05 ` [PATCH v10 04/28] gdbarch: add instruction predicate methods Markus Metzger
2014-01-14  8:05 ` [PATCH v10 11/28] record-btrace: make ranges include begin and end Markus Metzger
2014-01-14  8:05 ` [PATCH v10 23/28] record-btrace: add record goto target methods Markus Metzger
2014-01-14  8:05 ` [PATCH v10 17/28] frame: do not assume unwinding will succeed Markus Metzger
2014-01-14  8:05 ` [PATCH v10 12/28] btrace: add replay position to btrace thread info Markus Metzger
2014-01-14  8:05 ` [PATCH v10 24/28] record-btrace: extend unwinder Markus Metzger
2014-01-14  8:05 ` [PATCH v10 06/28] btrace: change branch trace data structure Markus Metzger
2015-01-08 20:49   ` x86_64-m32 internal error for multi-thread-step.exp [Re: [PATCH v10 06/28] btrace: change branch trace data structure] Jan Kratochvil
2015-01-20 15:19     ` Metzger, Markus T
2015-01-22 12:30       ` Metzger, Markus T
2015-01-22 13:36         ` Pedro Alves
2015-01-22 17:37           ` Linux: make target_is_async_p return false when async is off Pedro Alves
2015-01-23 10:39             ` Metzger, Markus T
2015-01-23 12:34               ` Pedro Alves
2015-01-22 16:37         ` x86_64-m32 internal error for multi-thread-step.exp [Re: [PATCH v10 06/28] btrace: change branch trace data structure] Jan Kratochvil
2015-01-23  7:56           ` Metzger, Markus T
2015-01-23 16:01             ` Metzger, Markus T
2015-01-23 16:33             ` Metzger, Markus T
2015-01-27 18:05               ` Pedro Alves
2015-01-29 16:28                 ` Metzger, Markus T
2015-01-25 19:56             ` record btrace experience [Re: x86_64-m32 internal error for multi-thread-step.exp [Re: [PATCH v10 06/28] btrace: change branch trace data structure]] Jan Kratochvil
2015-01-26 12:41               ` Metzger, Markus T
2015-01-27  8:07                 ` Jan Kratochvil
2015-01-27 15:52                   ` Pedro Alves
2015-01-29 19:28                   ` Metzger, Markus T
2015-01-23 12:55         ` x86_64-m32 internal error for multi-thread-step.exp [Re: [PATCH v10 06/28] btrace: change branch trace data structure] Patrick Palka
2014-01-14  8:05 ` [PATCH v10 09/28] btrace: increase buffer size Markus Metzger
2014-01-14  8:05 ` [PATCH v10 21/28] record-btrace: add to_wait and to_resume target methods Markus Metzger
2014-01-14  8:05 ` [PATCH v10 13/28] Add target_ops argument to to_prepare_to_store Markus Metzger
2014-01-14  8:05 ` [PATCH v10 22/28] record-btrace: provide target_find_new_threads method Markus Metzger
2014-01-14  8:05 ` [PATCH v10 15/28] frame, backtrace: allow targets to supply a frame unwinder Markus Metzger
2014-01-14  8:05 ` [PATCH v10 07/28] record-btrace: fix insn range in function call history Markus Metzger
2014-01-14  8:05 ` [PATCH v10 28/28] record-btrace: add (reverse-)stepping support Markus Metzger
2014-01-14  8:05 ` [PATCH v10 27/28] target: allow decr_pc_after_break to be defined by the target Markus Metzger
2014-01-15 15:54 ` [PATCH v10 00/28] record-btrace: reverse Pedro Alves
2014-01-16 12:01   ` Metzger, Markus T
2014-01-16 12:37     ` Pedro Alves
2014-01-16 14:35     ` Tom Tromey

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