public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v9 01/29] btrace, test: fix multi-line btrace tests
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (16 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 27/29] record-btrace: show trace from enable location Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 18:01   ` Pedro Alves
  2013-12-19 16:45 ` [PATCH v9 18/29] frame: do not assume unwinding will succeed Markus Metzger
                   ` (11 subsequent siblings)
  29 siblings, 1 reply; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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.

2013-12-19  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 97447e1..8bc44e7 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 c1a61b7..528ae19 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] 66+ messages in thread

* [PATCH v9 25/29] record-btrace: extend unwinder
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
  2013-12-19 16:45 ` [PATCH v9 22/29] record-btrace: add to_wait and to_resume target methods Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 16:45 ` [PATCH v9 10/29] btrace: increase buffer size Markus Metzger
                   ` (27 subsequent siblings)
  29 siblings, 0 replies; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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/frame.h                              |   1 -
 gdb/record-btrace.c                      | 288 +++++++++++++++++++++++++++++--
 gdb/record.h                             |   4 +
 gdb/testsuite/gdb.btrace/record_goto.exp |  12 ++
 gdb/testsuite/gdb.btrace/tailcall.exp    |  15 ++
 8 files changed, 321 insertions(+), 11 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 1ff2b5a..f387604 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -107,6 +107,8 @@ info exceptions REGEXP
   are listed.
 
 * The btrace record target now supports the 'record goto' command.
+  For locations inside the execution trace, the back trace is computed
+  based on the information stored in the execution trace.
 
 * New options
 
diff --git a/gdb/btrace.c b/gdb/btrace.c
index ad15481..1f35796 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -754,6 +754,10 @@ btrace_clear (struct thread_info *tp)
 
   DEBUG ("clear thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
 
+  /* Make sure btrace frames that may hold a pointer into the branch
+     trace data are destroyed.  */
+  reinit_frame_cache ();
+
   btinfo = &tp->btrace;
 
   it = btinfo->begin;
diff --git a/gdb/dwarf2-frame.c b/gdb/dwarf2-frame.c
index adad2dc..3213b1b 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/frame.h b/gdb/frame.h
index 00943ad..f9f2a30 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -801,5 +801,4 @@ extern int frame_unwinder_is (struct frame_info *fi,
 
 extern int frame_is_tailcall (struct frame_info *fi);
 
-
 #endif /* !defined (FRAME_H)  */
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 1d090e7..8e27f0f 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);
@@ -970,13 +993,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.  */
@@ -985,7 +1095,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.  */
@@ -995,8 +1125,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.  */
@@ -1006,15 +1174,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
@@ -1023,14 +1275,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.  */
@@ -1220,6 +1484,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;
@@ -1255,4 +1520,7 @@ _initialize_record_btrace (void)
 
   init_record_btrace_ops ();
   add_target (&record_btrace_ops);
+
+  bfcache = htab_create_alloc (50, bfcache_hash, bfcache_eq, NULL,
+			       xcalloc, xfree);
 }
diff --git a/gdb/record.h b/gdb/record.h
index 323b8d1..f670ca5 100644
--- a/gdb/record.h
+++ b/gdb/record.h
@@ -32,6 +32,10 @@ extern struct cmd_list_element *set_record_cmdlist;
 extern struct cmd_list_element *show_record_cmdlist;
 extern struct cmd_list_element *info_record_cmdlist;
 
+/* Unwinders for some record targets.  */
+extern const struct frame_unwind record_btrace_frame_unwind;
+extern const struct frame_unwind record_btrace_tailcall_frame_unwind;
+
 /* A list of flags specifying what record target methods should print.  */
 enum record_print_flag
 {
diff --git a/gdb/testsuite/gdb.btrace/record_goto.exp b/gdb/testsuite/gdb.btrace/record_goto.exp
index 3775be2..f8a7dda 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] 66+ messages in thread

* [PATCH v9 03/29] btrace: uppercase btrace_read_type
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (14 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 28/29] target: allow decr_pc_after_break to be defined by the target Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 16:45 ` [PATCH v9 27/29] record-btrace: show trace from enable location Markus Metzger
                   ` (13 subsequent siblings)
  29 siblings, 0 replies; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

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

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


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

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

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

* [PATCH v9 28/29] target: allow decr_pc_after_break to be defined by the target
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (13 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 21/29] record-btrace: provide xfer_partial target method Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 19:51   ` Pedro Alves
  2013-12-19 16:45 ` [PATCH v9 03/29] btrace: uppercase btrace_read_type Markus Metzger
                   ` (14 subsequent siblings)
  29 siblings, 1 reply; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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).

2013-12-19  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 df21a99..48b38de 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 f0f938d..aaf9f73 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 d8f9787..debd0c8 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 88af3b5..17a07e9 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -2738,7 +2738,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)
@@ -2747,7 +2747,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 4cc3a4c..b81a074 100644
--- a/gdb/linux-thread-db.c
+++ b/gdb/linux-thread-db.c
@@ -1394,7 +1394,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 b54314d..c68a000 100644
--- a/gdb/record-full.c
+++ b/gdb/record-full.c
@@ -1259,7 +1259,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);
@@ -1332,7 +1332,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,
@@ -1414,7 +1414,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 45261ef..2a81360 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4526,6 +4526,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 231a435..1c82c44 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?
      */
@@ -2036,4 +2042,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] 66+ messages in thread

* [PATCH v9 22/29] record-btrace: add to_wait and to_resume target methods.
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 16:45 ` [PATCH v9 25/29] record-btrace: extend unwinder Markus Metzger
                   ` (28 subsequent siblings)
  29 siblings, 0 replies; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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.

2013-12-19  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 5011d6c..bb140ce 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -1016,6 +1016,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
@@ -1050,6 +1089,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] 66+ messages in thread

* [PATCH v9 20/29] target, breakpoint: allow insert/remove breakpoint to be forwarded
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (21 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 15/29] record-btrace: supply register target methods Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 19:08   ` Pedro Alves
  2013-12-19 16:45 ` [PATCH v9 07/29] btrace: change branch trace data structure Markus Metzger
                   ` (6 subsequent siblings)
  29 siblings, 1 reply; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

2013-12-19  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.
	* 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     | 28 ++++++++++++++---------
 gdb/remote-m32r-sdi.c |  6 +++--
 gdb/remote-mips.c     |  8 +++----
 gdb/remote.c          | 10 +++++----
 gdb/target.c          | 62 +++++++++++++++++++++++++++++++++++++--------------
 gdb/target.h          | 25 +++++++++++++++++----
 11 files changed, 108 insertions(+), 49 deletions(-)

diff --git a/gdb/corelow.c b/gdb/corelow.c
index 2869eea..50f89cf 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 38d2edb..70409d9 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 f06c4ae..9e2a1a4 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 5d2c3f3..6074435 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 1c2f298..6c9604d 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 1710a51..b54314d 100644
--- a/gdb/record-full.c
+++ b/gdb/record-full.c
@@ -230,10 +230,12 @@ 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 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 int (*record_full_beneath_to_stopped_by_watchpoint) (void);
 static int (*record_full_beneath_to_stopped_data_address) (struct target_ops *,
@@ -801,9 +803,9 @@ 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 int (*tmp_to_remove_breakpoint) (struct target_ops *, struct gdbarch *,
 					struct bp_target_info *);
 static int (*tmp_to_stopped_by_watchpoint) (void);
 static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *);
@@ -1745,7 +1747,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 +1765,8 @@ 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);
+      ret = record_full_beneath_to_insert_breakpoint (ops->beneath, gdbarch,
+						      bp_tgt);
       do_cleanups (old_cleanups);
 
       if (ret != 0)
@@ -1782,7 +1786,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 +1807,8 @@ 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);
+	      ret = record_full_beneath_to_remove_breakpoint (ops->beneath,
+							      gdbarch, bp_tgt);
 	      do_cleanups (old_cleanups);
 
 	      if (ret != 0)
@@ -2252,7 +2258,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 +2268,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 f560388..c606d71 100644
--- a/gdb/remote-m32r-sdi.c
+++ b/gdb/remote-m32r-sdi.c
@@ -1145,7 +1145,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;
@@ -1189,7 +1190,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 5b3254d..507e157 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 5fe1c53..99ab6e0 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -8205,7 +8205,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.
@@ -8261,11 +8262,12 @@ remote_insert_breakpoint (struct gdbarch *gdbarch,
 	}
     }
 
-  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;
@@ -8295,7 +8297,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 0759ae9..c9fef6b 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -85,10 +85,10 @@ static void debug_to_open (char *, int);
 
 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);
@@ -580,8 +580,8 @@ update_current_target (void)
       /* Do not inherit to_prepare_to_store.  */
       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);
@@ -718,10 +718,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);
@@ -2449,9 +2445,12 @@ get_target_memory_unsigned (struct target_ops *ops, CORE_ADDR addr,
   return extract_unsigned_integer (buf, len, byte_order);
 }
 
+/* See target.h.  */
+
 int
-target_insert_breakpoint (struct gdbarch *gdbarch,
-			  struct bp_target_info *bp_tgt)
+forward_target_insert_breakpoint (struct target_ops *ops,
+				  struct gdbarch *gdbarch,
+				  struct bp_target_info *bp_tgt)
 {
   if (!may_insert_breakpoints)
     {
@@ -2459,13 +2458,29 @@ target_insert_breakpoint (struct gdbarch *gdbarch,
       return 1;
     }
 
-  return (*current_target.to_insert_breakpoint) (gdbarch, 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_remove_breakpoint (struct gdbarch *gdbarch,
+target_insert_breakpoint (struct gdbarch *gdbarch,
 			  struct bp_target_info *bp_tgt)
 {
+  return forward_target_insert_breakpoint (&current_target, gdbarch, bp_tgt);
+}
+
+/* See target.h.  */
+
+int
+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
      we should just take the user literally and assume that any
@@ -2476,7 +2491,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
@@ -4547,12 +4575,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",
@@ -4562,12 +4590,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 c415266..212f8d0 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 *);
@@ -1132,12 +1134,27 @@ int target_write_memory_blocks (VEC(memory_write_request_s) *requests,
      (*current_target.to_files_info) (&current_target)
 
 /* Insert 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_insert_breakpoint (struct target_ops *ops,
+					     struct gdbarch *gdbarch,
+					     struct bp_target_info *bp_tgt);
+
+/* Insert a breakpoint at address BP_TGT->placed_address in the target
    machine.  Result is 0 for success, non-zero for error.  */
 
 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,
@@ -1884,10 +1901,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] 66+ messages in thread

* [PATCH v9 27/29] record-btrace: show trace from enable location
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (15 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 03/29] btrace: uppercase btrace_read_type Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 16:45 ` [PATCH v9 01/29] btrace, test: fix multi-line btrace tests Markus Metzger
                   ` (12 subsequent siblings)
  29 siblings, 0 replies; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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.

2013-12-19  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           | 20 ++---
 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, 239 insertions(+), 117 deletions(-)
 create mode 100644 gdb/testsuite/gdb.btrace/delta.exp

diff --git a/gdb/btrace.c b/gdb/btrace.c
index c86a866..87ddf56 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -30,6 +30,7 @@
 #include "source.h"
 #include "filenames.h"
 #include "xml-support.h"
+#include "regcache.h"
 
 /* Print a record debug message.  Use do ... while (0) to avoid ambiguities
    when used in if statements.  */
@@ -663,6 +664,32 @@ btrace_compute_ftrace (struct btrace_thread_info *btinfo,
   btinfo->level = -level;
 }
 
+/* Add an entry for the current PC.  */
+
+static void
+btrace_add_pc (struct thread_info *tp)
+{
+  VEC (btrace_block_s) *btrace;
+  struct btrace_block *block;
+  struct regcache *regcache;
+  struct cleanup *cleanup;
+  CORE_ADDR pc;
+
+  regcache = get_thread_regcache (tp->ptid);
+  pc = regcache_read_pc (regcache);
+
+  btrace = NULL;
+  cleanup = make_cleanup (VEC_cleanup (btrace_block_s), &btrace);
+
+  block = VEC_safe_push (btrace_block_s, btrace, NULL);
+  block->begin = pc;
+  block->end = pc;
+
+  btrace_compute_ftrace (&tp->btrace, btrace);
+
+  do_cleanups (cleanup);
+}
+
 /* See btrace.h.  */
 
 void
@@ -677,6 +704,11 @@ btrace_enable (struct thread_info *tp)
   DEBUG ("enable thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
 
   tp->btrace.target = target_enable_btrace (tp->ptid);
+
+  /* Add an entry for the current PC so we start tracing from where we
+     enabled it.  */
+  if (tp->btrace.target != NULL)
+    btrace_add_pc (tp);
 }
 
 /* See btrace.h.  */
@@ -1464,3 +1496,22 @@ btrace_is_replaying (struct thread_info *tp)
 {
   return tp->btrace.replay != NULL;
 }
+
+/* See btrace.h.  */
+
+int
+btrace_is_empty (struct thread_info *tp)
+{
+  struct btrace_insn_iterator begin, end;
+  struct btrace_thread_info *btinfo;
+
+  btinfo = &tp->btrace;
+
+  if (btinfo->begin == NULL)
+    return 1;
+
+  btrace_insn_begin (&begin, btinfo);
+  btrace_insn_end (&end, btinfo);
+
+  return btrace_insn_cmp (&begin, &end) == 0;
+}
diff --git a/gdb/btrace.h b/gdb/btrace.h
index 0a48253..548ebba 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -314,4 +314,8 @@ extern void btrace_set_call_history (struct btrace_thread_info *,
 /* Determine if branch tracing is currently replaying TP.  */
 extern int btrace_is_replaying (struct thread_info *tp);
 
+/* Return non-zero if the branch trace for TP is empty; zero otherwise.  */
+extern int btrace_is_empty (struct thread_info *tp);
+
+
 #endif /* BTRACE_H */
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 8e27f0f..22eb7e5 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 5a0d625..5854a22 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\\(\\)"]
@@ -62,10 +63,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\t  test\\(\\)" \
-    "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\]*\t  test\\(\\)"]
diff --git a/gdb/testsuite/gdb.btrace/instruction_history.exp b/gdb/testsuite/gdb.btrace/instruction_history.exp
index 1b06574..0092aa4 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 f8a7dda..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\t  fun4\tinst 1,3" \
-  "2\t    fun1\tinst 4,7" \
-  "3\t  fun4\tinst 8,8" \
-  "4\t    fun2\tinst 9,11" \
-  "5\t      fun1\tinst 12,15" \
-  "6\t    fun2\tinst 16,17" \
-  "7\t  fun4\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\t  fun4\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\t  fun4\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\t  fun4\tinst 1,3" \
-  "2\t    fun1\tinst 4,7" \
-  "3\t  fun4\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\t  fun4\tinst 1,3" \
-  "2\t    fun1\tinst 4,7" \
-  "3\t  fun4\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\t  fun4\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\t  fun4\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] 66+ messages in thread

* [PATCH v9 02/29] btrace, linux: fix memory leak when reading branch trace
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (25 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 06/29] frame: add frame_id_build_unavailable_stack_special Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 16:45 ` [PATCH v9 17/29] frame, cfa: check unwind stop reason first Markus Metzger
                   ` (2 subsequent siblings)
  29 siblings, 0 replies; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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.

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

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


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

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

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

* [PATCH v9 10/29] btrace: increase buffer size
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
  2013-12-19 16:45 ` [PATCH v9 22/29] record-btrace: add to_wait and to_resume target methods Markus Metzger
  2013-12-19 16:45 ` [PATCH v9 25/29] record-btrace: extend unwinder Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 16:45 ` [PATCH v9 24/29] record-btrace: add record goto target methods Markus Metzger
                   ` (26 subsequent siblings)
  29 siblings, 0 replies; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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.

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

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


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

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

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

* [PATCH v9 07/29] btrace: change branch trace data structure
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (22 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 20/29] target, breakpoint: allow insert/remove breakpoint to be forwarded Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 16:45 ` [PATCH v9 14/29] target: add ops parameter to to_prepare_to_store method Markus Metzger
                   ` (5 subsequent siblings)
  29 siblings, 0 replies; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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.

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

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

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


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

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

* [PATCH v9 08/29] record-btrace: fix insn range in function call history
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (7 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 16/29] frame, backtrace: allow targets to supply a frame unwinder Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 16:45 ` [PATCH v9 11/29] record-btrace: optionally indent " Markus Metzger
                   ` (20 subsequent siblings)
  29 siblings, 0 replies; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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].

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

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


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

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

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

* [PATCH v9 05/29] frame:  add frame_is_tailcall function
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (18 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 18/29] frame: do not assume unwinding will succeed Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 20:19   ` Pedro Alves
  2013-12-19 16:45 ` [PATCH v9 26/29] btrace, gdbserver: read branch trace incrementally Markus Metzger
                   ` (9 subsequent siblings)
  29 siblings, 1 reply; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

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

Add a predicate for tailcall frames.

This patch is no longer needed for the btrace series.  I leave it in
since it might help someone in the future.  I'd also be OK to remove it.

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

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


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

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

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

* [PATCH v9 24/29] record-btrace: add record goto target methods
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (2 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 10/29] btrace: increase buffer size Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 16:45 ` [PATCH v9 29/29] record-btrace: add (reverse-)stepping support Markus Metzger
                   ` (25 subsequent siblings)
  29 siblings, 0 replies; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

Reviewed-by: Eli Zaretskii

2013-12-19  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 da7f97b..1ff2b5a 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -106,6 +106,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 f6272b4..1d090e7 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.  */
@@ -1073,6 +1089,103 @@ record_btrace_find_new_threads (struct target_ops *ops)
       }
 }
 
+/* Set the replay branch trace instruction iterator.  If IT is NULL, replay
+   is stopped.  */
+
+static void
+record_btrace_set_replay (struct thread_info *tp,
+			  const struct btrace_insn_iterator *it)
+{
+  struct btrace_thread_info *btinfo;
+
+  btinfo = &tp->btrace;
+
+  if (it == NULL || it->function == NULL)
+    {
+      if (btinfo->replay == NULL)
+	return;
+
+      xfree (btinfo->replay);
+      btinfo->replay = NULL;
+    }
+  else
+    {
+      if (btinfo->replay == NULL)
+	btinfo->replay = xmalloc (sizeof (*btinfo->replay));
+      else if (btrace_insn_cmp (btinfo->replay, it) == 0)
+	return;
+
+      *btinfo->replay = *it;
+    }
+
+  /* Clear the function call and instruction histories so we start anew
+     from the new replay position.  */
+  xfree (btinfo->insn_history);
+  xfree (btinfo->call_history);
+
+  btinfo->insn_history = NULL;
+  btinfo->call_history = NULL;
+
+  registers_changed_ptid (tp->ptid);
+}
+
+/* The to_goto_record_begin method of target record-btrace.  */
+
+static void
+record_btrace_goto_begin (void)
+{
+  struct thread_info *tp;
+  struct btrace_insn_iterator begin;
+
+  tp = require_btrace_thread ();
+
+  btrace_insn_begin (&begin, &tp->btrace);
+  record_btrace_set_replay (tp, &begin);
+
+  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
+}
+
+/* The to_goto_record_end method of target record-btrace.  */
+
+static void
+record_btrace_goto_end (void)
+{
+  struct thread_info *tp;
+
+  tp = require_btrace_thread ();
+
+  record_btrace_set_replay (tp, NULL);
+
+  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
+}
+
+/* The to_goto_record method of target record-btrace.  */
+
+static void
+record_btrace_goto (ULONGEST insn)
+{
+  struct thread_info *tp;
+  struct btrace_insn_iterator it;
+  unsigned int number;
+  int found;
+
+  number = insn;
+
+  /* Check for wrap-arounds.  */
+  if (number != insn)
+    error (_("Instruction number out of range."));
+
+  tp = require_btrace_thread ();
+
+  found = btrace_find_insn_by_number (&it, &tp->btrace, number);
+  if (found == 0)
+    error (_("No such instruction."));
+
+  record_btrace_set_replay (tp, &it);
+
+  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
+}
+
 /* Initialize the record-btrace target ops.  */
 
 static void
@@ -1110,6 +1223,9 @@ init_record_btrace_ops (void)
   ops->to_resume = record_btrace_resume;
   ops->to_wait = record_btrace_wait;
   ops->to_find_new_threads = record_btrace_find_new_threads;
+  ops->to_goto_record_begin = record_btrace_goto_begin;
+  ops->to_goto_record_end = record_btrace_goto_end;
+  ops->to_goto_record = record_btrace_goto;
   ops->to_stratum = record_stratum;
   ops->to_magic = OPS_MAGIC;
 }
diff --git a/gdb/testsuite/gdb.btrace/Makefile.in b/gdb/testsuite/gdb.btrace/Makefile.in
index 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..3775be2
--- /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\t  fun4\tinst 1,3" \
+  "2\t    fun1\tinst 4,7" \
+  "3\t  fun4\tinst 8,8" \
+  "4\t    fun2\tinst 9,11" \
+  "5\t      fun1\tinst 12,15" \
+  "6\t    fun2\tinst 16,17" \
+  "7\t  fun4\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\t  fun4\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\t  fun4\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\t  fun4\tinst 1,3" \
+  "2\t    fun1\tinst 4,7" \
+  "3\t  fun4\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\t  fun4\tinst 1,3" \
+  "2\t    fun1\tinst 4,7" \
+  "3\t  fun4\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\t  fun4\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\t  fun4\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] 66+ messages in thread

* [PATCH v9 21/29] record-btrace: provide xfer_partial target method
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (12 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 04/29] gdbarch: add instruction predicate methods Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 19:13   ` Pedro Alves
  2013-12-19 19:26   ` Pedro Alves
  2013-12-19 16:45 ` [PATCH v9 28/29] target: allow decr_pc_after_break to be defined by the target Markus Metzger
                   ` (15 subsequent siblings)
  29 siblings, 2 replies; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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.

2013-12-19  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.


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

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index cf30e17..5011d6c 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,116 @@ 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:
+	case TARGET_OBJECT_RAW_MEMORY:
+	case TARGET_OBJECT_STACK_MEMORY:
+	  {
+	    struct target_section *section;
+
+	    /* We do not allow writing memory in general.  */
+	    if (writebuf != NULL)
+	      throw_error (NOT_AVAILABLE_ERROR,
+			   _("This record target does not record memory."));
+
+	    /* 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;
+		  }
+	      }
+
+	    throw_error (NOT_AVAILABLE_ERROR,
+			 _("This record target does not record memory."));
+	  }
+	}
+    }
+
+  /* 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 +1043,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;
-- 
1.8.3.1

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

* [PATCH v9 16/29] frame, backtrace: allow targets to supply a frame unwinder
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (6 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 23/29] record-btrace: provide target_find_new_threads method Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 18:41   ` Pedro Alves
  2013-12-19 16:45 ` [PATCH v9 08/29] record-btrace: fix insn range in function call history Markus Metzger
                   ` (21 subsequent siblings)
  29 siblings, 1 reply; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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 8fabf52..e32f4b8 100644
--- a/gdb/frame-unwind.c
+++ b/gdb/frame-unwind.c
@@ -27,6 +27,7 @@
 #include "exceptions.h"
 #include "gdb_assert.h"
 #include "gdb_obstack.h"
+#include "target.h"
 
 static struct gdbarch_data *frame_unwind_data;
 
@@ -88,6 +89,48 @@ frame_unwind_append_unwinder (struct gdbarch *gdbarch,
   (*ip)->unwinder = unwinder;
 }
 
+/* Call SNIFFER from UNWINDER.  If it succeeded set UNWINDER for
+   THIS_FRAME and return 1.  Otherwise the function keeps THIS_FRAME
+   unchanged and returns 0.  */
+
+static int
+frame_unwind_try_unwinder (struct frame_info *this_frame, void **this_cache,
+                          const struct frame_unwind *unwinder)
+{
+  struct cleanup *old_cleanup;
+  volatile struct gdb_exception ex;
+  int res = 0;
+
+  old_cleanup = frame_prepare_for_sniffer (this_frame, unwinder);
+
+  TRY_CATCH (ex, RETURN_MASK_ERROR)
+    {
+      res = unwinder->sniffer (unwinder, this_frame, this_cache);
+    }
+  if (ex.reason < 0 && ex.error == NOT_AVAILABLE_ERROR)
+    {
+      /* This usually means that not even the PC is available,
+        thus most unwinders aren't able to determine if they're
+        the best fit.  Keep trying.  Fallback prologue unwinders
+        should always accept the frame.  */
+      do_cleanups (old_cleanup);
+      return 0;
+    }
+  else if (ex.reason < 0)
+    throw_exception (ex);
+  else if (res)
+    {
+      discard_cleanups (old_cleanup);
+      return 1;
+    }
+  else
+    {
+      do_cleanups (old_cleanup);
+      return 0;
+    }
+  gdb_assert_not_reached ("frame_unwind_try_unwinder");
+}
+
 /* Iterate through sniffers for THIS_FRAME frame until one returns with an
    unwinder implementation.  THIS_FRAME->UNWIND must be NULL, it will get set
    by this function.  Possibly initialize THIS_CACHE.  */
@@ -98,37 +141,24 @@ frame_unwind_find_by_frame (struct frame_info *this_frame, void **this_cache)
   struct gdbarch *gdbarch = get_frame_arch (this_frame);
   struct frame_unwind_table *table = gdbarch_data (gdbarch, frame_unwind_data);
   struct frame_unwind_table_entry *entry;
+  const struct frame_unwind *unwinder_from_target;
+
+  unwinder_from_target = target_get_unwinder ();
+  if (unwinder_from_target != NULL
+      && frame_unwind_try_unwinder (this_frame, this_cache,
+                                   unwinder_from_target))
+    return;
+
+  unwinder_from_target = target_get_tailcall_unwinder ();
+  if (unwinder_from_target != NULL
+      && frame_unwind_try_unwinder (this_frame, this_cache,
+                                   unwinder_from_target))
+    return;
 
   for (entry = table->list; entry != NULL; entry = entry->next)
-    {
-      struct cleanup *old_cleanup;
-      volatile struct gdb_exception ex;
-      int res = 0;
-
-      old_cleanup = frame_prepare_for_sniffer (this_frame, entry->unwinder);
-
-      TRY_CATCH (ex, RETURN_MASK_ERROR)
-	{
-	  res = entry->unwinder->sniffer (entry->unwinder, this_frame,
-					  this_cache);
-	}
-      if (ex.reason < 0 && ex.error == NOT_AVAILABLE_ERROR)
-	{
-	  /* This usually means that not even the PC is available,
-	     thus most unwinders aren't able to determine if they're
-	     the best fit.  Keep trying.  Fallback prologue unwinders
-	     should always accept the frame.  */
-	}
-      else if (ex.reason < 0)
-	throw_exception (ex);
-      else if (res)
-        {
-          discard_cleanups (old_cleanup);
-          return;
-        }
+    if (frame_unwind_try_unwinder (this_frame, this_cache, entry->unwinder))
+      return;
 
-      do_cleanups (old_cleanup);
-    }
   internal_error (__FILE__, __LINE__, _("frame_unwind_find_by_frame failed"));
 }
 
diff --git a/gdb/target.c b/gdb/target.c
index eb2d2d0..0759ae9 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4469,6 +4469,34 @@ target_call_history_range (ULONGEST begin, ULONGEST end, int flags)
   tcomplain ();
 }
 
+/* See target.h.  */
+
+const struct frame_unwind *
+target_get_unwinder (void)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    if (t->to_get_unwinder != NULL)
+      return t->to_get_unwinder;
+
+  return NULL;
+}
+
+/* See target.h.  */
+
+const struct frame_unwind *
+target_get_tailcall_unwinder (void)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    if (t->to_get_tailcall_unwinder != NULL)
+      return t->to_get_tailcall_unwinder;
+
+  return NULL;
+}
+
 static int
 deprecated_debug_xfer_memory (CORE_ADDR memaddr, bfd_byte *myaddr, int len,
 			      int write, struct mem_attrib *attrib,
diff --git a/gdb/target.h b/gdb/target.h
index f20407d..c415266 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?
      */
@@ -1778,6 +1783,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] 66+ messages in thread

* [PATCH v9 29/29] record-btrace: add (reverse-)stepping support
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (3 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 24/29] record-btrace: add record goto target methods Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 17:31   ` Eli Zaretskii
  2013-12-19 20:10   ` Pedro Alves
  2013-12-19 16:45 ` [PATCH v9 13/29] btrace: add replay position to btrace thread info Markus Metzger
                   ` (24 subsequent siblings)
  29 siblings, 2 replies; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches, Eli Zaretskii

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.

CC: Eli Zaretskii  <eliz@gnu.org>

2013-12-19  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                            | 434 +++++++++++++++++++++++--
 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, 1242 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 ceb880a..23412aa 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -110,6 +110,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 548ebba..5b52ec6 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -153,6 +153,25 @@ struct btrace_call_history
   struct btrace_call_iterator end;
 };
 
+/* Branch trace thread flags.  */
+enum btrace_thread_flag
+{
+  /* The thread is to be stepped forwards.  */
+  BTHR_STEP = (1 << 0),
+
+  /* The thread is to be stepped backwards.  */
+  BTHR_RSTEP = (1 << 1),
+
+  /* The thread is to be continued forwards.  */
+  BTHR_CONT = (1 << 2),
+
+  /* The thread is to be continued backwards.  */
+  BTHR_RCONT = (1 << 3),
+
+  /* The thread is to be moved.  */
+  BTHR_MOVE = (BTHR_STEP | BTHR_RSTEP | BTHR_CONT | BTHR_RCONT)
+};
+
 /* Branch trace information per thread.
 
    This represents the branch trace configuration as well as the entry point
@@ -182,6 +201,9 @@ struct btrace_thread_info
      becomes zero.  */
   int level;
 
+  /* A bit-vector of btrace_thread_flag.  */
+  enum btrace_thread_flag flags;
+
   /* The instruction history iterator.  */
   struct btrace_insn_history *insn_history;
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 10e86e5..e1bf164 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6255,8 +6255,10 @@ replay implementation.  This method allows replaying and reverse
 execution.
 
 @item btrace
-Hardware-supported instruction recording.  This method does not allow
-replaying and reverse execution.
+Hardware-supported instruction recording.  This method does not record
+data.  Further, the data is collected in a ring buffer so old data will
+be overwritten when the buffer is full.  It allows limited replay and
+reverse execution.
 
 This recording method may not be available on all processors.
 @end table
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 22eb7e5..6391f47 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -170,6 +170,10 @@ record_btrace_open (char *args, int from_tty)
   if (!target_supports_btrace ())
     error (_("Target does not support branch tracing."));
 
+  if (non_stop)
+    error (_("Record btrace can't debug inferior in non-stop mode "
+	     "(non-stop)."));
+
   gdb_assert (record_btrace_thread_observer == NULL);
 
   disable_chain = make_cleanup (null_cleanup, NULL);
@@ -1295,14 +1299,168 @@ 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;
+
+      /* Avoid stale frames based on branch trace unwinding.  */
+      registers_changed_ptid (tp->ptid);
+      get_current_frame ();
+
+      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)
@@ -1311,7 +1469,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.  */
@@ -1320,8 +1671,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)
@@ -1330,7 +1685,54 @@ record_btrace_wait (struct target_ops *ops, ptid_t ptid,
       error (_("Cannot find target for waiting."));
     }
 
-  error (_("You can't do this from here.  Do 'record goto end', first."));
+  /* Let's find a thread to move.  */
+  tp = record_btrace_find_thread_to_move (ptid);
+  if (tp == NULL)
+    {
+      DEBUG ("wait %s: no thread", target_pid_to_str (ptid));
+
+      status->kind = TARGET_WAITKIND_IGNORE;
+      return minus_one_ptid;
+    }
+
+  /* We only move a single thread.  We're not able to correlate threads.  */
+  *status = record_btrace_step_thread (tp);
+
+  /* Stop all other threads. */
+  if (!non_stop)
+    ALL_THREADS (other)
+      other->btrace.flags &= ~BTHR_MOVE;
+
+  /* Start record histories anew from the current position.  */
+  record_btrace_clear_histories (&tp->btrace);
+
+  /* GDB seems to need this.  Without, a stale PC seems to be used resulting in
+     the current location to be displayed incorrectly.  */
+  registers_changed_ptid (tp->ptid);
+
+  return tp->ptid;
+}
+
+/* The to_can_execute_reverse method of target record-btrace.  */
+
+static int
+record_btrace_can_execute_reverse (void)
+{
+  return 1;
+}
+
+/* The to_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.  */
@@ -1363,32 +1765,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.  */
@@ -1489,6 +1879,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..e3ca5f4
--- /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" "This record target does not record memory\."
+gdb_test "print loc" "This record target does not record memory\."
+
+# 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] 66+ messages in thread

* [PATCH v9 12/29] record-btrace: make ranges include begin and end
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (9 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 11/29] record-btrace: optionally indent " Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 17:29   ` Eli Zaretskii
  2013-12-19 16:45 ` [PATCH v9 09/29] record-btrace: start counting at one Markus Metzger
                   ` (18 subsequent siblings)
  29 siblings, 1 reply; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches, Eli Zaretskii

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

CC: Eli Zaretskii  <eliz@gnu.org>

2013-12-19  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                                |  6 ++--
 gdb/record-btrace.c                                | 38 ++++++++++++++++------
 gdb/target.h                                       |  4 +--
 gdb/testsuite/gdb.btrace/function_call_history.exp |  5 +--
 gdb/testsuite/gdb.btrace/instruction_history.exp   |  8 +++--
 6 files changed, 43 insertions(+), 21 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 12c424d..da7f97b 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -45,6 +45,9 @@
   The instruction range is now prefixed with 'insn'.
   The source line range is now prefixed with 'at'.
 
+* 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 effc73d..5c3f548 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6456,8 +6456,7 @@ Disassembles ten more instructions before the last disassembly.
 
 @item record instruction-history @var{begin} @var{end}
 Disassembles instructions beginning with instruction number
-@var{begin} until instruction number @var{end}.  The instruction
-number @var{end} is not included.
+@var{begin} until instruction number @var{end}.
 @end table
 
 This command may not be available for all recording methods.
@@ -6527,8 +6526,7 @@ Prints ten more functions before the last ten-line print.
 
 @item record function-call-history @var{begin} @var{end}
 Prints functions beginning with function number @var{begin} until
-function number @var{end}.  The function number @var{end} is not
-included.
+function number @var{end}.
 @end table
 
 This command may not be available for all recording methods.
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 9470d85..45d5e4a 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -368,7 +368,7 @@ record_btrace_insn_history_range (ULONGEST from, ULONGEST to, int flags)
   if (low != from || high != to)
     error (_("Bad range."));
 
-  if (high <= low)
+  if (high < low)
     error (_("Bad range."));
 
   btinfo = require_btrace ();
@@ -377,10 +377,17 @@ record_btrace_insn_history_range (ULONGEST from, ULONGEST to, int flags)
   if (found == 0)
     error (_("Range out of bounds."));
 
-  /* Silently truncate the range, if necessary.  */
   found = btrace_find_insn_by_number (&end, btinfo, high);
   if (found == 0)
-    btrace_insn_end (&end, btinfo);
+    {
+      /* Silently truncate the range.  */
+      btrace_insn_end (&end, btinfo);
+    }
+  else
+    {
+      /* We want both begin and end to be inclusive.  */
+      btrace_insn_next (&end, 1);
+    }
 
   btrace_insn_history (uiout, &begin, &end, flags);
   btrace_set_insn_history (btinfo, &begin, &end);
@@ -396,6 +403,8 @@ record_btrace_insn_history_from (ULONGEST from, int size, int flags)
   ULONGEST begin, end, context;
 
   context = abs (size);
+  if (context == 0)
+    error (_("Bad record instruction-history-size."));
 
   if (size < 0)
     {
@@ -404,12 +413,12 @@ record_btrace_insn_history_from (ULONGEST from, int size, int flags)
       if (from < context)
 	begin = 0;
       else
-	begin = from - context;
+	begin = from - context + 1;
     }
   else
     {
       begin = from;
-      end = from + context;
+      end = from + context - 1;
 
       /* Check for wrap-around.  */
       if (end < begin)
@@ -619,7 +628,7 @@ record_btrace_call_history_range (ULONGEST from, ULONGEST to, int flags)
   if (low != from || high != to)
     error (_("Bad range."));
 
-  if (high <= low)
+  if (high < low)
     error (_("Bad range."));
 
   btinfo = require_btrace ();
@@ -628,10 +637,17 @@ record_btrace_call_history_range (ULONGEST from, ULONGEST to, int flags)
   if (found == 0)
     error (_("Range out of bounds."));
 
-  /* Silently truncate the range, if necessary.  */
   found = btrace_find_call_by_number (&end, btinfo, high);
   if (found == 0)
-    btrace_call_end (&end, btinfo);
+    {
+      /* Silently truncate the range.  */
+      btrace_call_end (&end, btinfo);
+    }
+  else
+    {
+      /* We want both begin and end to be inclusive.  */
+      btrace_call_next (&end, 1);
+    }
 
   btrace_call_history (uiout, btinfo, &begin, &end, flags);
   btrace_set_call_history (btinfo, &begin, &end);
@@ -647,6 +663,8 @@ record_btrace_call_history_from (ULONGEST from, int size, int flags)
   ULONGEST begin, end, context;
 
   context = abs (size);
+  if (context == 0)
+    error (_("Bad record function-call-history-size."));
 
   if (size < 0)
     {
@@ -655,12 +673,12 @@ record_btrace_call_history_from (ULONGEST from, int size, int flags)
       if (from < context)
 	begin = 0;
       else
-	begin = from - context;
+	begin = from - context + 1;
     }
   else
     {
       begin = from;
-      end = from + context;
+      end = from + context - 1;
 
       /* Check for wrap-around.  */
       if (end < begin)
diff --git a/gdb/target.h b/gdb/target.h
index 5b9b34d..1cbf567 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 f149757..c576004 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 195fe72..1b06574 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] 66+ messages in thread

* [PATCH v9 15/29] record-btrace: supply register target methods
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (20 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 26/29] btrace, gdbserver: read branch trace incrementally Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 16:45 ` [PATCH v9 20/29] target, breakpoint: allow insert/remove breakpoint to be forwarded Markus Metzger
                   ` (7 subsequent siblings)
  29 siblings, 0 replies; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

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

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

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


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

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

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

* [PATCH v9 13/29] btrace: add replay position to btrace thread info
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (4 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 29/29] record-btrace: add (reverse-)stepping support Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 16:45 ` [PATCH v9 23/29] record-btrace: provide target_find_new_threads method Markus Metzger
                   ` (23 subsequent siblings)
  29 siblings, 0 replies; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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.

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

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


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

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

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

* [PATCH v9 19/29] record-btrace, frame: supply target-specific unwinder
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (27 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 17/29] frame, cfa: check unwind stop reason first Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 20:23 ` [PATCH v9 00/29] record-btrace: reverse Pedro Alves
  29 siblings, 0 replies; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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 26ae6d8..cf30e17 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -33,6 +33,7 @@
 #include "symtab.h"
 #include "filenames.h"
 #include "regcache.h"
+#include "frame-unwind.h"
 
 /* The target_ops of record-btrace.  */
 static struct target_ops record_btrace_ops;
@@ -840,6 +841,68 @@ record_btrace_prepare_to_store (struct target_ops *ops,
       }
 }
 
+/* Implement stop_reason method for record_btrace_frame_unwind.  */
+
+static enum unwind_stop_reason
+record_btrace_frame_unwind_stop_reason (struct frame_info *this_frame,
+					void **this_cache)
+{
+  return UNWIND_UNAVAILABLE;
+}
+
+/* Implement this_id method for record_btrace_frame_unwind.  */
+
+static void
+record_btrace_frame_this_id (struct frame_info *this_frame, void **this_cache,
+			     struct frame_id *this_id)
+{
+  /* Leave there the outer_frame_id value.  */
+}
+
+/* Implement prev_register method for record_btrace_frame_unwind.  */
+
+static struct value *
+record_btrace_frame_prev_register (struct frame_info *this_frame,
+				   void **this_cache,
+				   int regnum)
+{
+  throw_error (NOT_AVAILABLE_ERROR,
+              _("Registers are not available in btrace record history"));
+}
+
+/* Implement sniffer method for record_btrace_frame_unwind.  */
+
+static int
+record_btrace_frame_sniffer (const struct frame_unwind *self,
+			     struct frame_info *this_frame,
+			     void **this_cache)
+{
+  struct thread_info *tp;
+  struct btrace_thread_info *btinfo;
+  struct btrace_insn_iterator *replay;
+
+  /* THIS_FRAME does not contain a reference to its thread.  */
+  tp = find_thread_ptid (inferior_ptid);
+  gdb_assert (tp != NULL);
+
+  return btrace_is_replaying (tp);
+}
+
+/* btrace recording does not store previous memory content, neither the stack
+   frames content.  Any unwinding would return errorneous results as the stack
+   contents no longer matches the changed PC value restored from history.
+   Therefore this unwinder reports any possibly unwound registers as
+   <unavailable>.  */
+
+static const struct frame_unwind record_btrace_frame_unwind =
+{
+  NORMAL_FRAME,
+  record_btrace_frame_unwind_stop_reason,
+  record_btrace_frame_this_id,
+  record_btrace_frame_prev_register,
+  NULL,
+  record_btrace_frame_sniffer
+};
 /* Initialize the record-btrace target ops.  */
 
 static void
@@ -870,6 +933,7 @@ init_record_btrace_ops (void)
   ops->to_fetch_registers = record_btrace_fetch_registers;
   ops->to_store_registers = record_btrace_store_registers;
   ops->to_prepare_to_store = record_btrace_prepare_to_store;
+  ops->to_get_unwinder = &record_btrace_frame_unwind;
   ops->to_stratum = record_stratum;
   ops->to_magic = OPS_MAGIC;
 }
-- 
1.8.3.1

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

* [PATCH v9 04/29] gdbarch: add instruction predicate methods
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (11 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 09/29] record-btrace: start counting at one Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 20:19   ` Pedro Alves
  2013-12-19 16:45 ` [PATCH v9 21/29] record-btrace: provide xfer_partial target method Markus Metzger
                   ` (16 subsequent siblings)
  29 siblings, 1 reply; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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.

2013-12-19  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 d0dc587..5389e63 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.  */
 
@@ -2958,6 +3022,9 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 				      i386_stap_is_single_operand);
   set_gdbarch_stap_parse_special_token (gdbarch,
 					i386_stap_parse_special_token);
+  set_gdbarch_insn_is_call (gdbarch, amd64_insn_is_call);
+  set_gdbarch_insn_is_ret (gdbarch, amd64_insn_is_ret);
+  set_gdbarch_insn_is_jump (gdbarch, amd64_insn_is_jump);
 }
 \f
 
diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c
index cb566ad..8fcfe91 100644
--- a/gdb/arch-utils.c
+++ b/gdb/arch-utils.c
@@ -804,6 +804,21 @@ default_return_in_first_hidden_param_p (struct gdbarch *gdbarch,
   return language_pass_by_reference (type);
 }
 
+int default_insn_is_call (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  return 0;
+}
+
+int default_insn_is_ret (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  return 0;
+}
+
+int default_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  return 0;
+}
+
 /* */
 
 /* -Wmissing-prototypes */
diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h
index 3f0e64f..2cf83d4 100644
--- a/gdb/arch-utils.h
+++ b/gdb/arch-utils.h
@@ -170,4 +170,8 @@ extern const char *default_auto_wide_charset (void);
 
 extern int default_return_in_first_hidden_param_p (struct gdbarch *,
 						   struct type *);
+
+extern int default_insn_is_call (struct gdbarch *, CORE_ADDR);
+extern int default_insn_is_ret (struct gdbarch *, CORE_ADDR);
+extern int default_insn_is_jump (struct gdbarch *, CORE_ADDR);
 #endif
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index fb3595f..b226e9b 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -288,6 +288,9 @@ struct gdbarch
   gdbarch_core_info_proc_ftype *core_info_proc;
   gdbarch_iterate_over_objfiles_in_search_order_ftype *iterate_over_objfiles_in_search_order;
   struct ravenscar_arch_ops * ravenscar_ops;
+  gdbarch_insn_is_call_ftype *insn_is_call;
+  gdbarch_insn_is_ret_ftype *insn_is_ret;
+  gdbarch_insn_is_jump_ftype *insn_is_jump;
 };
 
 
@@ -461,6 +464,9 @@ struct gdbarch startup_gdbarch =
   0,  /* core_info_proc */
   default_iterate_over_objfiles_in_search_order,  /* iterate_over_objfiles_in_search_order */
   NULL,  /* ravenscar_ops */
+  default_insn_is_call,  /* insn_is_call */
+  default_insn_is_ret,  /* insn_is_ret */
+  default_insn_is_jump,  /* insn_is_jump */
   /* startup_gdbarch() */
 };
 
@@ -552,6 +558,9 @@ gdbarch_alloc (const struct gdbarch_info *info,
   gdbarch->gen_return_address = default_gen_return_address;
   gdbarch->iterate_over_objfiles_in_search_order = default_iterate_over_objfiles_in_search_order;
   gdbarch->ravenscar_ops = NULL;
+  gdbarch->insn_is_call = default_insn_is_call;
+  gdbarch->insn_is_ret = default_insn_is_ret;
+  gdbarch->insn_is_jump = default_insn_is_jump;
   /* gdbarch_alloc() */
 
   return gdbarch;
@@ -766,6 +775,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)
@@ -1099,6 +1111,15 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
                       "gdbarch_dump: inner_than = <%s>\n",
                       host_address_to_string (gdbarch->inner_than));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: insn_is_call = <%s>\n",
+                      host_address_to_string (gdbarch->insn_is_call));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: insn_is_jump = <%s>\n",
+                      host_address_to_string (gdbarch->insn_is_jump));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: insn_is_ret = <%s>\n",
+                      host_address_to_string (gdbarch->insn_is_ret));
+  fprintf_unfiltered (file,
                       "gdbarch_dump: int_bit = %s\n",
                       plongest (gdbarch->int_bit));
   fprintf_unfiltered (file,
@@ -4422,6 +4443,57 @@ set_gdbarch_ravenscar_ops (struct gdbarch *gdbarch,
   gdbarch->ravenscar_ops = ravenscar_ops;
 }
 
+int
+gdbarch_insn_is_call (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->insn_is_call != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_insn_is_call called\n");
+  return gdbarch->insn_is_call (gdbarch, addr);
+}
+
+void
+set_gdbarch_insn_is_call (struct gdbarch *gdbarch,
+                          gdbarch_insn_is_call_ftype insn_is_call)
+{
+  gdbarch->insn_is_call = insn_is_call;
+}
+
+int
+gdbarch_insn_is_ret (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->insn_is_ret != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_insn_is_ret called\n");
+  return gdbarch->insn_is_ret (gdbarch, addr);
+}
+
+void
+set_gdbarch_insn_is_ret (struct gdbarch *gdbarch,
+                         gdbarch_insn_is_ret_ftype insn_is_ret)
+{
+  gdbarch->insn_is_ret = insn_is_ret;
+}
+
+int
+gdbarch_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->insn_is_jump != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_insn_is_jump called\n");
+  return gdbarch->insn_is_jump (gdbarch, addr);
+}
+
+void
+set_gdbarch_insn_is_jump (struct gdbarch *gdbarch,
+                          gdbarch_insn_is_jump_ftype insn_is_jump)
+{
+  gdbarch->insn_is_jump = insn_is_jump;
+}
+
 
 /* Keep a registry of per-architecture data-pointers required by GDB
    modules.  */
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index b58efc8..c8a8a12 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -1261,6 +1261,24 @@ extern void set_gdbarch_iterate_over_objfiles_in_search_order (struct gdbarch *g
 extern struct ravenscar_arch_ops * gdbarch_ravenscar_ops (struct gdbarch *gdbarch);
 extern void set_gdbarch_ravenscar_ops (struct gdbarch *gdbarch, struct ravenscar_arch_ops * ravenscar_ops);
 
+/* Return non-zero if the instruction at ADDR is a call; zero otherwise. */
+
+typedef int (gdbarch_insn_is_call_ftype) (struct gdbarch *gdbarch, CORE_ADDR addr);
+extern int gdbarch_insn_is_call (struct gdbarch *gdbarch, CORE_ADDR addr);
+extern void set_gdbarch_insn_is_call (struct gdbarch *gdbarch, gdbarch_insn_is_call_ftype *insn_is_call);
+
+/* Return non-zero if the instruction at ADDR is a return; zero otherwise. */
+
+typedef int (gdbarch_insn_is_ret_ftype) (struct gdbarch *gdbarch, CORE_ADDR addr);
+extern int gdbarch_insn_is_ret (struct gdbarch *gdbarch, CORE_ADDR addr);
+extern void set_gdbarch_insn_is_ret (struct gdbarch *gdbarch, gdbarch_insn_is_ret_ftype *insn_is_ret);
+
+/* Return non-zero if the instruction at ADDR is a jump; zero otherwise. */
+
+typedef int (gdbarch_insn_is_jump_ftype) (struct gdbarch *gdbarch, CORE_ADDR addr);
+extern int gdbarch_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr);
+extern void set_gdbarch_insn_is_jump (struct gdbarch *gdbarch, gdbarch_insn_is_jump_ftype *insn_is_jump);
+
 /* Definition for an unknown syscall, used basically in error-cases.  */
 #define UNKNOWN_SYSCALL (-1)
 
diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh
index a678a78..706f57d 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -988,6 +988,15 @@ m:void:iterate_over_objfiles_in_search_order:iterate_over_objfiles_in_search_ord
 
 # Ravenscar arch-dependent ops.
 v:struct ravenscar_arch_ops *:ravenscar_ops:::NULL:NULL::0:host_address_to_string (gdbarch->ravenscar_ops)
+
+# Return non-zero if the instruction at ADDR is a call; zero otherwise.
+m:int:insn_is_call:CORE_ADDR addr:addr::default_insn_is_call::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 5b4a5a3..4055044 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.  */
 
@@ -7973,6 +8028,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] 66+ messages in thread

* [PATCH v9 18/29] frame: do not assume unwinding will succeed
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (17 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 01/29] btrace, test: fix multi-line btrace tests Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 16:45 ` [PATCH v9 05/29] frame: add frame_is_tailcall function Markus Metzger
                   ` (10 subsequent siblings)
  29 siblings, 0 replies; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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 8facdc00..8dd4714 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -2533,13 +2533,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] 66+ messages in thread

* [PATCH v9 09/29] record-btrace: start counting at one
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (10 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 12/29] record-btrace: make ranges include begin and end Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 17:27   ` Eli Zaretskii
  2013-12-19 16:45 ` [PATCH v9 04/29] gdbarch: add instruction predicate methods Markus Metzger
                   ` (17 subsequent siblings)
  29 siblings, 1 reply; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches, Eli Zaretskii

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.

CC: Eli Zaretskii  <eliz@gnu.org>

2013-12-19  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 e4baf50..fb1b4ce 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -35,6 +35,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 975b60c..d391ad5 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -208,7 +208,13 @@ ftrace_new_function (struct btrace_function *prev,
   bfun->lbegin = INT_MAX;
   bfun->lend = INT_MIN;
 
-  if (prev != NULL)
+  if (prev == NULL)
+    {
+      /* Start counting at one.  */
+      bfun->number = 1;
+      bfun->insn_offset = 1;
+    }
+  else
     {
       gdb_assert (prev->flow.next == NULL);
       prev->flow.next = bfun;
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 0888c6b..466aef2 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -227,11 +227,11 @@ record_btrace_info (void)
 
       btrace_call_end (&call, btinfo);
       btrace_call_prev (&call, 1);
-      calls = btrace_call_number (&call) + 1;
+      calls = btrace_call_number (&call);
 
       btrace_insn_end (&insn, btinfo);
       btrace_insn_prev (&insn, 1);
-      insns = btrace_insn_number (&insn) + 1;
+      insns = btrace_insn_number (&insn);
     }
 
   printf_unfiltered (_("Recorded %u instructions in %u functions for thread "
diff --git a/gdb/testsuite/gdb.btrace/function_call_history.exp b/gdb/testsuite/gdb.btrace/function_call_history.exp
index 0df1275..e233d17 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 6657ea0..195fe72 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] 66+ messages in thread

* [PATCH v9 14/29] target: add ops parameter to to_prepare_to_store method
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (23 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 07/29] btrace: change branch trace data structure Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 18:30   ` Pedro Alves
  2013-12-19 16:45 ` [PATCH v9 06/29] frame: add frame_id_build_unavailable_stack_special Markus Metzger
                   ` (4 subsequent siblings)
  29 siblings, 1 reply; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

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

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

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


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

diff --git a/gdb/inf-child.c b/gdb/inf-child.c
index 3db09c9..123ff14 100644
--- a/gdb/inf-child.c
+++ b/gdb/inf-child.c
@@ -100,7 +100,7 @@ inf_child_post_attach (int pid)
    program being debugged.  */
 
 static void
-inf_child_prepare_to_store (struct regcache *regcache)
+inf_child_prepare_to_store (struct target_ops *ops, struct regcache *regcache)
 {
 }
 
diff --git a/gdb/monitor.c b/gdb/monitor.c
index 2d94d42..5d2c3f3 100644
--- a/gdb/monitor.c
+++ b/gdb/monitor.c
@@ -1427,7 +1427,7 @@ monitor_store_registers (struct target_ops *ops,
    debugged.  */
 
 static void
-monitor_prepare_to_store (struct regcache *regcache)
+monitor_prepare_to_store (struct target_ops *ops, struct regcache *regcache)
 {
   /* Do nothing, since we can store individual regs.  */
 }
diff --git a/gdb/ravenscar-thread.c b/gdb/ravenscar-thread.c
index 9ef265d..53e0446 100644
--- a/gdb/ravenscar-thread.c
+++ b/gdb/ravenscar-thread.c
@@ -62,7 +62,8 @@ static void ravenscar_fetch_registers (struct target_ops *ops,
                                        struct regcache *regcache, int regnum);
 static void ravenscar_store_registers (struct target_ops *ops,
                                        struct regcache *regcache, int regnum);
-static void ravenscar_prepare_to_store (struct regcache *regcache);
+static void ravenscar_prepare_to_store (struct target_ops *ops,
+					struct regcache *regcache);
 static void ravenscar_resume (struct target_ops *ops, ptid_t ptid, int step,
 			      enum gdb_signal siggnal);
 static void ravenscar_mourn_inferior (struct target_ops *ops);
@@ -303,14 +304,14 @@ ravenscar_store_registers (struct target_ops *ops,
 }
 
 static void
-ravenscar_prepare_to_store (struct regcache *regcache)
+ravenscar_prepare_to_store (struct target_ops *ops, struct regcache *regcache)
 {
   struct target_ops *beneath = find_target_beneath (&ravenscar_ops);
 
   if (!ravenscar_runtime_initialized ()
       || ptid_equal (inferior_ptid, base_magic_null_ptid)
       || ptid_equal (inferior_ptid, ravenscar_running_thread ()))
-    beneath->to_prepare_to_store (regcache);
+    beneath->to_prepare_to_store (beneath, regcache);
   else
     {
       struct gdbarch *gdbarch = get_regcache_arch (regcache);
diff --git a/gdb/record-full.c b/gdb/record-full.c
index dfb033b..1710a51 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 *ops,
+				   struct regcache *regcache)
 {
 }
 
diff --git a/gdb/remote-m32r-sdi.c b/gdb/remote-m32r-sdi.c
index e81b2de..f560388 100644
--- a/gdb/remote-m32r-sdi.c
+++ b/gdb/remote-m32r-sdi.c
@@ -1013,7 +1013,7 @@ m32r_store_register (struct target_ops *ops,
    debugged.  */
 
 static void
-m32r_prepare_to_store (struct regcache *regcache)
+m32r_prepare_to_store (struct target_ops *target, struct regcache *regcache)
 {
   /* Do nothing, since we can store individual regs.  */
   if (remote_debug)
diff --git a/gdb/remote-mips.c b/gdb/remote-mips.c
index e4e5083..5b3254d 100644
--- a/gdb/remote-mips.c
+++ b/gdb/remote-mips.c
@@ -90,7 +90,8 @@ static int mips_map_regno (struct gdbarch *, int);
 
 static void mips_set_register (int regno, ULONGEST value);
 
-static void mips_prepare_to_store (struct regcache *regcache);
+static void mips_prepare_to_store (struct target_ops *ops,
+				   struct regcache *regcache);
 
 static int mips_fetch_word (CORE_ADDR addr, unsigned int *valp);
 
@@ -2064,7 +2065,7 @@ mips_fetch_registers (struct target_ops *ops,
    registers, so this function doesn't have to do anything.  */
 
 static void
-mips_prepare_to_store (struct regcache *regcache)
+mips_prepare_to_store (struct target_ops *ops, struct regcache *regcache)
 {
 }
 
diff --git a/gdb/remote.c b/gdb/remote.c
index 8472e2a..5fe1c53 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -98,7 +98,8 @@ static void async_handle_remote_sigint_twice (int);
 
 static void remote_files_info (struct target_ops *ignore);
 
-static void remote_prepare_to_store (struct regcache *regcache);
+static void remote_prepare_to_store (struct target_ops *ops,
+				     struct regcache *regcache);
 
 static void remote_open (char *name, int from_tty);
 
@@ -6413,7 +6414,7 @@ remote_fetch_registers (struct target_ops *ops,
    first.  */
 
 static void
-remote_prepare_to_store (struct regcache *regcache)
+remote_prepare_to_store (struct target_ops *ops, struct regcache *regcache)
 {
   struct remote_arch_state *rsa = get_remote_arch_state ();
   int i;
diff --git a/gdb/target.c b/gdb/target.c
index 04dc063..eb2d2d0 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -83,8 +83,6 @@ static struct target_ops debug_target;
 
 static void debug_to_open (char *, int);
 
-static void debug_to_prepare_to_store (struct regcache *);
-
 static void debug_to_files_info (struct target_ops *);
 
 static int debug_to_insert_breakpoint (struct gdbarch *,
@@ -579,7 +577,7 @@ update_current_target (void)
       /* Do not inherit to_wait.  */
       /* Do not inherit to_fetch_registers.  */
       /* Do not inherit to_store_registers.  */
-      INHERIT (to_prepare_to_store, t);
+      /* Do not inherit to_prepare_to_store.  */
       INHERIT (deprecated_xfer_memory, t);
       INHERIT (to_files_info, t);
       INHERIT (to_insert_breakpoint, t);
@@ -713,9 +711,6 @@ update_current_target (void)
   de_fault (to_post_attach,
 	    (void (*) (int))
 	    target_ignore);
-  de_fault (to_prepare_to_store,
-	    (void (*) (struct regcache *))
-	    noprocess);
   de_fault (deprecated_xfer_memory,
 	    (int (*) (CORE_ADDR, gdb_byte *, int, int,
 		      struct mem_attrib *, struct target_ops *))
@@ -3996,6 +3991,26 @@ target_store_registers (struct regcache *regcache, int regno)
   noprocess ();
 }
 
+/* See target.h.  */
+
+void
+target_prepare_to_store (struct regcache *regcache)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    {
+      if (t->to_prepare_to_store != NULL)
+	{
+	  t->to_prepare_to_store (t, regcache);
+	  if (targetdebug)
+	    fprintf_unfiltered (gdb_stdlog, "target_prepare_to_store ()\n");
+
+	  return;
+	}
+    }
+}
+
 int
 target_core_of_thread (ptid_t ptid)
 {
@@ -4454,14 +4469,6 @@ target_call_history_range (ULONGEST begin, ULONGEST end, int flags)
   tcomplain ();
 }
 
-static void
-debug_to_prepare_to_store (struct regcache *regcache)
-{
-  debug_target.to_prepare_to_store (regcache);
-
-  fprintf_unfiltered (gdb_stdlog, "target_prepare_to_store ()\n");
-}
-
 static int
 deprecated_debug_xfer_memory (CORE_ADDR memaddr, bfd_byte *myaddr, int len,
 			      int write, struct mem_attrib *attrib,
@@ -4913,7 +4920,6 @@ setup_target_debug (void)
 
   current_target.to_open = debug_to_open;
   current_target.to_post_attach = debug_to_post_attach;
-  current_target.to_prepare_to_store = debug_to_prepare_to_store;
   current_target.deprecated_xfer_memory = deprecated_debug_xfer_memory;
   current_target.to_files_info = debug_to_files_info;
   current_target.to_insert_breakpoint = debug_to_insert_breakpoint;
diff --git a/gdb/target.h b/gdb/target.h
index 1cbf567..f20407d 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
@@ -1005,8 +1005,7 @@ extern void target_store_registers (struct regcache *regcache, int regs);
    that REGISTERS contains all the registers from the program being
    debugged.  */
 
-#define	target_prepare_to_store(regcache)	\
-     (*current_target.to_prepare_to_store) (regcache)
+extern void target_prepare_to_store (struct regcache *);
 
 /* Determine current address space of thread PTID.  */
 
-- 
1.8.3.1

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

* [PATCH v9 11/29] record-btrace: optionally indent function call history
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (8 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 08/29] record-btrace: fix insn range in function call history Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 18:23   ` Pedro Alves
  2013-12-19 16:45 ` [PATCH v9 12/29] record-btrace: make ranges include begin and end Markus Metzger
                   ` (19 subsequent siblings)
  29 siblings, 1 reply; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

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

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

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

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

Reviewed-by: Eli Zaretskii

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

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

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

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


---
 gdb/NEWS                                           |   6 +
 gdb/doc/gdb.texinfo                                |  12 +-
 gdb/record-btrace.c                                |  33 ++-
 gdb/record.c                                       |   4 +
 gdb/record.h                                       |   3 +
 gdb/testsuite/gdb.btrace/Makefile.in               |   3 +-
 gdb/testsuite/gdb.btrace/exception.cc              |  56 +++++
 gdb/testsuite/gdb.btrace/exception.exp             |  71 ++++++
 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 +++
 14 files changed, 727 insertions(+), 38 deletions(-)
 create mode 100644 gdb/testsuite/gdb.btrace/exception.cc
 create mode 100755 gdb/testsuite/gdb.btrace/exception.exp
 create mode 100644 gdb/testsuite/gdb.btrace/tailcall.exp
 create mode 100644 gdb/testsuite/gdb.btrace/unknown_functions.c
 create mode 100644 gdb/testsuite/gdb.btrace/unknown_functions.exp
 create mode 100644 gdb/testsuite/gdb.btrace/x86-tailcall.S
 create mode 100644 gdb/testsuite/gdb.btrace/x86-tailcall.c

diff --git a/gdb/NEWS b/gdb/NEWS
index fb1b4ce..12c424d 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -39,6 +39,12 @@
   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 instruction range is now prefixed with 'insn'.
+  The source line range is now prefixed with 'at'.
+
 * Python scripting
 
   ** Frame filters and frame decorators have been added.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index b7551c2..effc73d 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6482,7 +6482,9 @@ line for each sequence of instructions that belong to the same
 function giving the name of that function, the source lines
 for this instruction sequence (if the @code{/l} modifier is
 specified), and the instructions numbers that form the sequence (if
-the @code{/i} modifier is specified).
+the @code{/i} modifier is specified).  The function names are indented
+to reflect the call stack depth if the @code{/c} modifier is
+specified.
 
 @smallexample
 (@value{GDBP}) @b{list 1, 10}
@@ -6496,10 +6498,10 @@ the @code{/i} modifier is specified).
 8     foo ();
 9     ...
 10  @}
-(@value{GDBP}) @b{record function-call-history /l}
-1  foo.c:6-8   bar
-2  foo.c:2-3   foo
-3  foo.c:9-10  bar
+(@value{GDBP}) @b{record function-call-history /lc}
+1  bar     at foo.c:6,8
+2    foo   at foo.c:2,3
+3  bar     at foo.c:9,10
 @end smallexample
 
 By default, ten lines are printed.  This can be changed using the
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 466aef2..9470d85 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -434,7 +434,7 @@ btrace_call_history_insn_range (struct ui_out *uiout,
   end = begin + size - 1;
 
   ui_out_field_uint (uiout, "insn begin", begin);
-  ui_out_text (uiout, "-");
+  ui_out_text (uiout, ",");
   ui_out_field_uint (uiout, "insn end", end);
 }
 
@@ -466,7 +466,7 @@ btrace_call_history_src_line (struct ui_out *uiout,
   if (end == begin)
     return;
 
-  ui_out_text (uiout, "-");
+  ui_out_text (uiout, ",");
   ui_out_field_int (uiout, "max line", end);
 }
 
@@ -474,6 +474,7 @@ btrace_call_history_src_line (struct ui_out *uiout,
 
 static void
 btrace_call_history (struct ui_out *uiout,
+		     const struct btrace_thread_info *btinfo,
 		     const struct btrace_call_iterator *begin,
 		     const struct btrace_call_iterator *end,
 		     enum record_print_flag flags)
@@ -497,23 +498,33 @@ btrace_call_history (struct ui_out *uiout,
       ui_out_field_uint (uiout, "index", bfun->number);
       ui_out_text (uiout, "\t");
 
+      if ((flags & RECORD_PRINT_INDENT_CALLS) != 0)
+	{
+	  int level = bfun->level + btinfo->level, i;
+
+	  for (i = 0; i < level; ++i)
+	    ui_out_text (uiout, "  ");
+	}
+
+      if (sym != NULL)
+	ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (sym));
+      else if (msym != NULL)
+	ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (msym));
+      else if (!ui_out_is_mi_like_p (uiout))
+	ui_out_field_string (uiout, "function", "??");
+
       if ((flags & RECORD_PRINT_INSN_RANGE) != 0)
 	{
+	  ui_out_text (uiout, _("\tinst "));
 	  btrace_call_history_insn_range (uiout, bfun);
-	  ui_out_text (uiout, "\t");
 	}
 
       if ((flags & RECORD_PRINT_SRC_LINE) != 0)
 	{
+	  ui_out_text (uiout, _("\tat "));
 	  btrace_call_history_src_line (uiout, bfun);
-	  ui_out_text (uiout, "\t");
 	}
 
-      if (sym != NULL)
-	ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (sym));
-      else if (msym != NULL)
-	ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (msym));
-
       ui_out_text (uiout, "\n");
     }
 }
@@ -570,7 +581,7 @@ record_btrace_call_history (int size, int flags)
     }
 
   if (covered > 0)
-    btrace_call_history (uiout, &begin, &end, flags);
+    btrace_call_history (uiout, btinfo, &begin, &end, flags);
   else
     {
       if (size < 0)
@@ -622,7 +633,7 @@ record_btrace_call_history_range (ULONGEST from, ULONGEST to, int flags)
   if (found == 0)
     btrace_call_end (&end, btinfo);
 
-  btrace_call_history (uiout, &begin, &end, flags);
+  btrace_call_history (uiout, btinfo, &begin, &end, flags);
   btrace_set_call_history (btinfo, &begin, &end);
 
   do_cleanups (uiout_cleanup);
diff --git a/gdb/record.c b/gdb/record.c
index ec2a042..e78070b 100644
--- a/gdb/record.c
+++ b/gdb/record.c
@@ -575,6 +575,9 @@ get_call_history_modifiers (char **arg)
 	    case 'i':
 	      modifiers |= RECORD_PRINT_INSN_RANGE;
 	      break;
+	    case 'c':
+	      modifiers |= RECORD_PRINT_INDENT_CALLS;
+	      break;
 	    default:
 	      error (_("Invalid modifier: %c."), *args);
 	    }
@@ -809,6 +812,7 @@ function.\n\
 Without modifiers, it prints the function name.\n\
 With a /l modifier, the source file and line number range is included.\n\
 With a /i modifier, the instruction number range is included.\n\
+With a /c modifier, the output is indented based on the call stack depth.\n\
 With no argument, prints ten more lines after the previous ten-line print.\n\
 \"record function-call-history -\" prints ten lines before a previous ten-line \
 print.\n\
diff --git a/gdb/record.h b/gdb/record.h
index 124c32b..323b8d1 100644
--- a/gdb/record.h
+++ b/gdb/record.h
@@ -40,6 +40,9 @@ enum record_print_flag
 
   /* Print the instruction number range (if applicable).  */
   RECORD_PRINT_INSN_RANGE = (1 << 1),
+
+  /* Indent based on call stack depth (if applicable).  */
+  RECORD_PRINT_INDENT_CALLS = (1 << 2)
 };
 
 /* Wrapper for target_read_memory that prints a debug message if
diff --git a/gdb/testsuite/gdb.btrace/Makefile.in b/gdb/testsuite/gdb.btrace/Makefile.in
index f4c06d1..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..5a0d625
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/exception.exp
@@ -0,0 +1,71 @@
+# 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
+#
+# here we see a known bug that the indentation starts at level 1 with
+# two leading spaces instead of level 0 without leading spaces.
+send_gdb "record function-call-history /c 1\n"
+gdb_expect_list "indented" "\r\n$gdb_prompt $" [list \
+  [join [list \
+    "1\t  test\\(\\)" \
+    "2\t    foo\\(\\)" \
+    "3\t      bar\\(\\)" \
+    "4\t        bad\\(\\)\r" \
+    ] "\r\n"] \
+  "" \
+  "\[0-9\]*\t  test\\(\\)"]
diff --git a/gdb/testsuite/gdb.btrace/function_call_history.exp b/gdb/testsuite/gdb.btrace/function_call_history.exp
index e233d17..f149757 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] 66+ messages in thread

* [PATCH v9 23/29] record-btrace: provide target_find_new_threads method
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (5 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 13/29] btrace: add replay position to btrace thread info Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 19:32   ` Pedro Alves
  2013-12-19 16:45 ` [PATCH v9 16/29] frame, backtrace: allow targets to supply a frame unwinder Markus Metzger
                   ` (22 subsequent siblings)
  29 siblings, 1 reply; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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.

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

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


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

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

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

* [PATCH v9 06/29] frame: add frame_id_build_unavailable_stack_special
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (24 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 14/29] target: add ops parameter to to_prepare_to_store method Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 18:12   ` Pedro Alves
  2013-12-19 16:45 ` [PATCH v9 02/29] btrace, linux: fix memory leak when reading branch trace Markus Metzger
                   ` (3 subsequent siblings)
  29 siblings, 1 reply; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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.

2013-12-19  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 99c2309..8facdc00 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 8b6755e..00943ad 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] 66+ messages in thread

* [PATCH v9 17/29] frame, cfa: check unwind stop reason first
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (26 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 02/29] btrace, linux: fix memory leak when reading branch trace Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 16:45 ` [PATCH v9 19/29] record-btrace, frame: supply target-specific unwinder Markus Metzger
  2013-12-19 20:23 ` [PATCH v9 00/29] record-btrace: reverse Pedro Alves
  29 siblings, 0 replies; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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 91b2120..adad2dc 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] 66+ messages in thread

* [PATCH v9 26/29] btrace, gdbserver: read branch trace incrementally
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (19 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 05/29] frame: add frame_is_tailcall function Markus Metzger
@ 2013-12-19 16:45 ` Markus Metzger
  2013-12-19 16:45 ` [PATCH v9 15/29] record-btrace: supply register target methods Markus Metzger
                   ` (8 subsequent siblings)
  29 siblings, 0 replies; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 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

2013-12-19  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 f387604..ceb880a 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -254,6 +254,10 @@ qXfer:libraries-svr4:read's annex
   necessary for library list updating, resulting in significant
   speedup.
 
+qXfer:btrace:read's annex
+  The qXfer:btrace:read packet supports a new annex 'delta' to read
+  branch trace incrementally.
+
 * New features in the GDB remote stub, GDBserver
 
   ** GDBserver now supports target-assisted range stepping.  Currently
diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c
index b1676ac..d9acda3 100644
--- a/gdb/amd64-linux-nat.c
+++ b/gdb/amd64-linux-nat.c
@@ -1173,10 +1173,10 @@ amd64_linux_enable_btrace (ptid_t ptid)
 static void
 amd64_linux_disable_btrace (struct btrace_target_info *tinfo)
 {
-  int errcode = linux_disable_btrace (tinfo);
+  enum btrace_error errcode = linux_disable_btrace (tinfo);
 
-  if (errcode != 0)
-    error (_("Could not disable branch tracing: %s."), safe_strerror (errcode));
+  if (errcode != BTRACE_ERR_NONE)
+    error (_("Could not disable branch tracing."));
 }
 
 /* Teardown branch tracing.  */
diff --git a/gdb/btrace.c b/gdb/btrace.c
index 1f35796..c86a866 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -599,9 +599,9 @@ btrace_compute_ftrace (struct btrace_thread_info *btinfo,
   DEBUG ("compute ftrace");
 
   gdbarch = target_gdbarch ();
-  begin = NULL;
-  end = NULL;
-  level = INT_MAX;
+  begin = btinfo->begin;
+  end = btinfo->end;
+  level = begin != NULL ? -btinfo->level : INT_MAX;
   blk = VEC_length (btrace_block_s, btrace);
 
   while (blk != 0)
@@ -717,27 +717,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);
     }
 
@@ -772,13 +903,7 @@ btrace_clear (struct thread_info *tp)
   btinfo->begin = NULL;
   btinfo->end = NULL;
 
-  xfree (btinfo->insn_history);
-  xfree (btinfo->call_history);
-  xfree (btinfo->replay);
-
-  btinfo->insn_history = NULL;
-  btinfo->call_history = NULL;
-  btinfo->replay = NULL;
+  btrace_clear_history (btinfo);
 }
 
 /* See btrace.h.  */
@@ -870,10 +995,7 @@ parse_xml_btrace (const char *buffer)
   errcode = gdb_xml_parse_quick (_("btrace"), "btrace.dtd", btrace_elements,
 				 buffer, &btrace);
   if (errcode != 0)
-    {
-      do_cleanups (cleanup);
-      return NULL;
-    }
+    error (_("Error parsing branch trace."));
 
   /* Keep parse results.  */
   discard_cleanups (cleanup);
diff --git a/gdb/common/btrace-common.h b/gdb/common/btrace-common.h
index 1986868..4bab92e 100644
--- a/gdb/common/btrace-common.h
+++ b/gdb/common/btrace-common.h
@@ -42,7 +42,9 @@
    asynchronous, e.g. interrupts.  */
 struct btrace_block
 {
-  /* The address of the first byte of the first instruction in the block.  */
+  /* The address of the first byte of the first instruction in the block.
+     The address may be zero if we do not know the beginning of this block,
+     such as for the first block in a delta trace.  */
   CORE_ADDR begin;
 
   /* The address of the first byte of the last instruction in the block.  */
@@ -67,7 +69,28 @@ enum btrace_read_type
   BTRACE_READ_ALL,
 
   /* Send all available trace, if it changed.  */
-  BTRACE_READ_NEW
+  BTRACE_READ_NEW,
+
+  /* Send the trace since the last request.  This will fail if the trace
+     buffer overflowed.  */
+  BTRACE_READ_DELTA
+};
+
+/* Enumeration of btrace errors.  */
+
+enum btrace_error
+{
+  /* No error.  Everything is OK.  */
+  BTRACE_ERR_NONE,
+
+  /* An unknown error.  */
+  BTRACE_ERR_UNKNOWN,
+
+  /* Branch tracing is not supported on this system.  */
+  BTRACE_ERR_NOT_SUPPORTED,
+
+  /* The branch trace buffer overflowed; no delta read possible.  */
+  BTRACE_ERR_OVERFLOW
 };
 
 #endif /* BTRACE_COMMON_H */
diff --git a/gdb/common/linux-btrace.c b/gdb/common/linux-btrace.c
index 801505b..4996a25 100644
--- a/gdb/common/linux-btrace.c
+++ b/gdb/common/linux-btrace.c
@@ -172,11 +172,11 @@ perf_event_sample_ok (const struct perf_event_sample *sample)
 
 static VEC (btrace_block_s) *
 perf_event_read_bts (struct btrace_target_info* tinfo, const uint8_t *begin,
-		     const uint8_t *end, const uint8_t *start)
+		     const uint8_t *end, const uint8_t *start, size_t size)
 {
   VEC (btrace_block_s) *btrace = NULL;
   struct perf_event_sample sample;
-  size_t read = 0, size = (end - begin);
+  size_t read = 0;
   struct btrace_block block = { 0, 0 };
   struct regcache *regcache;
 
@@ -252,6 +252,13 @@ perf_event_read_bts (struct btrace_target_info* tinfo, const uint8_t *begin,
       block.end = psample->bts.from;
     }
 
+  /* Push the last block (i.e. the first one of inferior execution), as well.
+     We don't know where it ends, but we know where it starts.  If we're
+     reading delta trace, we can fill in the start address later on.
+     Otherwise we will prune it.  */
+  block.begin = 0;
+  VEC_safe_push (btrace_block_s, btrace, &block);
+
   return btrace;
 }
 
@@ -476,7 +483,7 @@ linux_enable_btrace (ptid_t ptid)
 
 /* See linux-btrace.h.  */
 
-int
+enum btrace_error
 linux_disable_btrace (struct btrace_target_info *tinfo)
 {
   int errcode;
@@ -484,12 +491,12 @@ linux_disable_btrace (struct btrace_target_info *tinfo)
   errno = 0;
   errcode = munmap (tinfo->buffer, perf_event_mmap_size (tinfo));
   if (errcode != 0)
-    return errno;
+    return BTRACE_ERR_UNKNOWN;
 
   close (tinfo->file);
   xfree (tinfo);
 
-  return 0;
+  return BTRACE_ERR_NONE;
 }
 
 /* Check whether the branch trace has changed.  */
@@ -504,21 +511,24 @@ linux_btrace_has_changed (struct btrace_target_info *tinfo)
 
 /* See linux-btrace.h.  */
 
-VEC (btrace_block_s) *
-linux_read_btrace (struct btrace_target_info *tinfo,
+enum btrace_error
+linux_read_btrace (VEC (btrace_block_s) **btrace,
+		   struct btrace_target_info *tinfo,
 		   enum btrace_read_type type)
 {
-  VEC (btrace_block_s) *btrace = NULL;
   volatile struct perf_event_mmap_page *header;
   const uint8_t *begin, *end, *start;
-  unsigned long data_head, retries = 5;
-  size_t buffer_size;
+  unsigned long data_head, data_tail, retries = 5;
+  size_t buffer_size, size;
 
+  /* For delta reads, we return at least the partial last block containing
+     the current PC.  */
   if (type == BTRACE_READ_NEW && !linux_btrace_has_changed (tinfo))
-    return NULL;
+    return BTRACE_ERR_NONE;
 
   header = perf_event_header (tinfo);
   buffer_size = perf_event_buffer_size (tinfo);
+  data_tail = tinfo->data_head;
 
   /* We may need to retry reading the trace.  See below.  */
   while (retries--)
@@ -526,23 +536,45 @@ linux_read_btrace (struct btrace_target_info *tinfo,
       data_head = header->data_head;
 
       /* Delete any leftover trace from the previous iteration.  */
-      VEC_free (btrace_block_s, btrace);
+      VEC_free (btrace_block_s, *btrace);
 
-      /* If there's new trace, let's read it.  */
-      if (data_head != tinfo->data_head)
+      if (type == BTRACE_READ_DELTA)
 	{
-	  /* Data_head keeps growing; the buffer itself is circular.  */
-	  begin = perf_event_buffer_begin (tinfo);
-	  start = begin + data_head % buffer_size;
-
-	  if (data_head <= buffer_size)
-	    end = start;
-	  else
-	    end = perf_event_buffer_end (tinfo);
+	  /* Determine the number of bytes to read and check for buffer
+	     overflows.  */
+
+	  /* Check for data head overflows.  We might be able to recover from
+	     those but they are very unlikely and it's not really worth the
+	     effort, I think.  */
+	  if (data_head < data_tail)
+	    return BTRACE_ERR_OVERFLOW;
+
+	  /* If the buffer is smaller than the trace delta, we overflowed.  */
+	  size = data_head - data_tail;
+	  if (buffer_size < size)
+	    return BTRACE_ERR_OVERFLOW;
+	}
+      else
+	{
+	  /* Read the entire buffer.  */
+	  size = buffer_size;
 
-	  btrace = perf_event_read_bts (tinfo, begin, end, start);
+	  /* Adjust the size if the buffer has not overflowed, yet.  */
+	  if (data_head < size)
+	    size = data_head;
 	}
 
+      /* Data_head keeps growing; the buffer itself is circular.  */
+      begin = perf_event_buffer_begin (tinfo);
+      start = begin + data_head % buffer_size;
+
+      if (data_head <= buffer_size)
+	end = start;
+      else
+	end = perf_event_buffer_end (tinfo);
+
+      *btrace = perf_event_read_bts (tinfo, begin, end, start, size);
+
       /* The stopping thread notifies its ptracer before it is scheduled out.
 	 On multi-core systems, the debugger might therefore run while the
 	 kernel might be writing the last branch trace records.
@@ -554,7 +586,13 @@ linux_read_btrace (struct btrace_target_info *tinfo,
 
   tinfo->data_head = data_head;
 
-  return btrace;
+  /* Prune the incomplete last block (i.e. the first one of inferior execution)
+     if we're not doing a delta read.  There is no way of filling in its zeroed
+     BEGIN element.  */
+  if (!VEC_empty (btrace_block_s, *btrace) && type != BTRACE_READ_DELTA)
+    VEC_pop (btrace_block_s, *btrace);
+
+  return BTRACE_ERR_NONE;
 }
 
 #else /* !HAVE_LINUX_PERF_EVENT_H */
@@ -577,19 +615,20 @@ linux_enable_btrace (ptid_t ptid)
 
 /* See linux-btrace.h.  */
 
-int
+enum btrace_error
 linux_disable_btrace (struct btrace_target_info *tinfo)
 {
-  return ENOSYS;
+  return BTRACE_ERR_NOT_SUPPORTED;
 }
 
 /* See linux-btrace.h.  */
 
-VEC (btrace_block_s) *
-linux_read_btrace (struct btrace_target_info *tinfo,
+enum btrace_error
+linux_read_btrace (VEC (btrace_block_s) **btrace,
+		   struct btrace_target_info *tinfo,
 		   enum btrace_read_type type)
 {
-  return NULL;
+  return BTRACE_ERR_NOT_SUPPORTED;
 }
 
 #endif /* !HAVE_LINUX_PERF_EVENT_H */
diff --git a/gdb/common/linux-btrace.h b/gdb/common/linux-btrace.h
index d4e8402..0f0412c 100644
--- a/gdb/common/linux-btrace.h
+++ b/gdb/common/linux-btrace.h
@@ -61,17 +61,18 @@ struct btrace_target_info
   int ptr_bits;
 };
 
-/* Check whether branch tracing is supported.  */
+/* See to_supports_btrace in target.h.  */
 extern int linux_supports_btrace (void);
 
-/* Enable branch tracing for @ptid.  */
+/* See to_enable_btrace in target.h.  */
 extern struct btrace_target_info *linux_enable_btrace (ptid_t ptid);
 
-/* Disable branch tracing and deallocate @tinfo.  */
-extern int linux_disable_btrace (struct btrace_target_info *tinfo);
+/* See to_disable_btrace in target.h.  */
+extern enum btrace_error linux_disable_btrace (struct btrace_target_info *ti);
 
-/* Read branch trace data.  */
-extern VEC (btrace_block_s) *linux_read_btrace (struct btrace_target_info *,
-						enum btrace_read_type);
+/* See to_read_btrace in target.h.  */
+extern enum btrace_error linux_read_btrace (VEC (btrace_block_s) **btrace,
+					    struct btrace_target_info *btinfo,
+					    enum btrace_read_type type);
 
 #endif /* LINUX_BTRACE_H */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 5c3f548..10e86e5 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -39921,6 +39921,14 @@ Returns all available branch trace.
 @item new
 Returns all available branch trace if the branch trace changed since
 the last read request.
+
+@item delta
+Returns the new branch trace since the last read request.  Adds a new
+block to the end of the trace that begins at zero and ends at the source
+location of the first branch in the trace buffer.  This extra block is
+used to stitch traces together.
+
+If the trace buffer overflowed, returns an error indicating the overflow.
 @end table
 
 This packet is not probed by default; the remote stub must request it
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 770ee16..679b352 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -5705,7 +5705,7 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
 
 #ifdef HAVE_LINUX_BTRACE
 
-/* Enable branch tracing.  */
+/* See to_enable_btrace target method.  */
 
 static struct btrace_target_info *
 linux_low_enable_btrace (ptid_t ptid)
@@ -5725,17 +5725,39 @@ linux_low_enable_btrace (ptid_t ptid)
   return tinfo;
 }
 
-/* Read branch trace data as btrace xml document.  */
+/* See to_disable_btrace target method.  */
 
-static void
+static int
+linux_low_disable_btrace (struct btrace_target_info *tinfo)
+{
+  enum btrace_error err;
+
+  err = linux_disable_btrace (tinfo);
+  return (err == BTRACE_ERR_NONE ? 0 : -1);
+}
+
+/* See to_read_btrace target method.  */
+
+static int
 linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
 		       int type)
 {
   VEC (btrace_block_s) *btrace;
   struct btrace_block *block;
+  enum btrace_error err;
   int i;
 
-  btrace = linux_read_btrace (tinfo, type);
+  btrace = NULL;
+  err = linux_read_btrace (&btrace, tinfo, type);
+  if (err != BTRACE_ERR_NONE)
+    {
+      if (err == BTRACE_ERR_OVERFLOW)
+	buffer_grow_str0 (buffer, "E.Overflow.");
+      else
+	buffer_grow_str0 (buffer, "E.Generic Error.");
+
+      return -1;
+    }
 
   buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
   buffer_grow_str (buffer, "<btrace version=\"1.0\">\n");
@@ -5744,9 +5766,11 @@ linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
     buffer_xml_printf (buffer, "<block begin=\"0x%s\" end=\"0x%s\"/>\n",
 		       paddress (block->begin), paddress (block->end));
 
-  buffer_grow_str (buffer, "</btrace>\n");
+  buffer_grow_str0 (buffer, "</btrace>\n");
 
   VEC_free (btrace_block_s, btrace);
+
+  return 0;
 }
 #endif /* HAVE_LINUX_BTRACE */
 
@@ -5819,7 +5843,7 @@ static struct target_ops linux_target_ops = {
 #ifdef HAVE_LINUX_BTRACE
   linux_supports_btrace,
   linux_low_enable_btrace,
-  linux_disable_btrace,
+  linux_low_disable_btrace,
   linux_low_read_btrace,
 #else
   NULL,
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 1a110e0..5903edf 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1348,7 +1348,7 @@ handle_qxfer_btrace (const char *annex,
 {
   static struct buffer cache;
   struct thread_info *thread;
-  int type;
+  int type, result;
 
   if (the_target->read_btrace == NULL || writebuf != NULL)
     return -2;
@@ -1380,6 +1380,8 @@ handle_qxfer_btrace (const char *annex,
     type = BTRACE_READ_ALL;
   else if (strcmp (annex, "new") == 0)
     type = BTRACE_READ_NEW;
+  else if (strcmp (annex, "delta") == 0)
+    type = BTRACE_READ_DELTA;
   else
     {
       strcpy (own_buf, "E.Bad annex.");
@@ -1390,7 +1392,12 @@ handle_qxfer_btrace (const char *annex,
     {
       buffer_free (&cache);
 
-      target_read_btrace (thread->btrace, &cache, type);
+      result = target_read_btrace (thread->btrace, &cache, type);
+      if (result != 0)
+	{
+	  memcpy (own_buf, cache.buffer, cache.used_size);
+	  return -3;
+	}
     }
   else if (offset > cache.used_size)
     {
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index c5e6fee..6969b5c 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -356,12 +356,15 @@ struct target_ops
      information struct for reading and for disabling branch trace.  */
   struct btrace_target_info *(*enable_btrace) (ptid_t ptid);
 
-  /* Disable branch tracing.  */
+  /* Disable branch tracing.
+     Returns zero on success, non-zero otherwise.  */
   int (*disable_btrace) (struct btrace_target_info *tinfo);
 
   /* Read branch trace data into buffer.  We use an int to specify the type
-     to break a cyclic dependency.  */
-  void (*read_btrace) (struct btrace_target_info *, struct buffer *, int type);
+     to break a cyclic dependency.
+     Return 0 on success; print an error message into BUFFER and return -1,
+     otherwise.  */
+  int (*read_btrace) (struct btrace_target_info *, struct buffer *, int type);
 
   /* Return true if target supports range stepping.  */
   int (*supports_range_stepping) (void);
diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c
index c2f4fcc..f90bfaa 100644
--- a/gdb/i386-linux-nat.c
+++ b/gdb/i386-linux-nat.c
@@ -1084,10 +1084,10 @@ i386_linux_enable_btrace (ptid_t ptid)
 static void
 i386_linux_disable_btrace (struct btrace_target_info *tinfo)
 {
-  int errcode = linux_disable_btrace (tinfo);
+  enum btrace_error errcode = linux_disable_btrace (tinfo);
 
-  if (errcode != 0)
-    error (_("Could not disable branch tracing: %s."), safe_strerror (errcode));
+  if (errcode != BTRACE_ERR_NONE)
+    error (_("Could not disable branch tracing."));
 }
 
 /* Teardown branch tracing.  */
diff --git a/gdb/remote.c b/gdb/remote.c
index 99ab6e0..0c22b76 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -11430,13 +11430,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;
 
@@ -11455,6 +11456,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."),
@@ -11463,15 +11467,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 c9fef6b..45261ef 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4234,18 +4234,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 212f8d0..231a435 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);
@@ -1983,8 +1987,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] 66+ messages in thread

* [PATCH v9 00/29] record-btrace: reverse
@ 2013-12-19 16:45 Markus Metzger
  2013-12-19 16:45 ` [PATCH v9 22/29] record-btrace: add to_wait and to_resume target methods Markus Metzger
                   ` (29 more replies)
  0 siblings, 30 replies; 66+ messages in thread
From: Markus Metzger @ 2013-12-19 16:45 UTC (permalink / raw)
  To: jan.kratochvil, palves; +Cc: gdb-patches

This is a bigger update addressing Pedro's feedback.

I discarded my frame_id changes and instead use Pedro's unavailable stack
changes.  See "frame: add frame_id_build_unavailable_stack_special".

The v8 "record-btrace: provide xfer_partial target method" patch did not
have any effect since the caller would simply continue with the target
beneath.  I changed this to now throw an error if memory is not available.
To avoid issues with GDB trying to insert and remove breakpoints, this is
disabled for the duration of to_insert_breakoint and to_remove_breakpoint.

This needs a preparation patch "target, breakpoint: allow insert/remove
breakpoint to be forwarded" to allow ther espective target methods to be
forwarded.

I added a test covering the error on memory accesses to "record-btrace:
add (reverse-)stepping support".

The "frame, backtrace: allow targets to supply a frame unwinder" patch
has been split resulting in new patches "frame, cfa: check unwind stop
reason first" and "frame: do not assume unwinding will succeed".

A new patch "target: allow decr_pc_after_break to be defined by the target"
has been added to prepare for avoiding pc adjustments after break during
replay in "record-btrace: add (reverse-)stepping support".

This should also apply to the s/w record target but the condition under
which the pc needs to be adjusted is more complicated, here.

In "gdbarch: add instruction predicate methods" I added "::0" to the new
items I added to gdbarch.sh to fix an issue with verify_gdbarch.

There are lots of changes to all tests throughout the series plus a new
patch "btrace, test: fix multi-line btrace tests" to fix up existing tests.


Markus Metzger (29):
  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_is_tailcall function
  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
  target: add ops parameter to to_prepare_to_store method
  record-btrace: supply register target methods
  frame, backtrace: allow targets to supply a frame unwinder
  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                                           |   25 +
 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                                       | 1616 +++++++++++++++----
 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-tailcall.c                        |    4 +-
 gdb/dwarf2-frame.c                                 |   14 +-
 gdb/exec.c                                         |    3 +-
 gdb/frame-unwind.c                                 |   86 +-
 gdb/frame.c                                        |   48 +-
 gdb/frame.h                                        |   14 +-
 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/i386-linux-nat.c                               |    6 +-
 gdb/i386-tdep.c                                    |   59 +
 gdb/inf-child.c                                    |    2 +-
 gdb/infcmd.c                                       |    6 +-
 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                                   |    4 +-
 gdb/ravenscar-thread.c                             |    7 +-
 gdb/record-btrace.c                                | 1642 +++++++++++++++++---
 gdb/record-full.c                                  |   37 +-
 gdb/record.c                                       |    4 +
 gdb/record.h                                       |    7 +
 gdb/remote-m32r-sdi.c                              |    8 +-
 gdb/remote-mips.c                                  |   13 +-
 gdb/remote.c                                       |   42 +-
 gdb/stack.c                                        |    2 +-
 gdb/target.c                                       |  148 +-
 gdb/target.h                                       |   73 +-
 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             |   73 +
 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 +
 72 files changed, 6113 insertions(+), 987 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] 66+ messages in thread

* Re: [PATCH v9 09/29] record-btrace: start counting at one
  2013-12-19 16:45 ` [PATCH v9 09/29] record-btrace: start counting at one Markus Metzger
@ 2013-12-19 17:27   ` Eli Zaretskii
  0 siblings, 0 replies; 66+ messages in thread
From: Eli Zaretskii @ 2013-12-19 17:27 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: Thu, 19 Dec 2013 17:44:39 +0100
> 
> 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.
> 
> CC: Eli Zaretskii  <eliz@gnu.org>
> 
> 2013-12-19  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(-)

OK for the NEWS part.

Thanks.

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

* Re: [PATCH v9 12/29] record-btrace: make ranges include begin and end
  2013-12-19 16:45 ` [PATCH v9 12/29] record-btrace: make ranges include begin and end Markus Metzger
@ 2013-12-19 17:29   ` Eli Zaretskii
  0 siblings, 0 replies; 66+ messages in thread
From: Eli Zaretskii @ 2013-12-19 17:29 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: Thu, 19 Dec 2013 17:44:42 +0100
> 
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -45,6 +45,9 @@
>    The instruction range is now prefixed with 'insn'.
>    The source line range is now prefixed with 'at'.
>  
> +* The ranges given as arguments to the 'record function-call-history' and
> +  'record instruction-history' commands are now inclusive.

This part is OK.

>  @item record instruction-history @var{begin} @var{end}
>  Disassembles instructions beginning with instruction number
> -@var{begin} until instruction number @var{end}.  The instruction
> -number @var{end} is not included.
> +@var{begin} until instruction number @var{end}.
>  @end table

The previous text explicitly said the range was not inclusive, so I
suggest that we now say it _is_ inclusive.  I always ask myself this
question when I read similar descriptions that don't spell that out.

>  @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}.

Same here.

The documentation parts are OK with these changes.

Thanks.

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

* Re: [PATCH v9 29/29] record-btrace: add (reverse-)stepping support
  2013-12-19 16:45 ` [PATCH v9 29/29] record-btrace: add (reverse-)stepping support Markus Metzger
@ 2013-12-19 17:31   ` Eli Zaretskii
  2013-12-19 20:10   ` Pedro Alves
  1 sibling, 0 replies; 66+ messages in thread
From: Eli Zaretskii @ 2013-12-19 17:31 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: Thu, 19 Dec 2013 17:44:59 +0100
> 
> 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.

Thanks, the documentation bits are OK.

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

* Re: [PATCH v9 01/29] btrace, test: fix multi-line btrace tests
  2013-12-19 16:45 ` [PATCH v9 01/29] btrace, test: fix multi-line btrace tests Markus Metzger
@ 2013-12-19 18:01   ` Pedro Alves
  0 siblings, 0 replies; 66+ messages in thread
From: Pedro Alves @ 2013-12-19 18:01 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/19/2013 04:44 PM, Markus Metzger wrote:
> --- 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 \

This gdb_test should be indented.

> +  "0\tmain" \
> +  "1\tinc" \
> +  "2\tmain" \

Otherwise looks good to me.  Thanks for doing this.

-- 
Pedro Alves

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

* Re: [PATCH v9 06/29] frame: add frame_id_build_unavailable_stack_special
  2013-12-19 16:45 ` [PATCH v9 06/29] frame: add frame_id_build_unavailable_stack_special Markus Metzger
@ 2013-12-19 18:12   ` Pedro Alves
  0 siblings, 0 replies; 66+ messages in thread
From: Pedro Alves @ 2013-12-19 18:12 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/19/2013 04:44 PM, Markus Metzger wrote:
> Add a function to build a frame_id for a frame with unavailable stack
> and with a special identifier address.
> 
> 2013-12-19  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.

OK.

-- 
Pedro Alves

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

* Re: [PATCH v9 11/29] record-btrace: optionally indent function call history
  2013-12-19 16:45 ` [PATCH v9 11/29] record-btrace: optionally indent " Markus Metzger
@ 2013-12-19 18:23   ` Pedro Alves
  2013-12-20 12:54     ` Metzger, Markus T
  0 siblings, 1 reply; 66+ messages in thread
From: Pedro Alves @ 2013-12-19 18:23 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/19/2013 04:44 PM, Markus Metzger wrote:
> 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 ".

I was a little surprised the manual didn't get an update for this one,
but I see an /i example is currently lacking.  Can one use both
/i and /l at the same time ?

> Change the range syntax from "begin-end" to "begin,end" to allow copy&paste to
> the "record instruction-history" and "list" commands.

(This bit seems to be missing in NEWS.  Not sure it was on purpose.)

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

Hmm.  Why are we adding known bugs?  I'm not sure I understood it, but from
your description it sounds like the condition should be detectable?

-- 
Pedro Alves

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

* Re: [PATCH v9 14/29] target: add ops parameter to to_prepare_to_store method
  2013-12-19 16:45 ` [PATCH v9 14/29] target: add ops parameter to to_prepare_to_store method Markus Metzger
@ 2013-12-19 18:30   ` Pedro Alves
  2013-12-19 21:13     ` Tom Tromey
  0 siblings, 1 reply; 66+ messages in thread
From: Pedro Alves @ 2013-12-19 18:30 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/19/2013 04:44 PM, Markus Metzger wrote:
> To allow forwarding the prepare_to_store request to the target beneath,
> add a target_ops * parameter.
> 
> 2013-12-19  Markus Metzger  <markus.t.metzger@intel.com>
> 
> 	* target.h (target_ops) <to_prepare_to_store>: Add parameter.
> 	(target_prepare_to_store): Remove macro.  New function.
> 	* target.c (update_current_target): Do not inherit/default
> 	prepare_to_store.
> 	(target_prepare_to_store): New.
> 	(debug_to_prepare_to_store): Remove.
> 	* remote.c (remote_prepare_to_store): Add parameter.
> 	* remote-mips.c (mips_prepare_to_store): Add parameter.
> 	* remote-m32r-sdi.c (m32r_prepare_to_store): Add parameter.
> 	* ravenscar-thread.c (ravenscar_prepare_to_store): Add
> 	parameter.
> 	* monitor.c (monitor_prepare_to_store): Add parameter.
> 	* inf-child.c (inf_child_prepare_to_store): Add parameter.

There are more sites that need updating.  E.g., go32-nat.c,
nto-procfs.c, windows-nat.c, possibly others.  Try grepping
for to_prepare_to_store.



> -  de_fault (to_prepare_to_store,
> -	    (void (*) (struct regcache *))
> -	    noprocess);

...

> +void
> +target_prepare_to_store (struct regcache *regcache)
> +{
> +  struct target_ops *t;
> +
> +  for (t = current_target.beneath; t != NULL; t = t->beneath)
> +    {
> +      if (t->to_prepare_to_store != NULL)
> +	{
> +	  t->to_prepare_to_store (t, regcache);
> +	  if (targetdebug)
> +	    fprintf_unfiltered (gdb_stdlog, "target_prepare_to_store ()\n");
> +
> +	  return;
> +	}
> +    }

There should be a noprocess() call after the loop here.

> +}

-- 
Pedro Alves

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

* Re: [PATCH v9 16/29] frame, backtrace: allow targets to supply a frame unwinder
  2013-12-19 16:45 ` [PATCH v9 16/29] frame, backtrace: allow targets to supply a frame unwinder Markus Metzger
@ 2013-12-19 18:41   ` Pedro Alves
  0 siblings, 0 replies; 66+ messages in thread
From: Pedro Alves @ 2013-12-19 18:41 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/19/2013 04:44 PM, Markus Metzger wrote:
> Allow targets to supply their own target-specific frame unwinders; one for
> normal frames and one for tailcall frames.  If a target-specific unwinder
> is supplied, it will be chosen before any other unwinder.
> 
> The original patch has been split into this and the next two patches.

Looks good.  I think a target interface that allowed the
target to try any number of unwinders in the order it preferred
would be better.  E.g., by having a single target method that
returned a NULL terminated array of unwinders.  Anyway, that can
always be changed later.

-- 
Pedro Alves

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

* Re: [PATCH v9 20/29] target, breakpoint: allow insert/remove breakpoint to be forwarded
  2013-12-19 16:45 ` [PATCH v9 20/29] target, breakpoint: allow insert/remove breakpoint to be forwarded Markus Metzger
@ 2013-12-19 19:08   ` Pedro Alves
  0 siblings, 0 replies; 66+ messages in thread
From: Pedro Alves @ 2013-12-19 19:08 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/19/2013 04:44 PM, Markus Metzger wrote:

>  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 +1765,8 @@ 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);
> +      ret = record_full_beneath_to_insert_breakpoint (ops->beneath, gdbarch,

Pedantically, this shouldn't be ops->beneath (until Tromey's series is done).
You'll need to record the right ops in record_full_open
(like e.g., tmp_to_resume_ops).

> +int
> +forward_target_remove_breakpoint (struct target_ops *ops,
> +				  struct gdbarch *gdbarch,
> +				  struct bp_target_info *bp_tgt)
> +{

This is quite like Tromey's original target_delegate_... functions
in the target-async series.  Oh well, let's proceed, and it'll
end up redone when Tromey's done.  Please leave the may_insert_breakpoint
checks not in the forward function, but still in
target_remote|insert_breakpoint.  No need to redo that check at
each forward while traversing the target stack.

-- 
Pedro Alves

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

* Re: [PATCH v9 21/29] record-btrace: provide xfer_partial target method
  2013-12-19 16:45 ` [PATCH v9 21/29] record-btrace: provide xfer_partial target method Markus Metzger
@ 2013-12-19 19:13   ` Pedro Alves
  2013-12-20 13:37     ` Metzger, Markus T
  2013-12-19 19:26   ` Pedro Alves
  1 sibling, 1 reply; 66+ messages in thread
From: Pedro Alves @ 2013-12-19 19:13 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/19/2013 04:44 PM, Markus Metzger wrote:
> +
> +	    /* We do not allow writing memory in general.  */
> +	    if (writebuf != NULL)
> +	      throw_error (NOT_AVAILABLE_ERROR,
> +			   _("This record target does not record memory."));

So printing globals errors out?

I thought you were going to return TARGET_XFER_E_UNAVAILABLE
anyway?  Did you try making raw_memory_xfer_partial return
immediately if ops->to_xfer_partial returns TARGET_XFER_E_UNAVAILABLE,
instead of proceeding to query the target beneath?

-- 
Pedro Alves

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

* Re: [PATCH v9 21/29] record-btrace: provide xfer_partial target method
  2013-12-19 16:45 ` [PATCH v9 21/29] record-btrace: provide xfer_partial target method Markus Metzger
  2013-12-19 19:13   ` Pedro Alves
@ 2013-12-19 19:26   ` Pedro Alves
  2013-12-20 13:32     ` Metzger, Markus T
  1 sibling, 1 reply; 66+ messages in thread
From: Pedro Alves @ 2013-12-19 19:26 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/19/2013 04:44 PM, Markus Metzger wrote:

Also,

> +  /* 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:
> +	case TARGET_OBJECT_RAW_MEMORY:
> +	case TARGET_OBJECT_STACK_MEMORY:
> +	  {

I don't think you answered my comment about this in the previous
series.  I think you can't ever see TARGET_OBJECT_RAW_MEMORY or
TARGET_OBJECT_STACK_MEMORY here.

-- 
Pedro Alves

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

* Re: [PATCH v9 23/29] record-btrace: provide target_find_new_threads method
  2013-12-19 16:45 ` [PATCH v9 23/29] record-btrace: provide target_find_new_threads method Markus Metzger
@ 2013-12-19 19:32   ` Pedro Alves
  0 siblings, 0 replies; 66+ messages in thread
From: Pedro Alves @ 2013-12-19 19:32 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/19/2013 04:44 PM, Markus Metzger wrote:
> 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.

I think for completeness you should also provide a to_thread_alive
method.  These two go hand in hand.

> 
> 2013-12-19  Markus Metzger  <markus.t.metzger@intel.com>
> 
> 	* record-btrace.c (record_btrace_find_new_threads): New.
> 	(init_record_btrace_ops): Initialize to_find_new_threads.
> 
> 
> ---
>  gdb/record-btrace.c | 19 +++++++++++++++++++
>  1 file changed, 19 insertions(+)
> 
> diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
> index bb140ce..f6272b4 100644
> --- a/gdb/record-btrace.c
> +++ b/gdb/record-btrace.c
> @@ -1055,6 +1055,24 @@ record_btrace_wait (struct target_ops *ops, ptid_t ptid,
>    error (_("You can't do this from here.  Do 'record goto end', first."));
>  }
>  
> +/* The to_find_new_threads method of target record-btrace.  */
> +
> +static void
> +record_btrace_find_new_threads (struct target_ops *ops)
> +{
> +  /* Don't expect new threads if we're replaying.  */
> +  if (record_btrace_is_replaying ())
> +    return;
> +
> +  /* Forward the request.  */
> +  for (ops = ops->beneath; ops != NULL; ops = ops->beneath)
> +    if (ops->to_find_new_threads != NULL)
> +      {
> +	ops->to_find_new_threads (ops);
> +	break;
> +      }
> +}
> +
>  /* Initialize the record-btrace target ops.  */
>  
>  static void
> @@ -1091,6 +1109,7 @@ init_record_btrace_ops (void)
>    ops->to_get_unwinder = &record_btrace_frame_unwind;
>    ops->to_resume = record_btrace_resume;
>    ops->to_wait = record_btrace_wait;
> +  ops->to_find_new_threads = record_btrace_find_new_threads;
>    ops->to_stratum = record_stratum;
>    ops->to_magic = OPS_MAGIC;
>  }
> 


-- 
Pedro Alves

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

* Re: [PATCH v9 28/29] target: allow decr_pc_after_break to be defined by the target
  2013-12-19 16:45 ` [PATCH v9 28/29] target: allow decr_pc_after_break to be defined by the target Markus Metzger
@ 2013-12-19 19:51   ` Pedro Alves
  0 siblings, 0 replies; 66+ messages in thread
From: Pedro Alves @ 2013-12-19 19:51 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, palves, gdb-patches

On 12/19/2013 04:44 PM, Markus Metzger wrote:
> 
> 2013-12-19  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.

OK.

-- 
Pedro Alves

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

* Re: [PATCH v9 29/29] record-btrace: add (reverse-)stepping support
  2013-12-19 16:45 ` [PATCH v9 29/29] record-btrace: add (reverse-)stepping support Markus Metzger
  2013-12-19 17:31   ` Eli Zaretskii
@ 2013-12-19 20:10   ` Pedro Alves
  2013-12-20 14:37     ` Metzger, Markus T
  1 sibling, 1 reply; 66+ messages in thread
From: Pedro Alves @ 2013-12-19 20:10 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, palves, gdb-patches, Eli Zaretskii

On 12/19/2013 04:44 PM, Markus Metzger wrote:

> +  if (non_stop)
> +    error (_("Record btrace can't debug inferior in non-stop mode "
> +	     "(non-stop)."));

What's the intent of saying non-stop twice, in:

 "in non-stop mode (non-stop)"

?

> +  /* Stop all other threads. */
> +  if (!non_stop)
> +    ALL_THREADS (other)
> +      other->btrace.flags &= ~BTHR_MOVE;

(I know it doesn't work currently), but in non-stop, the
event thread should also get its BTHR_MOVE flag cleared.
I didn't spot where that was being done.

> +  /* GDB seems to need this.  Without, a stale PC seems to be used resulting in
> +     the current location to be displayed incorrectly.  */
> +  registers_changed_ptid (tp->ptid);

This really shouldn't be necessary, given target_resume does
it for you.  If you still needed, you're papering over some
problem.

-- 
Pedro Alves

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

* Re: [PATCH v9 04/29] gdbarch: add instruction predicate methods
  2013-12-19 16:45 ` [PATCH v9 04/29] gdbarch: add instruction predicate methods Markus Metzger
@ 2013-12-19 20:19   ` Pedro Alves
  0 siblings, 0 replies; 66+ messages in thread
From: Pedro Alves @ 2013-12-19 20:19 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/19/2013 04:44 PM, Markus Metzger wrote:
> 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.

Thanks, that's much better.  These hooks named as is sound
very x86 specific, but anyway, we can redo the interfaces
if/when other machines do branch tracing too.

-- 
Pedro Alves

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

* Re: [PATCH v9 05/29] frame:  add frame_is_tailcall function
  2013-12-19 16:45 ` [PATCH v9 05/29] frame: add frame_is_tailcall function Markus Metzger
@ 2013-12-19 20:19   ` Pedro Alves
  0 siblings, 0 replies; 66+ messages in thread
From: Pedro Alves @ 2013-12-19 20:19 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/19/2013 04:44 PM, Markus Metzger wrote:
> Currently, gdb is looking at the frame type when trying to identify
> tailcall frames.  This requires touching several places when adding
> a new tailcall frame type.
> 
> Add a predicate for tailcall frames.
> 
> This patch is no longer needed for the btrace series.  I leave it in
> since it might help someone in the future.  I'd also be OK to remove it.

Yes, please remove it.

Thanks,
-- 
Pedro Alves

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

* Re: [PATCH v9 00/29] record-btrace: reverse
  2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
                   ` (28 preceding siblings ...)
  2013-12-19 16:45 ` [PATCH v9 19/29] record-btrace, frame: supply target-specific unwinder Markus Metzger
@ 2013-12-19 20:23 ` Pedro Alves
  29 siblings, 0 replies; 66+ messages in thread
From: Pedro Alves @ 2013-12-19 20:23 UTC (permalink / raw)
  To: Markus Metzger; +Cc: jan.kratochvil, gdb-patches

On 12/19/2013 04:44 PM, Markus Metzger wrote:
> This is a bigger update addressing Pedro's feedback.

Thank you!  I read the series and sent a few comments.
Patches I didn't comment on looked fine to me.

(There were a couple things here and there I didn't
see addressed that I think we might be able to a little
cleaner around the unavailable stack frames handling, but
I didn't re-comment on those on purpose, in the interest
of moving forward.  Looking forward to have this in.)

-- 
Pedro Alves

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

* Re: [PATCH v9 14/29] target: add ops parameter to to_prepare_to_store method
  2013-12-19 18:30   ` Pedro Alves
@ 2013-12-19 21:13     ` Tom Tromey
  2013-12-20 13:07       ` Metzger, Markus T
  2013-12-20 17:23       ` Pedro Alves
  0 siblings, 2 replies; 66+ messages in thread
From: Tom Tromey @ 2013-12-19 21:13 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Markus Metzger, jan.kratochvil, gdb-patches

>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:

Pedro> There are more sites that need updating.  E.g., go32-nat.c,
Pedro> nto-procfs.c, windows-nat.c, possibly others.  Try grepping
Pedro> for to_prepare_to_store.
[...]
Pedro> There should be a noprocess() call after the loop here.

You can try this.  I don't know whether it is complete but it's
certainly more complete.

Tom

commit 1f51251857caf3522c45701ea2c47b4d43a4f60d
Author: Tom Tromey <tromey@redhat.com>
Date:   Tue Dec 17 21:28:22 2013 -0700

    Add target_ops argument to to_prepare_to_store
    
    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.

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 9cbb85f..fd7605d 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,25 @@
 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.
+
+2013-12-17  Tom Tromey  <tromey@redhat.com>
+
 	* target.h (struct target_ops) <to_post_attach>: Add argument.
 	(target_post_attach): Add argument.
 	* target.c (debug_to_post_attach): Add argument.
diff --git a/gdb/go32-nat.c b/gdb/go32-nat.c
index caf595c..ef6ceef 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 77853e8..de36417 100644
--- a/gdb/inf-child.c
+++ b/gdb/inf-child.c
@@ -100,7 +100,8 @@ inf_child_post_attach (struct target_ops *self, 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 70064ba..4292f47 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 bbc071f..308b398 100644
--- a/gdb/nto-procfs.c
+++ b/gdb/nto-procfs.c
@@ -1231,7 +1231,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 9ef265d..0d60277 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 dedb264..9e8ac1e 100644
--- a/gdb/record-full.c
+++ b/gdb/record-full.c
@@ -1969,7 +1969,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 43f3772..a57e76f 100644
--- a/gdb/remote-m32r-sdi.c
+++ b/gdb/remote-m32r-sdi.c
@@ -1013,7 +1013,7 @@ m32r_store_register (struct target_ops *ops,
    debugged.  */
 
 static void
-m32r_prepare_to_store (struct regcache *regcache)
+m32r_prepare_to_store (struct target_ops *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 9ba72bf..cefbda7 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 59ea2a1..9f1e5eb 100644
--- a/gdb/remote-sim.c
+++ b/gdb/remote-sim.c
@@ -81,7 +81,8 @@ static void gdbsim_close (struct target_ops *self);
 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 c9ce3fb..aaed11c 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -98,7 +98,8 @@ static void async_handle_remote_sigint_twice (int);
 
 static void remote_files_info (struct target_ops *ignore);
 
-static void remote_prepare_to_store (struct regcache *regcache);
+static void remote_prepare_to_store (struct target_ops *self,
+				     struct regcache *regcache);
 
 static void remote_open (char *name, int from_tty);
 
@@ -6415,7 +6416,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 db665a4..69f8c52 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -91,7 +91,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 *);
 
@@ -719,7 +720,7 @@ update_current_target (void)
 	    (void (*) (struct target_ops *, 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,
@@ -4383,9 +4384,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 a1b1e94..583fcd5 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -388,7 +388,7 @@ struct target_ops
     void (*to_fetch_registers) (struct target_ops *, struct regcache *, int);
     void (*to_store_registers) (struct target_ops *, struct regcache *, int)
       TARGET_DEFAULT (noprocess ());
-    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
@@ -1031,7 +1031,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 e2b3a57..3942f6f 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -2459,7 +2459,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.  */
 }

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

* RE: [PATCH v9 11/29] record-btrace: optionally indent function call history
  2013-12-19 18:23   ` Pedro Alves
@ 2013-12-20 12:54     ` Metzger, Markus T
  2013-12-20 16:47       ` Pedro Alves
  0 siblings, 1 reply; 66+ messages in thread
From: Metzger, Markus T @ 2013-12-20 12:54 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches

> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Thursday, December 19, 2013 7:24 PM
> To: Metzger, Markus T


> On 12/19/2013 04:44 PM, Markus Metzger wrote:
> > 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 ".
> 
> I was a little surprised the manual didn't get an update for this one,
> but I see an /i example is currently lacking.  Can one use both
> /i and /l at the same time ?

Yes.  Does this need to be documented explicitly?


> > Change the range syntax from "begin-end" to "begin,end" to allow
> copy&paste to
> > the "record instruction-history" and "list" commands.
> 
> (This bit seems to be missing in NEWS.  Not sure it was on purpose.)

I'll add it.


> > There is one known bug regarding indentation that results from the fact
> that we
> > have the current instruction already inside the branch trace.  When the
> current
> > instruction is the first (and only) instruction in a function on the outermost
> > level for which we have not seen the call, the indentation starts at level 1
> > with 2 leading spaces.
> 
> Hmm.  Why are we adding known bugs?  I'm not sure I understood it, but
> from
> your description it sounds like the condition should be detectable?

Yes, it is detectable.  You can't fix it up afterwards, though, so you need to
check this when computing the trace for each instruction.  I have not tried
to fix it because I think you wouldn't even notice it in practice.

The fix is relatively easy, so I added it and updated the tests.  Looking back, I
can't really say why I haven't just fixed it right away.

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

* RE: [PATCH v9 14/29] target: add ops parameter to to_prepare_to_store method
  2013-12-19 21:13     ` Tom Tromey
@ 2013-12-20 13:07       ` Metzger, Markus T
  2013-12-20 15:13         ` Tom Tromey
  2013-12-20 17:23       ` Pedro Alves
  1 sibling, 1 reply; 66+ messages in thread
From: Metzger, Markus T @ 2013-12-20 13:07 UTC (permalink / raw)
  To: Tom Tromey, Pedro Alves; +Cc: jan.kratochvil, gdb-patches

> -----Original Message-----
> From: Tom Tromey [mailto:tromey@redhat.com]
> Sent: Thursday, December 19, 2013 10:13 PM


> Pedro> There are more sites that need updating.  E.g., go32-nat.c,
> Pedro> nto-procfs.c, windows-nat.c, possibly others.  Try grepping
> Pedro> for to_prepare_to_store.
> [...]
> Pedro> There should be a noprocess() call after the loop here.
> 
> You can try this.  I don't know whether it is complete but it's
> certainly more complete.
> 
> Tom

Thanks, Tom.  I'll drop my patch and take yours.

Do you plan to commit this soon?

Regards,
Markus.


> commit 1f51251857caf3522c45701ea2c47b4d43a4f60d
> Author: Tom Tromey <tromey@redhat.com>
> Date:   Tue Dec 17 21:28:22 2013 -0700
> 
>     Add target_ops argument to to_prepare_to_store
> 
>     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.
> 
> diff --git a/gdb/ChangeLog b/gdb/ChangeLog
> index 9cbb85f..fd7605d 100644
> --- a/gdb/ChangeLog
> +++ b/gdb/ChangeLog
> @@ -1,5 +1,25 @@
>  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.
> +
> +2013-12-17  Tom Tromey  <tromey@redhat.com>
> +
>  	* target.h (struct target_ops) <to_post_attach>: Add argument.
>  	(target_post_attach): Add argument.
>  	* target.c (debug_to_post_attach): Add argument.
> diff --git a/gdb/go32-nat.c b/gdb/go32-nat.c
> index caf595c..ef6ceef 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 77853e8..de36417 100644
> --- a/gdb/inf-child.c
> +++ b/gdb/inf-child.c
> @@ -100,7 +100,8 @@ inf_child_post_attach (struct target_ops *self, 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 70064ba..4292f47 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 bbc071f..308b398 100644
> --- a/gdb/nto-procfs.c
> +++ b/gdb/nto-procfs.c
> @@ -1231,7 +1231,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 9ef265d..0d60277 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 dedb264..9e8ac1e 100644
> --- a/gdb/record-full.c
> +++ b/gdb/record-full.c
> @@ -1969,7 +1969,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 43f3772..a57e76f 100644
> --- a/gdb/remote-m32r-sdi.c
> +++ b/gdb/remote-m32r-sdi.c
> @@ -1013,7 +1013,7 @@ m32r_store_register (struct target_ops *ops,
>     debugged.  */
> 
>  static void
> -m32r_prepare_to_store (struct regcache *regcache)
> +m32r_prepare_to_store (struct target_ops *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 9ba72bf..cefbda7 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 59ea2a1..9f1e5eb 100644
> --- a/gdb/remote-sim.c
> +++ b/gdb/remote-sim.c
> @@ -81,7 +81,8 @@ static void gdbsim_close (struct target_ops *self);
>  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 c9ce3fb..aaed11c 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -98,7 +98,8 @@ static void async_handle_remote_sigint_twice (int);
> 
>  static void remote_files_info (struct target_ops *ignore);
> 
> -static void remote_prepare_to_store (struct regcache *regcache);
> +static void remote_prepare_to_store (struct target_ops *self,
> +				     struct regcache *regcache);
> 
>  static void remote_open (char *name, int from_tty);
> 
> @@ -6415,7 +6416,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 db665a4..69f8c52 100644
> --- a/gdb/target.c
> +++ b/gdb/target.c
> @@ -91,7 +91,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 *);
> 
> @@ -719,7 +720,7 @@ update_current_target (void)
>  	    (void (*) (struct target_ops *, 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,
> @@ -4383,9 +4384,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 a1b1e94..583fcd5 100644
> --- a/gdb/target.h
> +++ b/gdb/target.h
> @@ -388,7 +388,7 @@ struct target_ops
>      void (*to_fetch_registers) (struct target_ops *, struct regcache *, int);
>      void (*to_store_registers) (struct target_ops *, struct regcache *, int)
>        TARGET_DEFAULT (noprocess ());
> -    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
> @@ -1031,7 +1031,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 e2b3a57..3942f6f 100644
> --- a/gdb/windows-nat.c
> +++ b/gdb/windows-nat.c
> @@ -2459,7 +2459,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.  */
>  }
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] 66+ messages in thread

* RE: [PATCH v9 21/29] record-btrace: provide xfer_partial target method
  2013-12-19 19:26   ` Pedro Alves
@ 2013-12-20 13:32     ` Metzger, Markus T
  2013-12-20 15:36       ` Pedro Alves
  0 siblings, 1 reply; 66+ messages in thread
From: Metzger, Markus T @ 2013-12-20 13:32 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches

> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Thursday, December 19, 2013 8:27 PM
> To: Metzger, Markus T
> Cc: jan.kratochvil@redhat.com; gdb-patches@sourceware.org
> Subject: Re: [PATCH v9 21/29] record-btrace: provide xfer_partial target
> method
> 
> On 12/19/2013 04:44 PM, Markus Metzger wrote:
> 
> Also,
> 
> > +  /* 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:
> > +	case TARGET_OBJECT_RAW_MEMORY:
> > +	case TARGET_OBJECT_STACK_MEMORY:
> > +	  {
> 
> I don't think you answered my comment about this in the previous
> series.  I think you can't ever see TARGET_OBJECT_RAW_MEMORY or
> TARGET_OBJECT_STACK_MEMORY here.

If we ever end up seeing them, they will simply be forwarded to the
target beneath without check.   I think it's safer to leave them in.
If you'd rather have them removed, I can do that, as well.

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

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

* RE: [PATCH v9 21/29] record-btrace: provide xfer_partial target method
  2013-12-19 19:13   ` Pedro Alves
@ 2013-12-20 13:37     ` Metzger, Markus T
  2013-12-20 13:54       ` Metzger, Markus T
  0 siblings, 1 reply; 66+ messages in thread
From: Metzger, Markus T @ 2013-12-20 13:37 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches



> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Thursday, December 19, 2013 8:13 PM
> To: Metzger, Markus T
> Cc: jan.kratochvil@redhat.com; gdb-patches@sourceware.org
> Subject: Re: [PATCH v9 21/29] record-btrace: provide xfer_partial target
> method
> 
> On 12/19/2013 04:44 PM, Markus Metzger wrote:
> > +
> > +	    /* We do not allow writing memory in general.  */
> > +	    if (writebuf != NULL)
> > +	      throw_error (NOT_AVAILABLE_ERROR,
> > +			   _("This record target does not record memory."));
> 
> So printing globals errors out?
> 
> I thought you were going to return TARGET_XFER_E_UNAVAILABLE
> anyway?  Did you try making raw_memory_xfer_partial return
> immediately if ops->to_xfer_partial returns TARGET_XFER_E_UNAVAILABLE,
> instead of proceeding to query the target beneath?

I thought you were OK with the error.  Here's our discussion:

>>>> I now get an error when trying to access a variable with static storage
>>>> duration or when trying to access memory directly via its address.
>>>> It would be nice to also get an <unavailable> in those cases.  This would
>>>> require the respective layer to catch my exception.
>>>
>>> Please try returning TARGET_XFER_E_UNAVAILABLE instead.
>> 
>> That is ignored just like the -1 I returned earlier.  I nevertheless changed
>> the default return to that since it is more descriptive.
>
>Thanks.  Hmm, yes, looks like raw_memory_xfer_partial carries on
>looking at the target beneath, and then when that fails, we'll
>lose TARGET_XFER_E_UNAVAILABLE, and return TARGET_XFER_E_IO/-1,
>losing the better TARGET_XFER_E_UNAVAILABLE.

I can try to make raw_memorx_xfer_partial return on
TARGET_XFER_E_UNAVAILABLE.  This will be more intrusive, though.

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

* RE: [PATCH v9 21/29] record-btrace: provide xfer_partial target method
  2013-12-20 13:37     ` Metzger, Markus T
@ 2013-12-20 13:54       ` Metzger, Markus T
  2013-12-20 15:56         ` Pedro Alves
  0 siblings, 1 reply; 66+ messages in thread
From: Metzger, Markus T @ 2013-12-20 13:54 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches

> -----Original Message-----
> From: Metzger, Markus T
> Sent: Friday, December 20, 2013 2:37 PM
> To: Pedro Alves


> I can try to make raw_memorx_xfer_partial return on
> TARGET_XFER_E_UNAVAILABLE.  This will be more intrusive, though.

I did not get any regressions on 64bit IA Linux native.
And the error message is definitely nicer.

Should we add a new '-3' return value to be on the safe side?
I.e. raw_memory_xfer_partial would bail out on -3 and keep
going for -1 and -2.

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

* RE: [PATCH v9 29/29] record-btrace: add (reverse-)stepping support
  2013-12-19 20:10   ` Pedro Alves
@ 2013-12-20 14:37     ` Metzger, Markus T
  2013-12-20 14:47       ` Metzger, Markus T
  2013-12-20 16:07       ` Pedro Alves
  0 siblings, 2 replies; 66+ messages in thread
From: Metzger, Markus T @ 2013-12-20 14:37 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches, Eli Zaretskii

> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Thursday, December 19, 2013 9:10 PM


> > +  if (non_stop)
> > +    error (_("Record btrace can't debug inferior in non-stop mode "
> > +	     "(non-stop)."));
> 
> What's the intent of saying non-stop twice, in:
> 
>  "in non-stop mode (non-stop)"

I took this from s/w record without thinking.  Fixed.


> > +  /* Stop all other threads. */
> > +  if (!non_stop)
> > +    ALL_THREADS (other)
> > +      other->btrace.flags &= ~BTHR_MOVE;
> 
> (I know it doesn't work currently), but in non-stop, the
> event thread should also get its BTHR_MOVE flag cleared.
> I didn't spot where that was being done.

It's done in record_btrace_step_thread right at the beginning.


> > +  /* GDB seems to need this.  Without, a stale PC seems to be used
> resulting in
> > +     the current location to be displayed incorrectly.  */
> > +  registers_changed_ptid (tp->ptid);
> 
> This really shouldn't be necessary, given target_resume does
> it for you.  If you still needed, you're papering over some
> problem.

If we start replaying in to_wait, we'll call get_current_frame
to fix up some stepping related frames.  This will be done on
the current PC.

When we step later on in record_btrace_step_thread, we change
the replay position but not the PC.

I guess it will be more clear when I move this into
record_btrace_step_thread and change the comment.

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

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

* RE: [PATCH v9 29/29] record-btrace: add (reverse-)stepping support
  2013-12-20 14:37     ` Metzger, Markus T
@ 2013-12-20 14:47       ` Metzger, Markus T
  2013-12-20 16:31         ` Pedro Alves
  2013-12-20 16:07       ` Pedro Alves
  1 sibling, 1 reply; 66+ messages in thread
From: Metzger, Markus T @ 2013-12-20 14:47 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches, Eli Zaretskii

> -----Original Message-----
> From: Metzger, Markus T
> Sent: Friday, December 20, 2013 3:37 PM
> To: Pedro Alves


> > This really shouldn't be necessary, given target_resume does
> > it for you.  If you still needed, you're papering over some
> > problem.
> 
> If we start replaying in to_wait, we'll call get_current_frame
> to fix up some stepping related frames.  This will be done on
> the current PC.
> 
> When we step later on in record_btrace_step_thread, we change
> the replay position but not the PC.
> 
> I guess it will be more clear when I move this into
> record_btrace_step_thread and change the comment.

I moved it to record_btrace_start_replaying:

@@ -1406,15 +1405,15 @@ record_btrace_start_replaying (struct thread_info *tp)
   /* Restore the previous execution state.  */
   set_executing (tp->ptid, executing);
 
+  /* Invalidate registers again.  If this is called on the to_wait path,
+     we expect registers still invalid from to_resume.  */
+  registers_changed_ptid (tp->ptid);
+
   if (except.reason < 0)
     {
       xfree (btinfo->replay);
       btinfo->replay = NULL;
 
-      /* Avoid stale frames based on branch trace unwinding.  */
-      registers_changed_ptid (tp->ptid);
-      get_current_frame ();
-
       throw_exception (except);
     }

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

* Re: [PATCH v9 14/29] target: add ops parameter to to_prepare_to_store method
  2013-12-20 13:07       ` Metzger, Markus T
@ 2013-12-20 15:13         ` Tom Tromey
  0 siblings, 0 replies; 66+ messages in thread
From: Tom Tromey @ 2013-12-20 15:13 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: Pedro Alves, jan.kratochvil, gdb-patches

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

Markus> Do you plan to commit this soon?

I wasn't planning to, since it's part of my big target API update
series.  However it's fine if you push it when your series is approved.

Tom

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

* Re: [PATCH v9 21/29] record-btrace: provide xfer_partial target method
  2013-12-20 13:32     ` Metzger, Markus T
@ 2013-12-20 15:36       ` Pedro Alves
  0 siblings, 0 replies; 66+ messages in thread
From: Pedro Alves @ 2013-12-20 15:36 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches

On 12/20/2013 01:30 PM, Metzger, Markus T wrote:
>> -----Original Message-----
>> From: Pedro Alves [mailto:palves@redhat.com]
>> Sent: Thursday, December 19, 2013 8:27 PM
>> To: Metzger, Markus T
>> Cc: jan.kratochvil@redhat.com; gdb-patches@sourceware.org
>> Subject: Re: [PATCH v9 21/29] record-btrace: provide xfer_partial target
>> method
>>
>> On 12/19/2013 04:44 PM, Markus Metzger wrote:
>>
>> Also,
>>
>>> +  /* 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:
>>> +	case TARGET_OBJECT_RAW_MEMORY:
>>> +	case TARGET_OBJECT_STACK_MEMORY:
>>> +	  {
>>
>> I don't think you answered my comment about this in the previous
>> series.  I think you can't ever see TARGET_OBJECT_RAW_MEMORY or
>> TARGET_OBJECT_STACK_MEMORY here.
> 
> If we ever end up seeing them, they will simply be forwarded to the
> target beneath without check.   I think it's safer to leave them in.
> If you'd rather have them removed, I can do that, as well.

Please remove them.  (Note how you'd already be missing
TARGET_OBJECT_CODE_MEMORY.)  If targets ever saw those, we'd need
to update _all_ target's xfer_partial implementations.  I see a
reference to TARGET_OBJECT_RAW_MEMORY somehow ended up in
record-full.c.  We should remove that.  As long as targets shouldn't
see those, random references around the codebase just add to confusion
(the reader needs to wonder why are those only handled in some
targets, for no good reason).

-- 
Pedro Alves

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

* Re: [PATCH v9 21/29] record-btrace: provide xfer_partial target method
  2013-12-20 13:54       ` Metzger, Markus T
@ 2013-12-20 15:56         ` Pedro Alves
  2013-12-20 16:02           ` Pedro Alves
  2014-01-13 12:36           ` Metzger, Markus T
  0 siblings, 2 replies; 66+ messages in thread
From: Pedro Alves @ 2013-12-20 15:56 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches

On 12/20/2013 01:54 PM, Metzger, Markus T wrote:
>> -----Original Message-----
>> From: Metzger, Markus T
>> Sent: Friday, December 20, 2013 2:37 PM
>> To: Pedro Alves
> 
> 
>> I can try to make raw_memorx_xfer_partial return on
>> TARGET_XFER_E_UNAVAILABLE.  This will be more intrusive, though.
> 
> I did not get any regressions on 64bit IA Linux native.
> And the error message is definitely nicer.

Can you show the patchlet you did, and the before/after gdb
output though?  I'd be better to see that, and have any possible
follow up discussion discussion here now, rather than fractured
to a potential repost of the series.

BTW, I think that ...

+  /* 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:
+	case TARGET_OBJECT_RAW_MEMORY:
+	case TARGET_OBJECT_STACK_MEMORY:
+	  {
+	    struct target_section *section;
+
+	    /* We do not allow writing memory in general.  */
+	    if (writebuf != NULL)
+	      throw_error (NOT_AVAILABLE_ERROR,
+			   _("This record target does not record memory."));
+
+	    /* 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;

... here, this should return TARGET_XFER_E_IO rather than break and
falling through to the code that defers to the target beneath, as the
caller will already do that for memory transfers.

+		  }
+	      }
+
+	    throw_error (NOT_AVAILABLE_ERROR,
+			 _("This record target does not record memory."));

Then here return TARGET_XFER_E_UNAVAILABLE.

+	  }
+	}
+    }

> Should we add a new '-3' return value to be on the safe side?
> I.e. raw_memory_xfer_partial would bail out on -3 and keep
> going for -1 and -2.

I'm not seeing why we'd need that.  TARGET_XFER_E_UNAVAILABLE
is exactly designed for this use case.  It's just that
it was added recently, and we hadn't found a use outside
tracing yet.

-- 
Pedro Alves

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

* Re: [PATCH v9 21/29] record-btrace: provide xfer_partial target method
  2013-12-20 15:56         ` Pedro Alves
@ 2013-12-20 16:02           ` Pedro Alves
  2014-01-13 17:11             ` Pedro Alves
  2014-01-13 12:36           ` Metzger, Markus T
  1 sibling, 1 reply; 66+ messages in thread
From: Pedro Alves @ 2013-12-20 16:02 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches

On 12/20/2013 03:55 PM, Pedro Alves wrote:
> +		/* 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;
> 
> ... here, this should return TARGET_XFER_E_IO rather than break and
> falling through to the code that defers to the target beneath, as the
> caller will already do that for memory transfers.

Ah, no, ignore that.  You need to truncate len, so it wouldn't work
correctly, of course.  Silly me.

-- 
Pedro Alves

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

* Re: [PATCH v9 29/29] record-btrace: add (reverse-)stepping support
  2013-12-20 14:37     ` Metzger, Markus T
  2013-12-20 14:47       ` Metzger, Markus T
@ 2013-12-20 16:07       ` Pedro Alves
  1 sibling, 0 replies; 66+ messages in thread
From: Pedro Alves @ 2013-12-20 16:07 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches, Eli Zaretskii

On 12/20/2013 02:36 PM, Metzger, Markus T wrote:
>> -----Original Message-----
>> From: Pedro Alves [mailto:palves@redhat.com]
>> Sent: Thursday, December 19, 2013 9:10 PM
> 
> 
>>> +  if (non_stop)
>>> +    error (_("Record btrace can't debug inferior in non-stop mode "
>>> +	     "(non-stop)."));
>>
>> What's the intent of saying non-stop twice, in:
>>
>>  "in non-stop mode (non-stop)"
> 
> I took this from s/w record without thinking.  Fixed.

Ah, indeed I see it there now...

>>> +  /* Stop all other threads. */
>>> +  if (!non_stop)
>>> +    ALL_THREADS (other)
>>> +      other->btrace.flags &= ~BTHR_MOVE;
>>
>> (I know it doesn't work currently), but in non-stop, the
>> event thread should also get its BTHR_MOVE flag cleared.
>> I didn't spot where that was being done.
> 
> It's done in record_btrace_step_thread right at the beginning.



> 
> 
>>> +  /* GDB seems to need this.  Without, a stale PC seems to be used
>> resulting in
>>> +     the current location to be displayed incorrectly.  */
>>> +  registers_changed_ptid (tp->ptid);
>>
>> This really shouldn't be necessary, given target_resume does
>> it for you.  If you still needed, you're papering over some
>> problem.
> 
> If we start replaying in to_wait, we'll call get_current_frame
> to fix up some stepping related frames.  This will be done on
> the current PC.
> 
> When we step later on in record_btrace_step_thread, we change
> the replay position but not the PC.

Alright, that's the place to flush it then.  It's just like
the registers_changed calls in linux-nat.c, whenever the
target resumes the thread behind the core's back:

      registers_changed ();
      if (linux_nat_prepare_to_resume != NULL)
	linux_nat_prepare_to_resume (lp);
      linux_ops->to_resume (linux_ops, pid_to_ptid (ptid_get_lwp (lp->ptid)),
			    lp->step, GDB_SIGNAL_0);

etc.

> 
> I guess it will be more clear when I move this into
> record_btrace_step_thread and change the comment.

Thanks.

-- 
Pedro Alves

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

* Re: [PATCH v9 29/29] record-btrace: add (reverse-)stepping support
  2013-12-20 14:47       ` Metzger, Markus T
@ 2013-12-20 16:31         ` Pedro Alves
  0 siblings, 0 replies; 66+ messages in thread
From: Pedro Alves @ 2013-12-20 16:31 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches, Eli Zaretskii

On 12/20/2013 02:47 PM, Metzger, Markus T wrote:
>> -----Original Message-----
>> From: Metzger, Markus T
>> Sent: Friday, December 20, 2013 3:37 PM
>> To: Pedro Alves
> 
> 
>>> This really shouldn't be necessary, given target_resume does
>>> it for you.  If you still needed, you're papering over some
>>> problem.
>>
>> If we start replaying in to_wait, we'll call get_current_frame
>> to fix up some stepping related frames.  This will be done on
>> the current PC.
>>
>> When we step later on in record_btrace_step_thread, we change
>> the replay position but not the PC.
>>
>> I guess it will be more clear when I move this into
>> record_btrace_step_thread and change the comment.
> 
> I moved it to record_btrace_start_replaying:

Hmm.

> 
> @@ -1406,15 +1405,15 @@ record_btrace_start_replaying (struct thread_info *tp)
>    /* Restore the previous execution state.  */
>    set_executing (tp->ptid, executing);
>  
> +  /* Invalidate registers again.  If this is called on the to_wait path,
> +     we expect registers still invalid from to_resume.  */
> +  registers_changed_ptid (tp->ptid);
> +

The registers haven't really changed at this point.
It seems like it that may help because it just happens that today,
nothing is currently reading registers before you move the
thread to the next position.  If something does that, you'll get
stale results again.  It'd be safer to do this right after
moving the thread to the next trace position.  So with that
in mind, either indeed put it in record_btrace_step_thread or
just leave it at the place the original code had it already,
but update the comment to sound more determined, something like:

  /* We just moved the thread to a different trace position.
     If anything fetched the thread's registers before, its
     regcache is stale now.  */
  registers_changed_ptid (tp->ptid);

Thanks,
-- 
Pedro Alves

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

* Re: [PATCH v9 11/29] record-btrace: optionally indent function call history
  2013-12-20 12:54     ` Metzger, Markus T
@ 2013-12-20 16:47       ` Pedro Alves
  0 siblings, 0 replies; 66+ messages in thread
From: Pedro Alves @ 2013-12-20 16:47 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches

On 12/20/2013 12:53 PM, Metzger, Markus T wrote:
>> On 12/19/2013 04:44 PM, Markus Metzger wrote:
>>> 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 ".
>>
>> I was a little surprised the manual didn't get an update for this one,
>> but I see an /i example is currently lacking.  Can one use both
>> /i and /l at the same time ?
> 
> Yes.  Does this need to be documented explicitly?

I think it'd be nice.

>>> There is one known bug regarding indentation that results from the fact
>> that we
>>> have the current instruction already inside the branch trace.  When the
>> current
>>> instruction is the first (and only) instruction in a function on the outermost
>>> level for which we have not seen the call, the indentation starts at level 1
>>> with 2 leading spaces.
>>
>> Hmm.  Why are we adding known bugs?  I'm not sure I understood it, but
>> from
>> your description it sounds like the condition should be detectable?
> 
> Yes, it is detectable.  You can't fix it up afterwards, though, so you need to
> check this when computing the trace for each instruction.  I have not tried
> to fix it because I think you wouldn't even notice it in practice.
>
> The fix is relatively easy, so I added it and updated the tests.  Looking back, I
> can't really say why I haven't just fixed it right away.

Alright, sounds great.  Thanks for going the extra mile.

-- 
Pedro Alves

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

* Re: [PATCH v9 14/29] target: add ops parameter to to_prepare_to_store method
  2013-12-19 21:13     ` Tom Tromey
  2013-12-20 13:07       ` Metzger, Markus T
@ 2013-12-20 17:23       ` Pedro Alves
  1 sibling, 0 replies; 66+ messages in thread
From: Pedro Alves @ 2013-12-20 17:23 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Pedro Alves, Markus Metzger, jan.kratochvil, gdb-patches

On 12/19/2013 09:13 PM, Tom Tromey wrote:
>>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:
> 
> Pedro> There are more sites that need updating.  E.g., go32-nat.c,
> Pedro> nto-procfs.c, windows-nat.c, possibly others.  Try grepping
> Pedro> for to_prepare_to_store.
> [...]
> Pedro> There should be a noprocess() call after the loop here.
> 
> You can try this.  I don't know whether it is complete but it's
> certainly more complete.

FAOD, it looks fine to me.

Thanks,
-- 
Pedro Alves

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

* RE: [PATCH v9 21/29] record-btrace: provide xfer_partial target method
  2013-12-20 15:56         ` Pedro Alves
  2013-12-20 16:02           ` Pedro Alves
@ 2014-01-13 12:36           ` Metzger, Markus T
  2014-01-13 16:47             ` Pedro Alves
  1 sibling, 1 reply; 66+ messages in thread
From: Metzger, Markus T @ 2014-01-13 12:36 UTC (permalink / raw)
  To: Pedro Alves; +Cc: jan.kratochvil, gdb-patches

> -----Original Message-----
> From: Pedro Alves [mailto:palves@redhat.com]
> Sent: Friday, December 20, 2013 4:56 PM


> Can you show the patchlet you did, and the before/after gdb
> output though?  I'd be better to see that, and have any possible
> follow up discussion discussion here now, rather than fractured
> to a potential repost of the series.

Here's the current version of the function:

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;
}


And here's the new GDB output:

(gdb) print glob
Memory at address 0x601030 unavailable.

This used to be:

(gdb) print glob
This record target does not record memory.


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

* Re: [PATCH v9 21/29] record-btrace: provide xfer_partial target method
  2014-01-13 12:36           ` Metzger, Markus T
@ 2014-01-13 16:47             ` Pedro Alves
  0 siblings, 0 replies; 66+ messages in thread
From: Pedro Alves @ 2014-01-13 16:47 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: jan.kratochvil, gdb-patches

On 01/13/2014 12:36 PM, Metzger, Markus T wrote:
>> -----Original Message-----
>> From: Pedro Alves [mailto:palves@redhat.com]
>> Sent: Friday, December 20, 2013 4:56 PM
> 
> 
>> Can you show the patchlet you did, and the before/after gdb
>> output though?  I'd be better to see that, and have any possible
>> follow up discussion discussion here now, rather than fractured
>> to a potential repost of the series.
> 
> Here's the current version of the function:
> 
> 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;
> }
> 
> 
> And here's the new GDB output:
> 
> (gdb) print glob
> Memory at address 0x601030 unavailable.
> 
> This used to be:
> 
> (gdb) print glob
> This record target does not record memory.

Alright, thanks.  Once Yao is done with changing
the target_xfer_partial interface to return a read length
in an out parameter in addition to TARGET_XFER_E_UNAVAILABLE
we'll be able to make that graceful instead of an error, like:

(gdb) print glob
$1 = <unavailable>

Just like when debugging traceframes.  For now, it'll do.

-- 
Pedro Alves

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

* Re: [PATCH v9 21/29] record-btrace: provide xfer_partial target method
  2013-12-20 16:02           ` Pedro Alves
@ 2014-01-13 17:11             ` Pedro Alves
  0 siblings, 0 replies; 66+ messages in thread
From: Pedro Alves @ 2014-01-13 17:11 UTC (permalink / raw)
  To: gdb-patches; +Cc: Metzger, Markus T, jan.kratochvil

On 12/20/2013 04:02 PM, Pedro Alves wrote:
> On 12/20/2013 03:55 PM, Pedro Alves wrote:
>> +		/* 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;
>>
>> ... here, this should return TARGET_XFER_E_IO rather than break and
>> falling through to the code that defers to the target beneath, as the
>> caller will already do that for memory transfers.
> 
> Ah, no, ignore that.  You need to truncate len, so it wouldn't work
> correctly, of course.  Silly me.

BTW, for the record, after sending that, I started thinking that it
might be better/simpler to move this
falling-through-to-the-target-beneath to the targets even for
memory (like in your target), instead of having the core code
do it.  That is, remove the loop from raw_memory_xfer_partial.
I think all reasons that might require that memory be handled
differently have disappeared over the years (like., e.g.,
TARGET_OBJECT_RAW_MEMORY now traverses the stack too).
Having all target objects behave the same seems better by not
having special cases.  Though I guess only actually trying it
would tell better.

-- 
Pedro Alves

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

end of thread, other threads:[~2014-01-13 17:11 UTC | newest]

Thread overview: 66+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-12-19 16:45 [PATCH v9 00/29] record-btrace: reverse Markus Metzger
2013-12-19 16:45 ` [PATCH v9 22/29] record-btrace: add to_wait and to_resume target methods Markus Metzger
2013-12-19 16:45 ` [PATCH v9 25/29] record-btrace: extend unwinder Markus Metzger
2013-12-19 16:45 ` [PATCH v9 10/29] btrace: increase buffer size Markus Metzger
2013-12-19 16:45 ` [PATCH v9 24/29] record-btrace: add record goto target methods Markus Metzger
2013-12-19 16:45 ` [PATCH v9 29/29] record-btrace: add (reverse-)stepping support Markus Metzger
2013-12-19 17:31   ` Eli Zaretskii
2013-12-19 20:10   ` Pedro Alves
2013-12-20 14:37     ` Metzger, Markus T
2013-12-20 14:47       ` Metzger, Markus T
2013-12-20 16:31         ` Pedro Alves
2013-12-20 16:07       ` Pedro Alves
2013-12-19 16:45 ` [PATCH v9 13/29] btrace: add replay position to btrace thread info Markus Metzger
2013-12-19 16:45 ` [PATCH v9 23/29] record-btrace: provide target_find_new_threads method Markus Metzger
2013-12-19 19:32   ` Pedro Alves
2013-12-19 16:45 ` [PATCH v9 16/29] frame, backtrace: allow targets to supply a frame unwinder Markus Metzger
2013-12-19 18:41   ` Pedro Alves
2013-12-19 16:45 ` [PATCH v9 08/29] record-btrace: fix insn range in function call history Markus Metzger
2013-12-19 16:45 ` [PATCH v9 11/29] record-btrace: optionally indent " Markus Metzger
2013-12-19 18:23   ` Pedro Alves
2013-12-20 12:54     ` Metzger, Markus T
2013-12-20 16:47       ` Pedro Alves
2013-12-19 16:45 ` [PATCH v9 12/29] record-btrace: make ranges include begin and end Markus Metzger
2013-12-19 17:29   ` Eli Zaretskii
2013-12-19 16:45 ` [PATCH v9 09/29] record-btrace: start counting at one Markus Metzger
2013-12-19 17:27   ` Eli Zaretskii
2013-12-19 16:45 ` [PATCH v9 04/29] gdbarch: add instruction predicate methods Markus Metzger
2013-12-19 20:19   ` Pedro Alves
2013-12-19 16:45 ` [PATCH v9 21/29] record-btrace: provide xfer_partial target method Markus Metzger
2013-12-19 19:13   ` Pedro Alves
2013-12-20 13:37     ` Metzger, Markus T
2013-12-20 13:54       ` Metzger, Markus T
2013-12-20 15:56         ` Pedro Alves
2013-12-20 16:02           ` Pedro Alves
2014-01-13 17:11             ` Pedro Alves
2014-01-13 12:36           ` Metzger, Markus T
2014-01-13 16:47             ` Pedro Alves
2013-12-19 19:26   ` Pedro Alves
2013-12-20 13:32     ` Metzger, Markus T
2013-12-20 15:36       ` Pedro Alves
2013-12-19 16:45 ` [PATCH v9 28/29] target: allow decr_pc_after_break to be defined by the target Markus Metzger
2013-12-19 19:51   ` Pedro Alves
2013-12-19 16:45 ` [PATCH v9 03/29] btrace: uppercase btrace_read_type Markus Metzger
2013-12-19 16:45 ` [PATCH v9 27/29] record-btrace: show trace from enable location Markus Metzger
2013-12-19 16:45 ` [PATCH v9 01/29] btrace, test: fix multi-line btrace tests Markus Metzger
2013-12-19 18:01   ` Pedro Alves
2013-12-19 16:45 ` [PATCH v9 18/29] frame: do not assume unwinding will succeed Markus Metzger
2013-12-19 16:45 ` [PATCH v9 05/29] frame: add frame_is_tailcall function Markus Metzger
2013-12-19 20:19   ` Pedro Alves
2013-12-19 16:45 ` [PATCH v9 26/29] btrace, gdbserver: read branch trace incrementally Markus Metzger
2013-12-19 16:45 ` [PATCH v9 15/29] record-btrace: supply register target methods Markus Metzger
2013-12-19 16:45 ` [PATCH v9 20/29] target, breakpoint: allow insert/remove breakpoint to be forwarded Markus Metzger
2013-12-19 19:08   ` Pedro Alves
2013-12-19 16:45 ` [PATCH v9 07/29] btrace: change branch trace data structure Markus Metzger
2013-12-19 16:45 ` [PATCH v9 14/29] target: add ops parameter to to_prepare_to_store method Markus Metzger
2013-12-19 18:30   ` Pedro Alves
2013-12-19 21:13     ` Tom Tromey
2013-12-20 13:07       ` Metzger, Markus T
2013-12-20 15:13         ` Tom Tromey
2013-12-20 17:23       ` Pedro Alves
2013-12-19 16:45 ` [PATCH v9 06/29] frame: add frame_id_build_unavailable_stack_special Markus Metzger
2013-12-19 18:12   ` Pedro Alves
2013-12-19 16:45 ` [PATCH v9 02/29] btrace, linux: fix memory leak when reading branch trace Markus Metzger
2013-12-19 16:45 ` [PATCH v9 17/29] frame, cfa: check unwind stop reason first Markus Metzger
2013-12-19 16:45 ` [PATCH v9 19/29] record-btrace, frame: supply target-specific unwinder Markus Metzger
2013-12-19 20:23 ` [PATCH v9 00/29] record-btrace: reverse Pedro Alves

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