public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v3 4/8] Add record_start function.
  2016-11-21 15:49 [PATCH v3 0/8] Python bindings for btrace recordings Tim Wiederhake
                   ` (6 preceding siblings ...)
  2016-11-21 15:49 ` [PATCH v3 5/8] python: Create Python bindings for record history Tim Wiederhake
@ 2016-11-21 15:49 ` Tim Wiederhake
  2016-11-29 15:47   ` Metzger, Markus T
  2016-11-29 15:49 ` [PATCH v3 0/8] Python bindings for btrace recordings Metzger, Markus T
  8 siblings, 1 reply; 20+ messages in thread
From: Tim Wiederhake @ 2016-11-21 15:49 UTC (permalink / raw)
  To: gdb-patches; +Cc: palves, markus.t.metzger

2016-11-21  Tim Wiederhake  <tim.wiederhake@intel.com>

gdb/ChangeLog

	* record.h (record_start): New export.
	* record.c (record_start): New function.


---
 gdb/record.c | 28 ++++++++++++++++++++++++++++
 gdb/record.h |  5 +++++
 2 files changed, 33 insertions(+)

diff --git a/gdb/record.c b/gdb/record.c
index 34ebd1b..6df8ba1 100644
--- a/gdb/record.c
+++ b/gdb/record.c
@@ -93,6 +93,34 @@ record_preopen (void)
 
 /* See record.h.  */
 
+void
+record_start (const char *method, const char *format, int from_tty)
+{
+  if (method == NULL)
+    {
+      if (format == NULL)
+	return execute_command ("record", from_tty);
+    }
+  else if (strcmp (method, "full") == 0)
+    {
+      if (format == NULL)
+	return execute_command ("record full", from_tty);
+    }
+  else if (strcmp (method, "btrace") == 0)
+    {
+      if (format == NULL)
+	return execute_command ("record btrace", from_tty);
+      if (strcmp (format, "bts") == 0)
+	return execute_command ("record btrace bts", from_tty);
+      if (strcmp (format, "pt") == 0)
+	return execute_command ("record btrace pt", from_tty);
+    }
+
+  error (_("Invalid argument."));
+}
+
+/* See record.h.  */
+
 int
 record_read_memory (struct gdbarch *gdbarch,
 		    CORE_ADDR memaddr, gdb_byte *myaddr,
diff --git a/gdb/record.h b/gdb/record.h
index 84440c64..eb091ce 100644
--- a/gdb/record.h
+++ b/gdb/record.h
@@ -91,4 +91,9 @@ extern struct target_ops *find_record_target (void);
    it does anything.  */
 extern void record_preopen (void);
 
+/* Internal function that starts recording with the given METHOD and FORMAT.
+   NULL means default method or format.  */
+extern void record_start (const char *method, const char *format,
+			  int from_tty);
+
 #endif /* _RECORD_H_ */
-- 
2.7.4

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

* [PATCH v3 7/8] python: Add tests for record Python bindings
  2016-11-21 15:49 [PATCH v3 0/8] Python bindings for btrace recordings Tim Wiederhake
  2016-11-21 15:49 ` [PATCH v3 1/8] btrace: Count gaps as one instruction explicitly Tim Wiederhake
@ 2016-11-21 15:49 ` Tim Wiederhake
  2016-11-29 15:48   ` Metzger, Markus T
  2016-11-21 15:49 ` [PATCH v3 6/8] python: Implement btrace Python bindings for record history Tim Wiederhake
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Tim Wiederhake @ 2016-11-21 15:49 UTC (permalink / raw)
  To: gdb-patches; +Cc: palves, markus.t.metzger

2016-11-21  Tim Wiederhake  <tim.wiederhake@intel.com>

gdb/testsuite/ChangeLog:

        * gdb.python/py-record-btrace.c: New file.
        * gdb.python/py-record-btrace.exp: New file.


---
 gdb/testsuite/gdb.python/py-record-btrace.c   |  48 ++++++++
 gdb/testsuite/gdb.python/py-record-btrace.exp | 160 ++++++++++++++++++++++++++
 2 files changed, 208 insertions(+)
 create mode 100644 gdb/testsuite/gdb.python/py-record-btrace.c
 create mode 100644 gdb/testsuite/gdb.python/py-record-btrace.exp

diff --git a/gdb/testsuite/gdb.python/py-record-btrace.c b/gdb/testsuite/gdb.python/py-record-btrace.c
new file mode 100644
index 0000000..e12c132
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-record-btrace.c
@@ -0,0 +1,48 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2016 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <tim.wiederhake@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/>.  */
+
+int
+function (int arg)
+{
+  return arg + 1;
+}
+
+void
+inner (void)
+{
+}
+
+void
+outer (void)
+{
+  inner ();
+}
+
+int
+main (void)
+{
+  int i, j;
+
+  for (i = 0, j = 0; i < 100; ++i)
+    j = function (j);
+
+  outer ();
+
+  return j;
+}
diff --git a/gdb/testsuite/gdb.python/py-record-btrace.exp b/gdb/testsuite/gdb.python/py-record-btrace.exp
new file mode 100644
index 0000000..8f8beeb
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-record-btrace.exp
@@ -0,0 +1,160 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2016 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <tim.wiederhake@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/>.
+
+# Skip this test if btrace is disabled.
+
+if { [skip_btrace_tests] } { return -1 }
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+if [prepare_for_testing $testfile.exp $testfile $srcfile] { return -1 }
+
+# Skip this test if python is disabled.
+
+if { [skip_python_tests] } { return -1 }
+
+if ![runto_main ] then {
+    fail "Can't run to main"
+    return -1
+}
+
+with_test_prefix "no or double record" {
+    gdb_test "python print(gdb.current_recording())" "None"
+
+    gdb_test_no_output "python gdb.start_recording()"
+    gdb_test "python gdb.start_recording(\"full\")" ".*gdb\.error: The process is already being recorded\..*"
+
+    gdb_test_no_output "python gdb.stop_recording()" "first"
+    gdb_test "python gdb.stop_recording()" ".*gdb\.error: No record target is currently active\..*" "second"
+}
+
+with_test_prefix "preopened record full" {
+    gdb_test_no_output "record full"
+    gdb_test "python print(gdb.current_recording().method)" "full"
+    gdb_test "python print(gdb.current_recording().format)" "full"
+    gdb_test_no_output "python gdb.stop_recording()"
+}
+
+with_test_prefix "preopened record btrace" {
+    gdb_test_no_output "record btrace"
+    gdb_test "python print(gdb.current_recording().method)" "btrace"
+    gdb_test "python print(gdb.current_recording().format)" "pt|bts"
+    gdb_test_no_output "python gdb.stop_recording()"
+}
+
+with_test_prefix "open record full" {
+    gdb_test_no_output "python r = gdb.start_recording(\"full\")"
+    gdb_test "python print(r.method)" "full"
+    gdb_test "python print(r.format)" "full"
+    gdb_test_no_output "python gdb.stop_recording()"
+}
+
+with_test_prefix "open record btrace" {
+    gdb_test_no_output "python r = gdb.start_recording(\"btrace\")"
+    gdb_test "python print(r.method)" "btrace"
+    gdb_test "python print(r.format)" "pt|bts"
+    gdb_test_no_output "python gdb.stop_recording()"
+}
+
+with_test_prefix "prepare record" {
+    gdb_test_no_output "python r = gdb.start_recording(\"btrace\")"
+    gdb_test "stepi 100" ".*"
+    gdb_test_no_output "python insn = r.instruction_history"
+    gdb_test_no_output "python call = r.function_call_history"
+    gdb_test_no_output "python i = insn\[0\]"
+    gdb_test_no_output "python c = call\[0\]"
+}
+
+with_test_prefix "replay begin" {
+    gdb_test "python print(r.replay_position)" "None"
+    gdb_test "python r.goto(r.begin)"
+    gdb_test "python print(r.replay_position.number)" "1"
+}
+
+with_test_prefix "replay end" {
+    gdb_test "python r.goto(r.end)"
+    gdb_test "python print(r.replay_position)" "None"
+}
+
+with_test_prefix "instruction " {
+    gdb_test "python print(i.number)" "1"
+    gdb_test "python print(i.error)" "None"
+    gdb_test "python print(i.symbol)" "symbol and line for .*"
+    gdb_test "python print(i.pc)" "$decimal"
+    gdb_test "python print(i.data)" "<memory at $hex>"
+    gdb_test "python print(i.decoded)" ".*"
+    gdb_test "python print(i.size)" "$decimal"
+    gdb_test "python print(i.is_speculative)" "False"
+}
+
+with_test_prefix "function call" {
+    gdb_test "python print(c.number)" "1"
+    gdb_test "python print(c.symbol)" "main"
+    gdb_test "python print(c.level)" "$decimal"
+    gdb_test "python print(len(c.instructions))" "$decimal"
+    gdb_test "python print(c.up)" "None"
+    gdb_test "python print(c.prev_sibling)" "None"
+    gdb_test "python print(c == c.next_sibling.prev_sibling)" "True"
+}
+
+with_test_prefix "list" {
+    gdb_test "python print(len(insn))" "100"
+    gdb_test "python print(len(insn\[23:65\]))" "42"
+    gdb_test "python print(insn\[17:\]\[2\].number)" "20"
+    gdb_test "python print(i in insn)" "True"
+    gdb_test "python print(i in call)" "False"
+    gdb_test "python print(c in insn)" "False"
+    gdb_test "python print(c in call)" "True"
+    gdb_test "python print(insn.index(i))" "0"
+    gdb_test "python print(insn.count(i))" "1"
+}
+
+with_test_prefix "sublist" {
+    gdb_test_no_output "python s1 = insn\[3:72:5\]"
+    gdb_test_no_output "python s2 = s1\[2:13:3\]"
+    gdb_test_no_output "python s3 = s1\[13:2:-3\]"
+    gdb_test_no_output "python s4 = insn\[::-1\]"
+
+    gdb_test "python print(\[i.number for i in s1\])" "\\\[4, 9, 14, 19, 24, 29, 34, 39, 44, 49, 54, 59, 64, 69\\\]"
+    gdb_test "python print(\[i.number for i in s2\])" "\\\[14, 29, 44, 59\\\]"
+    gdb_test "python print(\[i.number for i in s3\])" "\\\[69, 54, 39, 24\\\]"
+
+    gdb_test "python print(len(s1))" "14"
+    gdb_test "python print(len(s2))" "4"
+    gdb_test "python print(len(s3))" "4"
+    gdb_test "python print(len(s4))" "100"
+
+    gdb_test "python print(s4\[5\].number)" "95"
+    gdb_test "python print(s4\[-5\].number)" "5"
+    gdb_test "python print(s4\[100\].number)" ".*IndexError.*"
+    gdb_test "python print(s4\[-101\].number)" ".*IndexError.*"
+}
+
+with_test_prefix "level" {
+    gdb_test_no_output "python gdb.stop_recording()"
+    gdb_test "break inner" "Breakpoint.*"
+    gdb_test "continue" "Continuing\..*"
+    gdb_test_no_output "record btrace"
+    gdb_test "step" "outer ().*" "step one"
+    gdb_test "step" "main ().*" "step two"
+    gdb_test "python print(gdb.current_recording().function_call_history\[0\].level)" "1"
+    gdb_test "python print(gdb.current_recording().function_call_history\[1\].level)" "0"
+}
-- 
2.7.4

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

* [PATCH v3 8/8] Add documentation for new instruction record Python bindings.
  2016-11-21 15:49 [PATCH v3 0/8] Python bindings for btrace recordings Tim Wiederhake
                   ` (2 preceding siblings ...)
  2016-11-21 15:49 ` [PATCH v3 6/8] python: Implement btrace Python bindings for record history Tim Wiederhake
@ 2016-11-21 15:49 ` Tim Wiederhake
  2016-11-29 15:48   ` Metzger, Markus T
  2016-11-21 15:49 ` [PATCH v3 3/8] btrace: Use binary search to find instruction Tim Wiederhake
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Tim Wiederhake @ 2016-11-21 15:49 UTC (permalink / raw)
  To: gdb-patches; +Cc: palves, markus.t.metzger

2016-11-21  Tim Wiederhake  <tim.wiederhake@intel.com>

gdb/ChangeLog:

	* NEWS: Add record Python bindings entry.

gdb/doc/ChangeLog:

	* python.texi (Recordings In Python): New section.


---
 gdb/NEWS            |   4 +
 gdb/doc/python.texi | 237 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 241 insertions(+)

diff --git a/gdb/NEWS b/gdb/NEWS
index a597405..b39860e 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,10 @@
 
 *** Changes since GDB 7.12
 
+* Python Scripting
+
+  ** New functions to start, stop and access a running btrace recording.
+
 * Building GDB and GDBserver now requires a C++11 compiler.
 
   For example, GCC 4.8 or later.
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index d6507e5..70b8d37 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -151,6 +151,7 @@ optional arguments while skipping others.  Example:
 * Inferiors In Python::         Python representation of inferiors (processes)
 * Events In Python::            Listening for events from @value{GDBN}.
 * Threads In Python::           Accessing inferior threads from Python.
+* Recordings In Python::        Accessing recordings from Python.
 * Commands In Python::          Implementing new commands in Python.
 * Parameters In Python::        Adding new @value{GDBN} parameters.
 * Functions In Python::         Writing new convenience functions.
@@ -3062,6 +3063,242 @@ Return a Boolean indicating whether the thread is running.
 Return a Boolean indicating whether the thread is exited.
 @end defun
 
+@node Recordings In Python
+@subsubsection Recordings In Python
+@cindex recordings in python
+
+The following recordings-related functions
+(@pxref{Process Record and Replay}) are available in the @code{gdb}
+module:
+
+@defun gdb.start_recording (@r{[}method@r{]}, @r{[}format@r{]})
+Start a recording using the given @var{method} and @var{format}.  If
+no @var{format} is given, the default format for the recording method
+is used.  If no @var{method} is given, the default method will be used.
+Returns a @code{gdb.Record} object on success.  Throw an exception on
+failure.
+
+The following strings can be passed as @var{method}:
+
+@itemize @bullet
+@item
+@code{"full"}
+@item
+@code{"btrace"}: Possible values for @var{format}: @code{"pt"},
+@code{"bts"} or leave out for default format.
+@end itemize
+@end defun
+
+@defun gdb.current_recording ()
+Access a currently running recording.  Return a @code{gdb.Record}
+object on success.  Return @code{None} if no recording is currently
+active.
+@end defun
+
+@defun gdb.stop_recording ()
+Stop the current recording.  Throw an exception if no recording is
+currently active.  All record objects become invalid after this call.
+@end defun
+
+A @code{gdb.Record} object has the following attributes:
+
+@defvar Record.method
+A string with the current recording method, e.g.@: @code{full} or
+@code{btrace}.
+@end defvar
+
+@defvar Record.format
+A string with the current recording format, e.g.@: @code{bt} or
+@code{pts}.  Can be empty.
+@end defvar
+
+@defvar Record.begin
+A format specific instruction object representing the first instruction
+in this recording.
+@end defvar
+
+@defvar Record.end
+A format specific instruction object representing the current
+instruction, that is not actually part of the recording.
+@end defvar
+
+@defvar Record.replay_position
+The instruction representing the current replay position.  If there is
+no replay active, this will be @code{None}.
+@end defvar
+
+@defvar Record.instruction_history
+A list with all recorded instructions.
+@end defvar
+
+@defvar Record.function_call_history
+A list with all recorded function call segments.
+@end defvar
+
+A @code{gdb.Record} object has the following methods:
+
+@defun Record.goto (instruction)
+Move the replay position to the given @var{instruction}.
+@end defun
+
+The attributes and methods of instruction objects depend on the current
+recording format.  Currently, only btrace instructions are supported.
+
+A @code{gdb.BtraceInstruction} object has the following attributes:
+
+@defvar BtraceInstruction.number
+An integer identifying this instruction.  @var{number} corresponds to
+the numbers seen in @code{record instruction-history}
+(@pxref{Process Record and Replay}).
+@end defvar
+
+@defvar BtraceInstruction.error
+An integer identifying the error code for gaps in the record.
+@code{None} for regular instructions.
+@end defvar
+
+@defvar BtraceInstruction.symbol
+A @code{gdb.Symtab_and_line} object representing the associated line
+and symbol of this instruction.  May be @code{None} if the instruction
+is a gap.
+@end defvar
+
+@defvar BtraceInstruction.pc
+An integer representing this instruction's address.  May be @code{None}
+if the instruction is a gap or the debug symbols could not be read.
+@end defvar
+
+@defvar BtraceInstruction.data
+A buffer with the raw instruction data.  May be @code{None} if the
+instruction is a gap.
+@end defvar
+
+@defvar BtraceInstruction.decoded
+A human readable string with the decoded instruction.  Contains the
+error message for gaps.
+@end defvar
+
+@defvar BtraceInstruction.size
+The size of the instruction in bytes.  Will be @code{None} if the
+instruction is a gap.
+@end defvar
+
+@defvar BtraceInstruction.is_speculative
+A boolean indicating whether the instruction was executed
+speculatively.  Will be @code{None} for gaps.
+@end defvar
+
+The attributes and methods of function call objects depend on the
+current recording format.  Currently, only btrace function calls are
+supported.
+
+A @code{gdb.BtraceFunctionCall} object has the following attributes:
+
+@defvar BtraceFunctionCall.number
+An integer identifying this function call.  @var{number} corresponds to
+the numbers seen in @code{record function-call-history}
+(@pxref{Process Record and Replay}).
+@end defvar
+
+@defvar BtraceFunctionCall.symbol
+A @code{gdb.Symbol} object representing the associated symbol.  May be
+@code{Node} if the function call is a gap or the debug symbols could
+not be read.
+@end defvar
+
+@defvar BtraceFunctionCall.level
+An integer representing the function call's stack level.  May be
+@code{None} if the function call is a gap.
+@end defvar
+
+@defvar BtraceFunctionCall.instructions
+A list of instructions associated with this function call.
+@end defvar
+
+@defvar BtraceFunctionCall.up
+A @code{gdb.BtraceFunctionCall} object representing the caller's
+function segment.  If the call has not been recorded, this will be the
+function segment to which control returns.  If neither the call nor the
+return have been recorded, this will be @code{None}.
+@end defvar
+
+@defvar BtraceFunctionCall.prev_sibling
+A @code{gdb.BtraceFunctionCall} object representing the previous
+segment of this function call.  May be @code{None}.
+@end defvar
+
+@defvar BtraceFunctionCall.next_sibling
+A @code{gdb.BtraceFunctionCall} object representing the next segment of
+this function call.  May be @code{None}.
+@end defvar
+
+The following example demonstrates the usage of these objects and
+functions to create a function that will rewind a record to the last
+time a function in a different file was executed.  This would typically
+be used to track the execution of user provided callback functions in a
+library which typically are not visible in a back trace.
+
+@smallexample
+def bringback ():
+    rec = gdb.current_recording ()
+    if not rec:
+        return
+
+    insn = rec.instruction_history
+    if len (insn) == 0:
+        return
+
+    try:
+        position = insn.index (rec.replay_position)
+    except:
+        position = -1
+    try:
+        filename = insn[position].symbol.symtab.fullname ()
+    except:
+        filename = None
+
+    for i in reversed (insn[:position]):
+	try:
+            current = i.symbol.symtab.fullname ()
+	except:
+            current = None
+
+        if filename == current:
+            continue
+
+        rec.goto (i)
+        return
+@end smallexample
+
+Another possible application is to write a function that counts the
+number of code executions in a given line range.  This line range can
+contain parts of functions or span across several functions and is not
+limited to be contiguous.
+
+@smallexample
+def countrange (filename, linerange):
+    count = 0
+
+    def filter_only (file_name):
+        for call in gdb.current_recording ().function_call_history:
+            try:
+                if file_name in call.symbol.symtab.fullname ():
+                    yield call
+            except:
+                pass
+
+    for c in filter_only (filename):
+        for i in c.instructions:
+            try:
+                if i.symbol.line in linerange:
+                    count += 1
+                    break;
+            except:
+                    pass
+
+    return count
+@end smallexample
+
 @node Commands In Python
 @subsubsection Commands In Python
 
-- 
2.7.4

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

* [PATCH v3 0/8] Python bindings for btrace recordings
@ 2016-11-21 15:49 Tim Wiederhake
  2016-11-21 15:49 ` [PATCH v3 1/8] btrace: Count gaps as one instruction explicitly Tim Wiederhake
                   ` (8 more replies)
  0 siblings, 9 replies; 20+ messages in thread
From: Tim Wiederhake @ 2016-11-21 15:49 UTC (permalink / raw)
  To: gdb-patches; +Cc: palves, markus.t.metzger

This patch series adds Python bindings for btrace recordings.

V1 of this series can be found here:
https://sourceware.org/ml/gdb-patches/2016-10/msg00733.html

V2 of this series can be found here:
https://sourceware.org/ml/gdb-patches/2016-11/msg00084.html

Changes in V3:
- Rebased to current master.
- Replaced some strncmp with strcmp in patch 4 ("Add record_start function").
- Incorporated Eli's feedback regarding the documentation part.

Tim Wiederhake (8):
  btrace: Count gaps as one instruction explicitly.
  btrace: Export btrace_decode_error function.
  btrace: Use binary search to find instruction.
  Add record_start function.
  python: Create Python bindings for record history.
  python: Implement btrace Python bindings for record history.
  python: Add tests for record Python bindings
  Add documentation for new instruction record Python bindings.

 gdb/Makefile.in                               |   4 +
 gdb/NEWS                                      |   4 +
 gdb/btrace.c                                  | 163 +++--
 gdb/btrace.h                                  |  21 +-
 gdb/doc/python.texi                           | 237 ++++++
 gdb/python/py-btrace.c                        | 994 ++++++++++++++++++++++++++
 gdb/python/py-btrace.h                        |  32 +
 gdb/python/py-record.c                        | 257 +++++++
 gdb/python/py-record.h                        |  57 ++
 gdb/python/python-internal.h                  |   7 +
 gdb/python/python.c                           |  14 +
 gdb/record-btrace.c                           | 131 ++--
 gdb/record-full.c                             |  20 +
 gdb/record.c                                  |  28 +
 gdb/record.h                                  |   5 +
 gdb/target-debug.h                            |   2 +
 gdb/target-delegates.c                        |  33 +
 gdb/target.c                                  |   7 +
 gdb/target.h                                  |  10 +
 gdb/testsuite/gdb.python/py-record-btrace.c   |  48 ++
 gdb/testsuite/gdb.python/py-record-btrace.exp | 160 +++++
 21 files changed, 2096 insertions(+), 138 deletions(-)
 create mode 100644 gdb/python/py-btrace.c
 create mode 100644 gdb/python/py-btrace.h
 create mode 100644 gdb/python/py-record.c
 create mode 100644 gdb/python/py-record.h
 create mode 100644 gdb/testsuite/gdb.python/py-record-btrace.c
 create mode 100644 gdb/testsuite/gdb.python/py-record-btrace.exp

-- 
2.7.4

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

* [PATCH v3 3/8] btrace: Use binary search to find instruction.
  2016-11-21 15:49 [PATCH v3 0/8] Python bindings for btrace recordings Tim Wiederhake
                   ` (3 preceding siblings ...)
  2016-11-21 15:49 ` [PATCH v3 8/8] Add documentation for new instruction record Python bindings Tim Wiederhake
@ 2016-11-21 15:49 ` Tim Wiederhake
  2016-11-29 15:47   ` Metzger, Markus T
  2016-11-21 15:49 ` [PATCH v3 2/8] btrace: Export btrace_decode_error function Tim Wiederhake
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Tim Wiederhake @ 2016-11-21 15:49 UTC (permalink / raw)
  To: gdb-patches; +Cc: palves, markus.t.metzger

Currently, btrace_find_insn_by_number will iterate over all function call
segments to find the one that contains the needed instruction.  This linear
search is too slow for the upcoming Python bindings that will use this
function to access instructions.  This patch introduces a vector in struct
btrace_thread_info that holds pointers to all recorded function segments and
allows to use binary search.

The proper solution is to turn the underlying tree into a vector of objects
and use indices for access.  This requires more work.  A patch set is
currently being worked on and will be published later.

2016-11-21  Tim Wiederhake  <tim.wiederhake@intel.com>

gdb/ChangeLog:
	* btrace.c (btrace_fetch): Copy function call segments pointer
	into a vector.
	(btrace_clear): Clear the vector.
	(btrace_find_insn_by_number): Use binary search to find the correct
	function call segment.
	* btrace.h (brace_fun_p): New typedef.
	(struct btrace_thread_info) <functions>: New field.


---
 gdb/btrace.c | 39 +++++++++++++++++++++++++++++++++------
 gdb/btrace.h |  7 +++++++
 2 files changed, 40 insertions(+), 6 deletions(-)

diff --git a/gdb/btrace.c b/gdb/btrace.c
index ac7d46e..c878407 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -1810,6 +1810,8 @@ btrace_fetch (struct thread_info *tp)
   /* Compute the trace, provided we have any.  */
   if (!btrace_data_empty (&btrace))
     {
+      struct btrace_function *bfun;
+
       /* Store the raw trace data.  The stored data will be cleared in
 	 btrace_clear, so we always append the new trace.  */
       btrace_data_append (&btinfo->data, &btrace);
@@ -1817,6 +1819,11 @@ btrace_fetch (struct thread_info *tp)
 
       btrace_clear_history (btinfo);
       btrace_compute_ftrace (tp, &btrace);
+
+      VEC_truncate (btrace_fun_p, btinfo->functions, 0);
+
+      for (bfun = btinfo->begin; bfun != NULL; bfun = bfun->flow.next)
+	VEC_safe_push (btrace_fun_p, btinfo->functions, bfun);
     }
 
   do_cleanups (cleanup);
@@ -1839,6 +1846,8 @@ btrace_clear (struct thread_info *tp)
 
   btinfo = &tp->btrace;
 
+  VEC_free (btrace_fun_p, btinfo->functions);
+
   it = btinfo->begin;
   while (it != NULL)
     {
@@ -2429,20 +2438,38 @@ btrace_find_insn_by_number (struct btrace_insn_iterator *it,
 			    unsigned int number)
 {
   const struct btrace_function *bfun;
+  unsigned int upper, lower, average;
 
-  for (bfun = btinfo->end; bfun != NULL; bfun = bfun->flow.prev)
-    if (bfun->insn_offset <= number)
-      break;
+  if (VEC_empty (btrace_fun_p, btinfo->functions))
+      return 0;
 
-  if (bfun == NULL)
+  lower = 0;
+  bfun = VEC_index (btrace_fun_p, btinfo->functions, lower);
+  if (number < bfun->insn_offset)
     return 0;
 
-  if (bfun->insn_offset + ftrace_call_num_insn (bfun) <= number)
+  upper = VEC_length (btrace_fun_p, btinfo->functions) - 1;
+  bfun = VEC_index (btrace_fun_p, btinfo->functions, upper);
+  if (number >= bfun->insn_offset + ftrace_call_num_insn (bfun))
     return 0;
 
+  average = lower + (upper - lower) / 2;
+  bfun = VEC_index (btrace_fun_p, btinfo->functions, average);
+
+  while ((number >= bfun->insn_offset + ftrace_call_num_insn (bfun))
+      || (number < bfun->insn_offset))
+    {
+      if (number < bfun->insn_offset)
+	upper = average - 1;
+      else
+	lower = average + 1;
+
+      average = lower + (upper - lower) / 2;
+      bfun = VEC_index (btrace_fun_p, btinfo->functions, average);
+    }
+
   it->function = bfun;
   it->index = number - bfun->insn_offset;
-
   return 1;
 }
 
diff --git a/gdb/btrace.h b/gdb/btrace.h
index d0497b4..6de2307 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -187,6 +187,9 @@ struct btrace_function
   btrace_function_flags flags;
 };
 
+typedef struct btrace_function *btrace_fun_p;
+DEF_VEC_P (btrace_fun_p);
+
 /* A branch trace instruction iterator.  */
 struct btrace_insn_iterator
 {
@@ -337,6 +340,10 @@ struct btrace_thread_info
   struct btrace_function *begin;
   struct btrace_function *end;
 
+  /* Vector of pointer to decoded function segments.  These are in execution
+     order with the first element == BEGIN and the last element == END.  */
+  VEC (btrace_fun_p) *functions;
+
   /* The function level offset.  When added to each function's LEVEL,
      this normalizes the function levels such that the smallest level
      becomes zero.  */
-- 
2.7.4

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

* [PATCH v3 1/8] btrace: Count gaps as one instruction explicitly.
  2016-11-21 15:49 [PATCH v3 0/8] Python bindings for btrace recordings Tim Wiederhake
@ 2016-11-21 15:49 ` Tim Wiederhake
  2016-11-29 15:47   ` Metzger, Markus T
  2016-11-21 15:49 ` [PATCH v3 7/8] python: Add tests for record Python bindings Tim Wiederhake
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Tim Wiederhake @ 2016-11-21 15:49 UTC (permalink / raw)
  To: gdb-patches; +Cc: palves, markus.t.metzger

This gives all instructions, including gaps, a unique number.  Add a function
to retrieve the error code if a btrace instruction iterator points to an
invalid instruction.

2016-11-21  Tim Wiederhake  <tim.wiederhake@intel.com>

gdb/ChangeLog:

	* btrace.c (ftrace_call_num_insn, btrace_insn_get_error): New function.
	(ftrace_new_function, btrace_insn_number, btrace_insn_cmp,
	btrace_find_insn_by_number): Remove special case for gaps.
	* btrace.h (btrace_insn_get_error): New export.
	(btrace_insn_number, btrace_find_insn_by_number): Adjust comment.
	* record-btrace.c (btrace_insn_history): Print number for gaps.
	(record_btrace_info, record_btrace_goto): Handle gaps.


---
 gdb/btrace.c        | 83 +++++++++++++++++------------------------------------
 gdb/btrace.h        |  9 ++++--
 gdb/record-btrace.c | 34 ++++++++--------------
 3 files changed, 45 insertions(+), 81 deletions(-)

diff --git a/gdb/btrace.c b/gdb/btrace.c
index 39d537c..7df1b00 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -141,6 +141,20 @@ ftrace_debug (const struct btrace_function *bfun, const char *prefix)
 		prefix, fun, file, level, ibegin, iend);
 }
 
+/* Return the number of instructions in a given function call segment.  */
+
+static unsigned int
+ftrace_call_num_insn (const struct btrace_function* bfun)
+{
+  if (bfun == NULL)
+    return 0;
+
+  if (bfun->errcode != 0)
+    return 1;
+
+  return VEC_length (btrace_insn_s, bfun->insn);
+}
+
 /* Return non-zero if BFUN does not match MFUN and FUN,
    return zero otherwise.  */
 
@@ -216,8 +230,7 @@ ftrace_new_function (struct btrace_function *prev,
       prev->flow.next = bfun;
 
       bfun->number = prev->number + 1;
-      bfun->insn_offset = (prev->insn_offset
-			   + VEC_length (btrace_insn_s, prev->insn));
+      bfun->insn_offset = prev->insn_offset + ftrace_call_num_insn (prev);
       bfun->level = prev->level;
     }
 
@@ -2150,18 +2163,18 @@ btrace_insn_get (const struct btrace_insn_iterator *it)
 
 /* See btrace.h.  */
 
-unsigned int
-btrace_insn_number (const struct btrace_insn_iterator *it)
+int
+btrace_insn_get_error (const struct btrace_insn_iterator *it)
 {
-  const struct btrace_function *bfun;
-
-  bfun = it->function;
+  return it->function->errcode;
+}
 
-  /* Return zero if the iterator points to a gap in the trace.  */
-  if (bfun->errcode != 0)
-    return 0;
+/* See btrace.h.  */
 
-  return bfun->insn_offset + it->index;
+unsigned int
+btrace_insn_number (const struct btrace_insn_iterator *it)
+{
+  return it->function->insn_offset + it->index;
 }
 
 /* See btrace.h.  */
@@ -2356,37 +2369,6 @@ btrace_insn_cmp (const struct btrace_insn_iterator *lhs,
   lnum = btrace_insn_number (lhs);
   rnum = btrace_insn_number (rhs);
 
-  /* A gap has an instruction number of zero.  Things are getting more
-     complicated if gaps are involved.
-
-     We take the instruction number offset from the iterator's function.
-     This is the number of the first instruction after the gap.
-
-     This is OK as long as both lhs and rhs point to gaps.  If only one of
-     them does, we need to adjust the number based on the other's regular
-     instruction number.  Otherwise, a gap might compare equal to an
-     instruction.  */
-
-  if (lnum == 0 && rnum == 0)
-    {
-      lnum = lhs->function->insn_offset;
-      rnum = rhs->function->insn_offset;
-    }
-  else if (lnum == 0)
-    {
-      lnum = lhs->function->insn_offset;
-
-      if (lnum == rnum)
-	lnum -= 1;
-    }
-  else if (rnum == 0)
-    {
-      rnum = rhs->function->insn_offset;
-
-      if (rnum == lnum)
-	rnum -= 1;
-    }
-
   return (int) (lnum - rnum);
 }
 
@@ -2398,26 +2380,15 @@ btrace_find_insn_by_number (struct btrace_insn_iterator *it,
 			    unsigned int number)
 {
   const struct btrace_function *bfun;
-  unsigned int end, length;
 
   for (bfun = btinfo->end; bfun != NULL; bfun = bfun->flow.prev)
-    {
-      /* Skip gaps. */
-      if (bfun->errcode != 0)
-	continue;
-
-      if (bfun->insn_offset <= number)
-	break;
-    }
+    if (bfun->insn_offset <= number)
+      break;
 
   if (bfun == NULL)
     return 0;
 
-  length = VEC_length (btrace_insn_s, bfun->insn);
-  gdb_assert (length > 0);
-
-  end = bfun->insn_offset + length;
-  if (end <= number)
+  if (bfun->insn_offset + ftrace_call_num_insn (bfun) <= number)
     return 0;
 
   it->function = bfun;
diff --git a/gdb/btrace.h b/gdb/btrace.h
index 202b986..114242d 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -405,9 +405,12 @@ extern void parse_xml_btrace_conf (struct btrace_config *conf, const char *xml);
 extern const struct btrace_insn *
   btrace_insn_get (const struct btrace_insn_iterator *);
 
+/* Return the error code for a branch trace instruction iterator.  Returns zero
+   if there is no error, i.e. the instruction is valid.  */
+extern int btrace_insn_get_error (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.  */
+   Returns one past the maximum instruction number for the end iterator.  */
 extern unsigned int btrace_insn_number (const struct btrace_insn_iterator *);
 
 /* Initialize a branch trace instruction iterator to point to the begin/end of
@@ -433,7 +436,7 @@ extern unsigned int btrace_insn_prev (struct btrace_insn_iterator *,
 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.
+/* Find an instruction or gap 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.  */
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 7c0e39f..6c50bab 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -439,28 +439,12 @@ record_btrace_info (struct target_ops *self)
       calls = btrace_call_number (&call);
 
       btrace_insn_end (&insn, btinfo);
-
       insns = btrace_insn_number (&insn);
-      if (insns != 0)
-	{
-	  /* The last instruction does not really belong to the trace.  */
-	  insns -= 1;
-	}
-      else
-	{
-	  unsigned int steps;
 
-	  /* Skip gaps at the end.  */
-	  do
-	    {
-	      steps = btrace_insn_prev (&insn, 1);
-	      if (steps == 0)
-		break;
-
-	      insns = btrace_insn_number (&insn);
-	    }
-	  while (insns == 0);
-	}
+      /* If the last instruction is not a gap, it is the current instruction
+	 that is not actually part of the record.  */
+      if (btrace_insn_get (&insn) != NULL)
+	insns -= 1;
 
       gaps = btinfo->ngaps;
     }
@@ -736,7 +720,11 @@ btrace_insn_history (struct ui_out *uiout,
 	  /* We have trace so we must have a configuration.  */
 	  gdb_assert (conf != NULL);
 
-	  btrace_ui_out_decode_error (uiout, it.function->errcode,
+	  ui_out_field_fmt (uiout, "insn-number", "%u",
+			    btrace_insn_number (&it));
+	  ui_out_text (uiout, "\t");
+
+	  btrace_ui_out_decode_error (uiout, btrace_insn_get_error (&it),
 				      conf->format);
 	}
       else
@@ -2827,7 +2815,9 @@ record_btrace_goto (struct target_ops *self, ULONGEST insn)
   tp = require_btrace_thread ();
 
   found = btrace_find_insn_by_number (&it, &tp->btrace, number);
-  if (found == 0)
+
+  /* Check if the instruction could not be found or is a gap.  */
+  if (found == 0 || btrace_insn_get (&it) == NULL)
     error (_("No such instruction."));
 
   record_btrace_set_replay (tp, &it);
-- 
2.7.4

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

* [PATCH v3 2/8] btrace: Export btrace_decode_error function.
  2016-11-21 15:49 [PATCH v3 0/8] Python bindings for btrace recordings Tim Wiederhake
                   ` (4 preceding siblings ...)
  2016-11-21 15:49 ` [PATCH v3 3/8] btrace: Use binary search to find instruction Tim Wiederhake
@ 2016-11-21 15:49 ` Tim Wiederhake
  2016-11-29 15:47   ` Metzger, Markus T
  2016-11-21 15:49 ` [PATCH v3 5/8] python: Create Python bindings for record history Tim Wiederhake
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Tim Wiederhake @ 2016-11-21 15:49 UTC (permalink / raw)
  To: gdb-patches; +Cc: palves, markus.t.metzger

2016-11-21  Tim Wiederhake  <tim.wiederhake@intel.com>

gdb/ChangeLog:

	* record-btrace.c (btrace_ui_out_decode_error): Move most of it ...
	* btrace.c (btrace_decode_error): ... here.  New function.
	* btrace.h (btrace_decode_error): New export.


---
 gdb/btrace.c        | 49 ++++++++++++++++++++++++++++++++++++++++++++
 gdb/btrace.h        |  5 +++++
 gdb/record-btrace.c | 59 ++++-------------------------------------------------
 3 files changed, 58 insertions(+), 55 deletions(-)

diff --git a/gdb/btrace.c b/gdb/btrace.c
index 7df1b00..ac7d46e 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -1699,6 +1699,55 @@ btrace_maint_clear (struct btrace_thread_info *btinfo)
 
 /* See btrace.h.  */
 
+const char *
+btrace_decode_error (enum btrace_format format, int errcode)
+{
+  switch (format)
+  {
+    case BTRACE_FORMAT_BTS:
+      switch (errcode)
+      {
+	case BDE_BTS_OVERFLOW:
+	  return _("instruction overflow");
+
+	case BDE_BTS_INSN_SIZE:
+	  return _("unknown instruction");
+
+	default:
+	  break;
+      }
+      break;
+
+#if defined (HAVE_LIBIPT)
+    case BTRACE_FORMAT_PT:
+      switch (errcode)
+      {
+	case BDE_PT_USER_QUIT:
+	  return _("trace decode cancelled");
+
+	case BDE_PT_DISABLED:
+	  return _("disabled");
+
+	case BDE_PT_OVERFLOW:
+	  return _("overflow");
+
+	default:
+	  if (errcode < 0)
+	    return pt_errstr (pt_errcode (errcode));
+	  break;
+      }
+      break;
+#endif /* defined (HAVE_LIBIPT)  */
+
+    default:
+      break;
+  }
+
+  return _("unknown");
+}
+
+/* See btrace.h.  */
+
 void
 btrace_fetch (struct thread_info *tp)
 {
diff --git a/gdb/btrace.h b/gdb/btrace.h
index 114242d..d0497b4 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -384,6 +384,11 @@ extern void btrace_disable (struct thread_info *);
    target_teardown_btrace instead of target_disable_btrace.  */
 extern void btrace_teardown (struct thread_info *);
 
+/* Return a human readable error string for the given ERRCODE in FORMAT.
+   The pointer will never be NULL and must not be freed.  */
+
+extern const char *btrace_decode_error (enum btrace_format format, int errcode);
+
 /* Fetch the branch trace for a single thread.  */
 extern void btrace_fetch (struct thread_info *);
 
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 6c50bab..0576123 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -464,63 +464,12 @@ static void
 btrace_ui_out_decode_error (struct ui_out *uiout, int errcode,
 			    enum btrace_format format)
 {
-  const char *errstr;
-  int is_error;
-
-  errstr = _("unknown");
-  is_error = 1;
-
-  switch (format)
-    {
-    default:
-      break;
-
-    case BTRACE_FORMAT_BTS:
-      switch (errcode)
-	{
-	default:
-	  break;
-
-	case BDE_BTS_OVERFLOW:
-	  errstr = _("instruction overflow");
-	  break;
-
-	case BDE_BTS_INSN_SIZE:
-	  errstr = _("unknown instruction");
-	  break;
-	}
-      break;
-
-#if defined (HAVE_LIBIPT)
-    case BTRACE_FORMAT_PT:
-      switch (errcode)
-	{
-	case BDE_PT_USER_QUIT:
-	  is_error = 0;
-	  errstr = _("trace decode cancelled");
-	  break;
-
-	case BDE_PT_DISABLED:
-	  is_error = 0;
-	  errstr = _("disabled");
-	  break;
-
-	case BDE_PT_OVERFLOW:
-	  is_error = 0;
-	  errstr = _("overflow");
-	  break;
-
-	default:
-	  if (errcode < 0)
-	    errstr = pt_errstr (pt_errcode (errcode));
-	  break;
-	}
-      break;
-#endif /* defined (HAVE_LIBIPT)  */
-    }
+  const char *errstr = btrace_decode_error (format, errcode);
 
   ui_out_text (uiout, _("["));
-  if (is_error)
+
+  /* ERRCODE > 0 indicates notifications on BTRACE_FORMAT_PT.  */
+  if (!(format == BTRACE_FORMAT_PT && errcode > 0))
     {
       ui_out_text (uiout, _("decode error ("));
       ui_out_field_int (uiout, "errcode", errcode);
-- 
2.7.4

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

* [PATCH v3 6/8] python: Implement btrace Python bindings for record history.
  2016-11-21 15:49 [PATCH v3 0/8] Python bindings for btrace recordings Tim Wiederhake
  2016-11-21 15:49 ` [PATCH v3 1/8] btrace: Count gaps as one instruction explicitly Tim Wiederhake
  2016-11-21 15:49 ` [PATCH v3 7/8] python: Add tests for record Python bindings Tim Wiederhake
@ 2016-11-21 15:49 ` Tim Wiederhake
  2016-11-29 15:48   ` Metzger, Markus T
  2016-11-21 15:49 ` [PATCH v3 8/8] Add documentation for new instruction record Python bindings Tim Wiederhake
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Tim Wiederhake @ 2016-11-21 15:49 UTC (permalink / raw)
  To: gdb-patches; +Cc: palves, markus.t.metzger

This patch implements the gdb.Record Python object methods and fields for
record target btrace.  Additionally, add a stub for record target full.

2016-11-21  Tim Wiederhake  <tim.wiederhake@intel.com>

gdb/ChangeLog:

	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-btrace.o.
	(SUBDIR_PYTHON_SRCS): Add py-btrace.c.
	* python/py-btrace.c: New file.
	* python/py-btrace.h: New file.
	* python/python-internal.h (gdbpy_initialize_btrace): New export.
	* python/python.c (_initialize_python): Add gdbpy_initialize_btrace.
	* record-btrace.c: Add conditional includes for python/py-record.h
	and python/py-btrace.h
	(record_btrace_python_interface): New function.
	(init_record_btrace_ops): Add record_btrace_python_interface.
	* record-full.c: Add conditional include for python/py-record.h.
	(record_full_python_interface): New function.
	(init_record_full_ops): Add record_full_python_interface.


---
 gdb/Makefile.in              |   2 +
 gdb/python/py-btrace.c       | 994 +++++++++++++++++++++++++++++++++++++++++++
 gdb/python/py-btrace.h       |  32 ++
 gdb/python/python-internal.h |   2 +
 gdb/python/python.c          |   1 +
 gdb/record-btrace.c          |  38 ++
 gdb/record-full.c            |  20 +
 7 files changed, 1089 insertions(+)
 create mode 100644 gdb/python/py-btrace.c
 create mode 100644 gdb/python/py-btrace.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 3340b5e..8bc0bf8 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -381,6 +381,7 @@ SUBDIR_PYTHON_OBS = \
 	py-block.o \
 	py-bpevent.o \
 	py-breakpoint.o \
+	py-btrace.o \
 	py-cmd.o \
 	py-continueevent.o \
 	py-xmethods.o \
@@ -422,6 +423,7 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-block.c \
 	python/py-bpevent.c \
 	python/py-breakpoint.c \
+	python/py-btrace.c \
 	python/py-cmd.c \
 	python/py-continueevent.c \
 	python/py-xmethods.c \
diff --git a/gdb/python/py-btrace.c b/gdb/python/py-btrace.c
new file mode 100644
index 0000000..0420ff6
--- /dev/null
+++ b/gdb/python/py-btrace.c
@@ -0,0 +1,994 @@
+/* Python interface to btrace instruction history.
+
+   Copyright 2016 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <tim.wiederhake@intel.com>
+
+   This file is part of GDB.
+
+   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 "defs.h"
+#include "py-btrace.h"
+#include "py-record.h"
+#include "gdbcore.h"
+#include "gdbcmd.h"
+#include "gdbthread.h"
+#include "btrace.h"
+#include "disasm.h"
+
+#if defined (IS_PY3K)
+
+#define BTPY_PYSLICE(x) (x)
+#define PyInt_FromSsize_t PyLong_FromSsize_t
+#define PyInt_FromSize_t PyLong_FromSize_t
+#define PyInt_AsSsize_t PyLong_AsSsize_t
+
+#else
+
+#define BTPY_PYSLICE(x) ((PySliceObject *) x)
+
+#endif
+
+#define BTPY_REQUIRE_VALID_INSN(obj, iter)				\
+    do {								\
+      struct thread_info *tinfo = find_thread_ptid (obj->ptid);		\
+      if (tinfo == NULL || btrace_is_empty (tinfo))			\
+	return PyErr_Format (gdbpy_gdb_error, _("Empty branch trace."));\
+      if (0 == btrace_find_insn_by_number (&iter, &tinfo->btrace,	\
+					   obj->number))		\
+	return PyErr_Format (gdbpy_gdb_error, _("No such instruction."));\
+    } while (0)
+
+#define BTPY_REQUIRE_VALID_CALL(obj, iter)				\
+    do {								\
+      struct thread_info *tinfo = find_thread_ptid (obj->ptid);		\
+      if (tinfo == NULL || btrace_is_empty (tinfo))			\
+	return PyErr_Format (gdbpy_gdb_error, _("Empty branch trace."));\
+      if (0 == btrace_find_call_by_number (&iter, &tinfo->btrace,	\
+					   obj->number))		\
+	return PyErr_Format (gdbpy_gdb_error, _("No such call segment."));\
+    } while (0)
+
+/* Python object for btrace records.  This can either be an instruction or a
+   function call segment, depending on the chosen type.  */
+
+typedef struct {
+  PyObject_HEAD
+
+  /* The thread this object belongs to.  */
+  ptid_t ptid;
+
+  /* Instruction number or function call segment number, depending on the type
+     of this object.  */
+  Py_ssize_t number;
+} btpy_object;
+
+/* Python object for btrace record lists.  */
+
+typedef struct {
+  PyObject_HEAD
+
+  /* The thread this list belongs to.  */
+  ptid_t ptid;
+
+  /* The first index being part of this list.  */
+  Py_ssize_t first;
+
+  /* The last index begin part of this list.  */
+  Py_ssize_t last;
+
+  /* Stride size.  */
+  Py_ssize_t step;
+
+  /* Either &BTPY_CALL_TYPE or &BTPY_INSN_TYPE.  */
+  PyTypeObject* element_type;
+} btpy_list_object;
+
+/* Python type for btrace instructions.  */
+
+static PyTypeObject btpy_insn_type = {
+  PyVarObject_HEAD_INIT (NULL, 0)
+};
+
+/* Python type for btrace function-calls.  */
+
+static PyTypeObject btpy_call_type = {
+  PyVarObject_HEAD_INIT (NULL, 0)
+};
+
+/* Python type for btrace lists.  */
+
+static PyTypeObject btpy_list_type = {
+  PyVarObject_HEAD_INIT (NULL, 0)
+};
+
+/* Create a new gdb.BtraceInstruction or gdb.BtraceFunctionCall object,
+   depending on TYPE.  */
+
+static PyObject *
+btpy_new (ptid_t ptid, Py_ssize_t number, PyTypeObject* type)
+{
+  btpy_object * const obj = PyObject_New (btpy_object, type);
+
+  if (obj == NULL)
+    return NULL;
+
+  obj->ptid = ptid;
+  obj->number = number;
+
+  return (PyObject *) obj;
+}
+
+/* Create a new gdb.BtraceInstruction object.  */
+
+static PyObject *
+btpy_insn_new (ptid_t ptid, Py_ssize_t number)
+{
+  return btpy_new (ptid, number, &btpy_insn_type);
+}
+
+/* Create a new gdb.BtraceFunctionCall object.  */
+
+static PyObject *
+btpy_call_new (ptid_t ptid, Py_ssize_t number)
+{
+  return btpy_new (ptid, number, &btpy_call_type);
+}
+
+/* Create a new gdb.BtraceList object.  */
+
+static PyObject *
+btpy_list_new (ptid_t ptid, Py_ssize_t first, Py_ssize_t last, Py_ssize_t step,
+	       PyTypeObject *element_type)
+{
+  btpy_list_object * const obj = PyObject_New (btpy_list_object,
+					       &btpy_list_type);
+
+  if (obj == NULL)
+    return NULL;
+
+  obj->ptid = ptid;
+  obj->first = first;
+  obj->last = last;
+  obj->step = step;
+  obj->element_type = element_type;
+
+  return (PyObject *) obj;
+}
+
+/* Implementation of BtraceInstruction.number [int] and
+   BtraceFunctionCall.number [int].  */
+
+static PyObject *
+btpy_number (PyObject *self, void *closure)
+{
+  const btpy_object * const obj = (btpy_object *) self;
+
+  return PyInt_FromSsize_t (obj->number);
+}
+
+/* Implementation of BtraceInstruction.__hash__ () -> int and
+   BtraceFunctionCall.__hash__ () -> int.  */
+
+static Py_hash_t
+btpy_hash (PyObject *self)
+{
+  const btpy_object * const obj = (btpy_object *) self;
+
+  return obj->number;
+}
+
+/* Implementation of BtraceInstruction.error [int].  Returns the
+   error code for gaps.  */
+
+static PyObject *
+btpy_insn_error (PyObject *self, void *closure)
+{
+  const btpy_object * const obj = (btpy_object *) self;
+  struct btrace_insn_iterator iter;
+  int error;
+
+  BTPY_REQUIRE_VALID_INSN (obj, iter);
+
+  error = btrace_insn_get_error (&iter);
+
+  if (error == 0)
+    Py_RETURN_NONE;
+
+  return PyInt_FromLong (error);
+}
+
+/* Implementation of BtraceInstruction.symbol [gdb.Symtab_and_line].
+   Return the symbol associated with this instruction.  */
+
+static PyObject *
+btpy_insn_symbol (PyObject *self, void *closure)
+{
+  const btpy_object * const obj = (btpy_object *) self;
+  const struct btrace_insn *insn;
+  struct btrace_insn_iterator iter;
+  PyObject *result = NULL;
+
+  BTPY_REQUIRE_VALID_INSN (obj, iter);
+
+  insn = btrace_insn_get (&iter);
+  if (insn == NULL)
+    Py_RETURN_NONE;
+
+  TRY
+    {
+      result = symtab_and_line_to_sal_object (find_pc_line (insn->pc, 0));
+    }
+  CATCH (except, RETURN_MASK_ALL)
+    {
+      GDB_PY_HANDLE_EXCEPTION (except);
+    }
+  END_CATCH
+
+  return result;
+}
+
+/* Implementation of BtraceInstruction.pc [int].  Returns
+   the instruction address.  */
+
+static PyObject *
+btpy_insn_pc (PyObject *self, void *closure)
+{
+  const btpy_object * const obj = (btpy_object *) self;
+  const struct btrace_insn *insn;
+  struct btrace_insn_iterator iter;
+
+  BTPY_REQUIRE_VALID_INSN (obj, iter);
+
+  insn = btrace_insn_get (&iter);
+  if (insn == NULL)
+    Py_RETURN_NONE;
+
+  return PyInt_FromSize_t (insn->pc);
+}
+
+/* Implementation of BtraceInstruction.size [int].  Returns
+   the instruction size.  */
+
+static PyObject *
+btpy_insn_size (PyObject *self, void *closure)
+{
+  const btpy_object * const obj = (btpy_object *) self;
+  const struct btrace_insn *insn;
+  struct btrace_insn_iterator iter;
+
+  BTPY_REQUIRE_VALID_INSN (obj, iter);
+
+  insn = btrace_insn_get (&iter);
+  if (insn == NULL)
+    Py_RETURN_NONE;
+
+  return PyInt_FromLong (insn->size);
+}
+
+/* Implementation of BtraceInstruction.is_speculative [bool].
+   Returns if this instruction was executed speculatively.  */
+
+static PyObject *
+btpy_insn_is_speculative (PyObject *self, void *closure)
+{
+  const btpy_object * const obj = (btpy_object *) self;
+  const struct btrace_insn *insn;
+  struct btrace_insn_iterator iter;
+
+  BTPY_REQUIRE_VALID_INSN (obj, iter);
+
+  insn = btrace_insn_get (&iter);
+  if (insn == NULL)
+    Py_RETURN_NONE;
+
+  if (insn->flags & BTRACE_INSN_FLAG_SPECULATIVE)
+    Py_RETURN_TRUE;
+  else
+    Py_RETURN_FALSE;
+}
+
+/* Implementation of BtraceInstruction.data [buffer].
+   Returns raw instruction data.  */
+
+static PyObject *
+btpy_insn_data (PyObject *self, void *closure)
+{
+  const btpy_object * const obj = (btpy_object *) self;
+  const struct btrace_insn *insn;
+  struct btrace_insn_iterator iter;
+  gdb_byte *buffer = NULL;
+  PyObject *object;
+
+  BTPY_REQUIRE_VALID_INSN (obj, iter);
+
+  insn = btrace_insn_get (&iter);
+  if (insn == NULL)
+    Py_RETURN_NONE;
+
+  TRY
+    {
+      buffer = (gdb_byte *) xmalloc (insn->size);
+      read_memory (insn->pc, buffer, insn->size);
+    }
+  CATCH (except, RETURN_MASK_ALL)
+    {
+      xfree (buffer);
+      GDB_PY_HANDLE_EXCEPTION (except);
+    }
+  END_CATCH
+
+  object = PyBytes_FromStringAndSize ((const char*) buffer, insn->size);
+  xfree (buffer);
+
+  if (object == NULL)
+    return NULL;
+
+  return PyMemoryView_FromObject (object);
+}
+
+/* Implementation of BtraceInstruction.decode [str].  Returns
+   the instruction as human readable string.  */
+
+static PyObject *
+btpy_insn_decode (PyObject *self, void *closure)
+{
+  const btpy_object * const obj = (btpy_object *) self;
+  const struct btrace_insn *insn;
+  struct btrace_insn_iterator iter;
+  struct cleanup *cleanup;
+  struct ui_file *strfile;
+  char* buffer;
+  PyObject *object;
+
+  BTPY_REQUIRE_VALID_INSN (obj, iter);
+
+  insn = btrace_insn_get (&iter);
+  if (insn == NULL)
+    {
+      int error_code = btrace_insn_get_error (&iter);
+      const struct btrace_config *config;
+
+      config = btrace_conf (&find_thread_ptid (obj->ptid)->btrace);
+      return PyBytes_FromString (btrace_decode_error (config->format,
+						      error_code));
+    }
+
+  strfile = mem_fileopen ();
+  cleanup = make_cleanup_ui_file_delete (strfile);
+
+  gdb_print_insn (target_gdbarch (), insn->pc, strfile, NULL);
+
+  buffer = ui_file_xstrdup (strfile, NULL);
+  make_cleanup (xfree, buffer);
+
+  object = PyBytes_FromString (buffer);
+
+  do_cleanups (cleanup);
+  return object;
+}
+
+/* Implementation of BtraceFunctionCall.level [int].  Returns the
+   call level.  */
+
+static PyObject *
+btpy_call_level (PyObject *self, void *closure)
+{
+  const btpy_object * const obj = (btpy_object *) self;
+  const struct btrace_function *func;
+  struct btrace_call_iterator iter;
+
+  BTPY_REQUIRE_VALID_CALL (obj, iter);
+
+  func = btrace_call_get (&iter);
+  if (func == NULL)
+    Py_RETURN_NONE;
+
+  return PyInt_FromLong (iter.btinfo->level + func->level);
+}
+
+/* Implementation of BtraceFunctionCall.symbol [gdb.Symbol].  Returns
+   the symbol associated with this function call.  */
+
+static PyObject *
+btpy_call_symbol (PyObject *self, void *closure)
+{
+  const btpy_object * const obj = (btpy_object *) self;
+  const struct btrace_function *func;
+  struct btrace_call_iterator iter;
+
+  BTPY_REQUIRE_VALID_CALL (obj, iter);
+
+  func = btrace_call_get (&iter);
+  if (func == NULL)
+    Py_RETURN_NONE;
+
+  if (func->sym == NULL)
+    Py_RETURN_NONE;
+
+  return symbol_to_symbol_object (func->sym);
+}
+
+/* Implementation of BtraceFunctionCall.instructions [list].
+   Return the list of instructions that belong to this function call.  */
+
+static PyObject *
+btpy_call_instructions (PyObject *self, void *closure)
+{
+  const btpy_object * const obj = (btpy_object *) self;
+  const struct btrace_function *func;
+  struct btrace_call_iterator iter;
+  unsigned int len;
+
+  BTPY_REQUIRE_VALID_CALL (obj, iter);
+
+  func = btrace_call_get (&iter);
+  if (func == NULL)
+    Py_RETURN_NONE;
+
+  len = VEC_length (btrace_insn_s, func->insn);
+
+  /* Gaps count as one instruction.  */
+  if (len == 0)
+    len = 1;
+
+  return btpy_list_new (obj->ptid, func->insn_offset, func->insn_offset + len,
+			1, &btpy_insn_type);
+}
+
+/* Implementation of BtraceFunctionCall.up [gdb.BtraceRecordCall].
+   Return the caller / returnee of this function.  */
+
+static PyObject *
+btpy_call_up (PyObject *self, void *closure)
+{
+  const btpy_object * const obj = (btpy_object *) self;
+  const struct btrace_function *func;
+  struct btrace_call_iterator iter;
+
+  BTPY_REQUIRE_VALID_CALL (obj, iter);
+
+  func = btrace_call_get (&iter);
+  if (func == NULL)
+    Py_RETURN_NONE;
+
+  if (func->up == NULL)
+    Py_RETURN_NONE;
+
+  return btpy_call_new (obj->ptid, func->up->number);
+}
+
+/* Implementation of BtraceFunctionCall.prev_sibling [BtraceFunctionCall].
+   Return a previous segment of this function.  */
+
+static PyObject *
+btpy_call_prev_sibling (PyObject *self, void *closure)
+{
+  const btpy_object * const obj = (btpy_object *) self;
+  const struct btrace_function *func;
+  struct btrace_call_iterator iter;
+
+  BTPY_REQUIRE_VALID_CALL (obj, iter);
+
+  func = btrace_call_get (&iter);
+  if (func == NULL)
+    Py_RETURN_NONE;
+
+  if (func->segment.prev == NULL)
+    Py_RETURN_NONE;
+
+  return btpy_call_new (obj->ptid, func->segment.prev->number);
+}
+
+/* Implementation of BtraceFunctionCall.next_sibling [BtraceFunctionCall].
+   Return a following segment of this function.  */
+
+static PyObject *
+btpy_call_next_sibling (PyObject *self, void *closure)
+{
+  const btpy_object * const obj = (btpy_object *) self;
+  const struct btrace_function *func;
+  struct btrace_call_iterator iter;
+
+  BTPY_REQUIRE_VALID_CALL (obj, iter);
+
+  func = btrace_call_get (&iter);
+  if (func == NULL)
+    Py_RETURN_NONE;
+
+  if (func->segment.next == NULL)
+    Py_RETURN_NONE;
+
+  return btpy_call_new (obj->ptid, func->segment.next->number);
+}
+
+/* Python rich compare function to allow for equality and inequality checks
+   in Python.  */
+
+static PyObject *
+btpy_richcompare (PyObject *self, PyObject *other, int op)
+{
+  const btpy_object * const obj1 = (btpy_object *) self;
+  const btpy_object * const obj2 = (btpy_object *) other;
+
+  if (Py_TYPE (self) != Py_TYPE (other))
+    {
+      Py_INCREF (Py_NotImplemented);
+      return Py_NotImplemented;
+    }
+
+  switch (op)
+  {
+    case Py_EQ:
+      if (ptid_equal (obj1->ptid, obj2->ptid) && obj1->number == obj2->number)
+	Py_RETURN_TRUE;
+      else
+	Py_RETURN_FALSE;
+
+    case Py_NE:
+      if (!ptid_equal (obj1->ptid, obj2->ptid) || obj1->number != obj2->number)
+	Py_RETURN_TRUE;
+      else
+	Py_RETURN_FALSE;
+
+    default:
+      break;
+  }
+
+  Py_INCREF (Py_NotImplemented);
+  return Py_NotImplemented;
+}
+
+/* Implementation of BtraceList.__len__ (self) -> int.  */
+
+static Py_ssize_t
+btpy_list_length (PyObject *self)
+{
+  const btpy_list_object * const obj = (btpy_list_object *) self;
+  const Py_ssize_t distance = obj->last - obj->first;
+  const Py_ssize_t result = distance / obj->step;
+
+  if ((distance % obj->step) == 0)
+    return result;
+
+  return result + 1;
+}
+
+/* Implementation of
+   BtraceList.__getitem__ (self, key) -> BtraceInstruction and
+   BtraceList.__getitem__ (self, key) -> BtraceFunctionCall.  */
+
+static PyObject *
+btpy_list_item (PyObject *self, Py_ssize_t index)
+{
+  const btpy_list_object * const obj = (btpy_list_object *) self;
+  struct thread_info * const tinfo = find_thread_ptid (obj->ptid);
+
+  if (index < 0 || index >= btpy_list_length (self))
+    return PyErr_Format (PyExc_IndexError, _("Index out of range: %zd."),
+			 index);
+
+  return btpy_new (obj->ptid, obj->first + (obj->step * index),
+		   obj->element_type);
+}
+
+/* Implementation of BtraceList.__getitem__ (self, slice) -> BtraceList.  */
+
+static PyObject *
+btpy_list_slice (PyObject *self, PyObject *value)
+{
+  const btpy_list_object * const obj = (btpy_list_object *) self;
+  const Py_ssize_t length = btpy_list_length (self);
+  Py_ssize_t start, stop, step, slicelength;
+
+  if (PyInt_Check (value))
+    {
+      Py_ssize_t index = PyInt_AsSsize_t (value);
+
+      /* Emulate Python behavior for negative indices.  */
+      if (index < 0)
+	index += length;
+
+      return btpy_list_item (self, index);
+    }
+
+  if (!PySlice_Check (value))
+    return PyErr_Format (PyExc_TypeError, _("Index must be int or slice."));
+
+  if (0 != PySlice_GetIndicesEx (BTPY_PYSLICE (value), length, &start, &stop,
+				 &step, &slicelength))
+    return NULL;
+
+  return btpy_list_new (obj->ptid, obj->first + obj->step * start,
+			obj->first + obj->step * stop, obj->step * step,
+			obj->element_type);
+}
+
+/* Helper function that returns the position of an element in a BtraceList
+   or -1 if the element is not in the list.  */
+
+static LONGEST
+btpy_list_position (PyObject *self, PyObject *value)
+{
+  const btpy_list_object * const list_obj = (btpy_list_object *) self;
+  const btpy_object * const obj = (btpy_object *) value;
+  Py_ssize_t index = obj->number;
+
+  if (list_obj->element_type != Py_TYPE (value))
+    return -1;
+
+  if (!ptid_equal (list_obj->ptid, obj->ptid))
+    return -1;
+
+  if (index < list_obj->first || index > list_obj->last)
+    return -1;
+
+  index -= list_obj->first;
+
+  if (index % list_obj->step != 0)
+    return -1;
+
+  return index / list_obj->step;
+}
+
+/* Implementation of "in" operator for BtraceLists.  */
+
+static int
+btpy_list_contains (PyObject *self, PyObject *value)
+{
+  if (btpy_list_position (self, value) < 0)
+    return 0;
+
+  return 1;
+}
+
+/* Implementation of BtraceLists.index (self, value) -> int.  */
+
+static PyObject *
+btpy_list_index (PyObject *self, PyObject *value)
+{
+  const LONGEST index = btpy_list_position (self, value);
+
+  if (index < 0)
+    return PyErr_Format (PyExc_ValueError, _("Not in list."));
+
+  return PyInt_FromSize_t (index);
+}
+
+/* Implementation of BtraceList.count (self, value) -> int.  */
+
+static PyObject *
+btpy_list_count (PyObject *self, PyObject *value)
+{
+  const LONGEST index = btpy_list_position (self, value);
+
+  if (index < 0)
+    return PyInt_FromLong (0);
+  else
+    return PyInt_FromLong (1);
+}
+
+/* Python rich compare function to allow for equality and inequality checks
+   in Python.  */
+
+static PyObject *
+btpy_list_richcompare (PyObject *self, PyObject *other, int op)
+{
+  const btpy_list_object * const obj1 = (btpy_list_object *) self;
+  const btpy_list_object * const obj2 = (btpy_list_object *) other;
+
+  if (Py_TYPE (self) != Py_TYPE (other))
+    {
+      Py_INCREF (Py_NotImplemented);
+      return Py_NotImplemented;
+    }
+
+  switch (op)
+  {
+    case Py_EQ:
+      if (ptid_equal (obj1->ptid, obj2->ptid)
+	  && obj1->element_type == obj2->element_type
+	  && obj1->first == obj2->first
+	  && obj1->last == obj2->last
+	  && obj1->step == obj2->step)
+	Py_RETURN_TRUE;
+      else
+	Py_RETURN_FALSE;
+
+    case Py_NE:
+      if (!ptid_equal (obj1->ptid, obj2->ptid)
+	  || obj1->element_type != obj2->element_type
+	  || obj1->first != obj2->first
+	  || obj1->last != obj2->last
+	  || obj1->step != obj2->step)
+	Py_RETURN_TRUE;
+      else
+	Py_RETURN_FALSE;
+
+    default:
+      break;
+  }
+
+  Py_INCREF (Py_NotImplemented);
+  return Py_NotImplemented;
+}
+
+/* Implementation of
+   BtraceRecord.replay_position [BtraceInstruction].  */
+
+static PyObject *
+recpy_bt_replay_position (PyObject* self, void* closure)
+{
+  const struct thread_info * const tinfo = find_thread_ptid (inferior_ptid);
+
+  if (tinfo == NULL)
+    Py_RETURN_NONE;
+
+  if (tinfo->btrace.replay == NULL)
+    Py_RETURN_NONE;
+
+  return btpy_insn_new (inferior_ptid,
+			btrace_insn_number (tinfo->btrace.replay));
+}
+
+/* Implementation of
+   BtraceRecord.begin [BtraceInstruction].  */
+
+static PyObject *
+recpy_bt_begin (PyObject* self, void* closure)
+{
+  struct thread_info * const tinfo = find_thread_ptid (inferior_ptid);
+  struct btrace_insn_iterator iterator;
+
+  if (tinfo == NULL)
+    Py_RETURN_NONE;
+
+  btrace_fetch (tinfo);
+
+  if (btrace_is_empty (tinfo))
+    Py_RETURN_NONE;
+
+  btrace_insn_begin (&iterator, &tinfo->btrace);
+  return btpy_insn_new (inferior_ptid, btrace_insn_number (&iterator));
+}
+
+/* Implementation of
+   BtraceRecord.end [BtraceInstruction].  */
+
+static PyObject *
+recpy_bt_end (PyObject* self, void* closure)
+{
+  struct thread_info * const tinfo = find_thread_ptid (inferior_ptid);
+  struct btrace_insn_iterator iterator;
+
+  if (tinfo == NULL)
+    Py_RETURN_NONE;
+
+  btrace_fetch (tinfo);
+
+  if (btrace_is_empty (tinfo))
+    Py_RETURN_NONE;
+
+  btrace_insn_end (&iterator, &tinfo->btrace);
+  return btpy_insn_new (inferior_ptid, btrace_insn_number (&iterator));
+}
+
+/* Implementation of
+   BtraceRecord.instruction_history [list].  */
+
+static PyObject *
+recpy_bt_instruction_history (PyObject* self, void* closure)
+{
+  struct thread_info * const tinfo = find_thread_ptid (inferior_ptid);
+  struct btrace_insn_iterator iterator;
+  unsigned long first = 0;
+  unsigned long last = 0;
+
+   if (tinfo == NULL)
+     Py_RETURN_NONE;
+
+   btrace_fetch (tinfo);
+
+   if (btrace_is_empty (tinfo))
+     Py_RETURN_NONE;
+
+   btrace_insn_begin (&iterator, &tinfo->btrace);
+   first = btrace_insn_number (&iterator);
+
+   btrace_insn_end (&iterator, &tinfo->btrace);
+   last = btrace_insn_number (&iterator);
+
+   return btpy_list_new (inferior_ptid, first, last, 1, &btpy_insn_type);
+}
+
+/* Implementation of
+   BtraceRecord.function_call_history [list].  */
+
+static PyObject *
+recpy_bt_function_call_history (PyObject* self, void* closure)
+{
+    struct thread_info * const tinfo = find_thread_ptid (inferior_ptid);
+    struct btrace_call_iterator iterator;
+    unsigned long first = 0;
+    unsigned long last = 0;
+
+    if (tinfo == NULL)
+      Py_RETURN_NONE;
+
+    btrace_fetch (tinfo);
+
+    if (btrace_is_empty (tinfo))
+      Py_RETURN_NONE;
+
+    btrace_call_begin (&iterator, &tinfo->btrace);
+    first = btrace_call_number (&iterator);
+
+    btrace_call_end (&iterator, &tinfo->btrace);
+    last = btrace_call_number (&iterator);
+
+    return btpy_list_new (inferior_ptid, first, last, 1, &btpy_call_type);
+}
+
+/* Implementation of BtraceRecord.goto (self, BtraceInstruction) -> None.  */
+
+static PyObject *
+recpy_bt_goto_position (PyObject* self, PyObject* args)
+{
+  struct thread_info * const tinfo = find_thread_ptid (inferior_ptid);
+  const btpy_object *obj;
+
+  if (tinfo == NULL || btrace_is_empty (tinfo))
+	return PyErr_Format (gdbpy_gdb_error, _("Empty branch trace."));
+
+  if (!PyArg_ParseTuple (args, "O", &obj))
+    return NULL;
+
+  if (Py_TYPE (obj) != &btpy_insn_type)
+    return PyErr_Format (PyExc_TypeError, _("Argument must be instruction."));
+
+  TRY
+    {
+      struct btrace_insn_iterator iter;
+      char buffer[256] = { '\0' };
+
+      btrace_insn_end (&iter, &tinfo->btrace);
+
+      if (btrace_insn_number (&iter) == obj->number)
+	execute_command_to_string ("record goto end", 0);
+      else
+	{
+	  snprintf (buffer, sizeof (buffer), "record goto %zi", obj->number);
+	  execute_command_to_string (buffer, 0);
+	}
+    }
+  CATCH (except, RETURN_MASK_ALL)
+    {
+      GDB_PY_HANDLE_EXCEPTION (except);
+    }
+  END_CATCH
+
+  Py_RETURN_NONE;
+}
+
+/* See py-btrace.h.  */
+
+void
+recpy_btrace_python_interface (struct record_python_interface *iface)
+{
+  iface->begin = recpy_bt_begin;
+  iface->end = recpy_bt_end;
+  iface->function_call_history = recpy_bt_function_call_history;
+  iface->instruction_history = recpy_bt_instruction_history;
+  iface->goto_position = recpy_bt_goto_position;
+  iface->replay_position = recpy_bt_replay_position;
+}
+
+/* BtraceInstruction members.  */
+
+struct PyGetSetDef btpy_insn_getset[] = {
+  { "number", btpy_number, NULL, "instruction number", NULL},
+  { "error", btpy_insn_error, NULL, "error number for gaps", NULL},
+  { "symbol", btpy_insn_symbol, NULL, "associated symbol", NULL},
+  { "pc", btpy_insn_pc, NULL, "instruction address", NULL},
+  { "data", btpy_insn_data, NULL, "raw instruction data", NULL},
+  { "decoded", btpy_insn_decode, NULL, "decoded instruction", NULL},
+  { "size", btpy_insn_size, NULL, "instruction size in byte", NULL},
+  { "is_speculative", btpy_insn_is_speculative, NULL, "if the instruction was \
+executed speculatively", NULL},
+  {NULL}
+};
+
+/* BtraceFunctionCall members.  */
+
+static PyGetSetDef btpy_call_getset[] = {
+  { "number", btpy_number, NULL, "function call number", NULL},
+  { "level", btpy_call_level, NULL, "call stack level", NULL},
+  { "symbol", btpy_call_symbol, NULL, "associated line and symbol", NULL},
+  { "instructions", btpy_call_instructions, NULL, "list of instructions in \
+this function call", NULL},
+  { "up", btpy_call_up, NULL, "caller or returned-to function segment", NULL},
+  { "prev_sibling", btpy_call_prev_sibling, NULL, "previous segment of this \
+function", NULL},
+  { "next_sibling", btpy_call_next_sibling, NULL, "next segment of this \
+function", NULL},
+  {NULL}
+};
+
+/* BtraceList methods.  */
+
+struct PyMethodDef btpy_list_methods[] = {
+  { "count", btpy_list_count, METH_O, "count number of occurences"},
+  { "index", btpy_list_index, METH_O, "index of entry"},
+  {NULL}
+};
+
+/* BtraceList sequence methods.  */
+
+static PySequenceMethods btpy_list_sequence_methods = {
+  NULL
+};
+
+/* BtraceList mapping methods.  Necessary for slicing.  */
+
+static PyMappingMethods btpy_list_mapping_methods = {
+  NULL
+};
+
+/* Sets up the btrace record API.  */
+
+int
+gdbpy_initialize_btrace (void)
+{
+  btpy_insn_type.tp_new = PyType_GenericNew;
+  btpy_insn_type.tp_flags = Py_TPFLAGS_DEFAULT;
+  btpy_insn_type.tp_basicsize = sizeof (btpy_object);
+  btpy_insn_type.tp_name = "gdb.BtraceInstruction";
+  btpy_insn_type.tp_doc = "GDB btrace instruction object";
+  btpy_insn_type.tp_getset = btpy_insn_getset;
+  btpy_insn_type.tp_richcompare = btpy_richcompare;
+  btpy_insn_type.tp_hash = btpy_hash;
+
+  btpy_call_type.tp_new = PyType_GenericNew;
+  btpy_call_type.tp_flags = Py_TPFLAGS_DEFAULT;
+  btpy_call_type.tp_basicsize = sizeof (btpy_object);
+  btpy_call_type.tp_name = "gdb.BtraceFunctionCall";
+  btpy_call_type.tp_doc = "GDB btrace call object";
+  btpy_call_type.tp_getset = btpy_call_getset;
+  btpy_call_type.tp_richcompare = btpy_richcompare;
+  btpy_call_type.tp_hash = btpy_hash;
+
+  btpy_list_type.tp_new = PyType_GenericNew;
+  btpy_list_type.tp_flags = Py_TPFLAGS_DEFAULT;
+  btpy_list_type.tp_basicsize = sizeof (btpy_list_object);
+  btpy_list_type.tp_name = "gdb.BtraceInstructionList";
+  btpy_list_type.tp_doc = "GDB instruction list object";
+  btpy_list_type.tp_methods = btpy_list_methods;
+  btpy_list_type.tp_as_sequence = &btpy_list_sequence_methods;
+  btpy_list_type.tp_as_mapping = &btpy_list_mapping_methods;
+  btpy_list_type.tp_richcompare = btpy_list_richcompare;
+
+  btpy_list_sequence_methods.sq_item = btpy_list_item;
+  btpy_list_sequence_methods.sq_length = btpy_list_length;
+  btpy_list_sequence_methods.sq_contains = btpy_list_contains;
+
+  btpy_list_mapping_methods.mp_subscript = btpy_list_slice;
+
+  if (PyType_Ready (&btpy_insn_type) < 0
+      || PyType_Ready (&btpy_call_type) < 0
+      || PyType_Ready (&btpy_list_type) < 0)
+    return -1;
+  else
+    return 0;
+}
diff --git a/gdb/python/py-btrace.h b/gdb/python/py-btrace.h
new file mode 100644
index 0000000..70e30a2
--- /dev/null
+++ b/gdb/python/py-btrace.h
@@ -0,0 +1,32 @@
+/* Python interface to btrace record targets.
+
+   Copyright 2016 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <tim.wiederhake@intel.com>
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#ifndef GDB_PY_BTRACE_H
+#define GDB_PY_BTRACE_H
+
+struct record_python_interface;
+
+/* Fill in the record btrace python interface object and return non-zero.
+   Return zero on failure or if no recording is active.  */
+
+extern void recpy_btrace_python_interface (struct record_python_interface *);
+
+#endif /* GDB_PY_BTRACE_H */
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index e2bb9a8..8dc8608 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -432,6 +432,8 @@ int gdbpy_initialize_values (void)
   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 int gdbpy_initialize_frames (void)
   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+int gdbpy_initialize_btrace (void)
+  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 int gdbpy_initialize_record (void)
   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 int gdbpy_initialize_symtabs (void)
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 7f031ae..53cebd0 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1787,6 +1787,7 @@ message == an error message without a stack will be printed."),
       || gdbpy_initialize_frames () < 0
       || gdbpy_initialize_commands () < 0
       || gdbpy_initialize_record () < 0
+      || gdbpy_initialize_btrace () < 0
       || gdbpy_initialize_symbols () < 0
       || gdbpy_initialize_symtabs () < 0
       || gdbpy_initialize_blocks () < 0
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 0576123..129a2ff 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -41,6 +41,11 @@
 #include "vec.h"
 #include <algorithm>
 
+#if HAVE_PYTHON
+#include "python/py-record.h"
+#include "python/py-btrace.h"
+#endif
+
 /* The target_ops of record-btrace.  */
 static struct target_ops record_btrace_ops;
 
@@ -2783,6 +2788,38 @@ record_btrace_stop_replaying_all (struct target_ops *self)
     record_btrace_stop_replaying (tp);
 }
 
+/* The to_record_python_interface method of target record-btrace.  */
+
+static int
+record_btrace_python_interface (struct target_ops *self,
+				struct record_python_interface *iface)
+{
+#if HAVE_PYTHON
+  struct thread_info *tp;
+  const struct btrace_config *conf;
+  int err;
+
+  gdb_assert (iface != NULL);
+
+  tp = find_thread_ptid (inferior_ptid);
+  if (tp == NULL)
+    return 0;
+
+  btrace_fetch (tp);
+
+  conf = btrace_conf (&tp->btrace);
+
+  recpy_btrace_python_interface (iface);
+
+  iface->format = btrace_format_short_string (conf->format);
+  iface->method = "btrace";
+
+  return 1;
+#else
+  return 0;
+#endif
+}
+
 /* The to_execution_direction target method.  */
 
 static enum exec_direction_kind
@@ -2836,6 +2873,7 @@ init_record_btrace_ops (void)
   ops->to_record_is_replaying = record_btrace_is_replaying;
   ops->to_record_will_replay = record_btrace_will_replay;
   ops->to_record_stop_replaying = record_btrace_stop_replaying_all;
+  ops->to_record_python_interface = record_btrace_python_interface;
   ops->to_xfer_partial = record_btrace_xfer_partial;
   ops->to_remove_breakpoint = record_btrace_remove_breakpoint;
   ops->to_insert_breakpoint = record_btrace_insert_breakpoint;
diff --git a/gdb/record-full.c b/gdb/record-full.c
index 803c5d4..d627774 100644
--- a/gdb/record-full.c
+++ b/gdb/record-full.c
@@ -38,6 +38,10 @@
 
 #include <signal.h>
 
+#if HAVE_PYTHON
+#include "python/py-record.h"
+#endif
+
 /* This module implements "target record-full", also known as "process
    record and replay".  This target sits on top of a "normal" target
    (a target that "has execution"), and provides a record and replay
@@ -1954,6 +1958,21 @@ record_full_stop_replaying (struct target_ops *self)
   record_full_goto_end (self);
 }
 
+/* The "to_record_python_interface" target method.  */
+
+static int
+record_full_python_interface (struct target_ops *self,
+				struct record_python_interface *iface)
+{
+#if HAVE_PYTHON
+  iface->method = "full";
+  iface->format = "full";
+  return 1;
+#else
+  return 0;
+#endif
+}
+
 static void
 init_record_full_ops (void)
 {
@@ -1997,6 +2016,7 @@ init_record_full_ops (void)
   record_full_ops.to_record_is_replaying = record_full_is_replaying;
   record_full_ops.to_record_will_replay = record_full_will_replay;
   record_full_ops.to_record_stop_replaying = record_full_stop_replaying;
+  record_full_ops.to_record_python_interface = record_full_python_interface;
   record_full_ops.to_goto_record_begin = record_full_goto_begin;
   record_full_ops.to_goto_record_end = record_full_goto_end;
   record_full_ops.to_goto_record = record_full_goto;
-- 
2.7.4

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

* [PATCH v3 5/8] python: Create Python bindings for record history.
  2016-11-21 15:49 [PATCH v3 0/8] Python bindings for btrace recordings Tim Wiederhake
                   ` (5 preceding siblings ...)
  2016-11-21 15:49 ` [PATCH v3 2/8] btrace: Export btrace_decode_error function Tim Wiederhake
@ 2016-11-21 15:49 ` Tim Wiederhake
  2016-11-29 15:48   ` Metzger, Markus T
  2016-11-21 15:49 ` [PATCH v3 4/8] Add record_start function Tim Wiederhake
  2016-11-29 15:49 ` [PATCH v3 0/8] Python bindings for btrace recordings Metzger, Markus T
  8 siblings, 1 reply; 20+ messages in thread
From: Tim Wiederhake @ 2016-11-21 15:49 UTC (permalink / raw)
  To: gdb-patches; +Cc: palves, markus.t.metzger

This patch adds three new functions to the gdb module in Python:
	- start_recording
	- stop_recording
	- current_recording
start_recording and current_recording return an object of the new type
gdb.Record, which can be used to access the recorded data.

2016-11-21  Tim Wiederhake  <tim.wiederhake@intel.com>

gdb/ChangeLog

	* Makefile.in (SUBDIR_PYTHON_OBS): Add python/py-record.o.
	(SUBDIR_PYTHON_SRCS): Add python/py-record.c.
	* python/py-record.c: New file.
	* python/py-record.h: New file.
	* python/python-internal.h (gdbpy_start_recording,
	gdbpy_current_recording, gdpy_stop_recording,
	gdbpy_initialize_record): New export.
	* python/python.c (_initialize_python): Add gdbpy_initialize_record.
	(python_GdbMethods): Add gdbpy_start_recording,
	gdbpy_current_recording and gdbpy_stop_recording.
	* target-debug.h (target_debug_print_struct_record_python_interface):
	New define.
	* target-delegates.c: Regenerate.
	* target.c (target_record_python_interface): New function.
	* target.h: Added struct record_python_interface forward declaration.
	Export target_record_python_interface.
	(struct target_ops): Add to_record_python_interface function.


---
 gdb/Makefile.in              |   2 +
 gdb/python/py-record.c       | 257 +++++++++++++++++++++++++++++++++++++++++++
 gdb/python/py-record.h       |  57 ++++++++++
 gdb/python/python-internal.h |   5 +
 gdb/python/python.c          |  13 +++
 gdb/target-debug.h           |   2 +
 gdb/target-delegates.c       |  33 ++++++
 gdb/target.c                 |   7 ++
 gdb/target.h                 |  10 ++
 9 files changed, 386 insertions(+)
 create mode 100644 gdb/python/py-record.c
 create mode 100644 gdb/python/py-record.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index b68cf58..3340b5e 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -403,6 +403,7 @@ SUBDIR_PYTHON_OBS = \
 	py-param.o \
 	py-prettyprint.o \
 	py-progspace.o \
+	py-record.o \
 	py-signalevent.o \
 	py-stopevent.o \
 	py-symbol.o \
@@ -443,6 +444,7 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-param.c \
 	python/py-prettyprint.c \
 	python/py-progspace.c \
+	python/py-record.c \
 	python/py-signalevent.c \
 	python/py-stopevent.c \
 	python/py-symbol.c \
diff --git a/gdb/python/py-record.c b/gdb/python/py-record.c
new file mode 100644
index 0000000..e25f6ca
--- /dev/null
+++ b/gdb/python/py-record.c
@@ -0,0 +1,257 @@
+/* Python interface to record targets.
+
+   Copyright 2016 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <tim.wiederhake@intel.com>
+
+   This file is part of GDB.
+
+   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 "defs.h"
+#include "gdbcmd.h"
+#include "record.h"
+#include "record-btrace.h"
+#include "record-full.h"
+#include "py-record.h"
+#include "target.h"
+
+/* Python Record object.  */
+
+typedef struct
+{
+  PyObject_HEAD
+
+  struct record_python_interface interface;
+} recpy_record_object;
+
+/* Python Record type.  */
+
+static PyTypeObject recpy_record_type = {
+  PyVarObject_HEAD_INIT (NULL, 0)
+};
+
+/* Implementation of record.method.  */
+
+static PyObject *
+recpy_method (PyObject *self, void* closure)
+{
+  recpy_record_object *obj = (recpy_record_object *) self;
+
+  if (obj->interface.method == NULL)
+    return PyString_FromString (_("unknown"));
+
+  return PyString_FromString (obj->interface.method);
+}
+
+/* Implementation of record.format.  */
+
+static PyObject *
+recpy_format (PyObject *self, void* closure)
+{
+  recpy_record_object *obj = (recpy_record_object *) self;
+
+  if (obj->interface.format == NULL)
+    return PyString_FromString (_("unknown"));
+
+  return PyString_FromString (obj->interface.format);
+}
+
+/* Implementation of record.goto (instruction) -> None.  */
+
+static PyObject *
+recpy_goto (PyObject *self, PyObject *value)
+{
+  recpy_record_object *obj = (recpy_record_object *) self;
+
+  if (obj->interface.goto_position == NULL)
+    return PyErr_Format (PyExc_NotImplementedError, _("Not implemented."));
+
+  return obj->interface.goto_position (self, value);
+}
+
+/* Implementation of record.replay_position [instruction]  */
+
+static PyObject *
+recpy_replay_position (PyObject *self, void *closure)
+{
+  recpy_record_object *obj = (recpy_record_object *) self;
+
+  if (obj->interface.replay_position == NULL)
+    return PyErr_Format (PyExc_NotImplementedError, _("Not implemented."));
+
+  return obj->interface.replay_position (self, closure);
+}
+
+/* Implementation of record.instruction_history [list].  */
+
+static PyObject *
+recpy_instruction_history (PyObject *self, void* closure)
+{
+  recpy_record_object *obj = (recpy_record_object *) self;
+
+  if (obj->interface.instruction_history == NULL)
+    return PyErr_Format (PyExc_NotImplementedError, _("Not implemented."));
+
+  return obj->interface.instruction_history (self, closure);
+}
+
+/* Implementation of record.function_call_history [list].  */
+
+static PyObject *
+recpy_function_call_history (PyObject *self, void* closure)
+{
+  recpy_record_object *obj = (recpy_record_object *) self;
+
+  if (obj->interface.function_call_history == NULL)
+    return PyErr_Format (PyExc_NotImplementedError, _("Not implemented."));
+
+  return obj->interface.function_call_history (self, closure);
+}
+
+/* Implementation of record.begin [instruction].  */
+
+static PyObject *
+recpy_begin (PyObject *self, void* closure)
+{
+  recpy_record_object *obj = (recpy_record_object *) self;
+
+  if (obj->interface.begin == NULL)
+    return PyErr_Format (PyExc_NotImplementedError, _("Not implemented."));
+
+  return obj->interface.begin (self, closure);
+}
+
+/* Implementation of record.end [instruction].  */
+
+static PyObject *
+recpy_end (PyObject *self, void* closure)
+{
+  recpy_record_object *obj = (recpy_record_object *) self;
+
+  if (obj->interface.end == NULL)
+    return PyErr_Format (PyExc_NotImplementedError, _("Not implemented."));
+
+  return obj->interface.end (self, closure);
+}
+
+/* Record method list.  */
+
+static PyMethodDef recpy_record_methods[] = {
+  { "goto", recpy_goto, METH_VARARGS,
+    "goto (instruction|function_call) -> None.\n\
+Rewind to given location."},
+  { NULL }
+};
+
+/* Record member list.  */
+
+static PyGetSetDef recpy_record_getset[] = {
+  { "method", recpy_method, NULL, "Current recording method.", NULL },
+  { "format", recpy_format, NULL, "Current recording format.", NULL },
+  { "replay_position", recpy_replay_position, NULL, "Current replay position.",
+    NULL },
+  { "instruction_history", recpy_instruction_history, NULL,
+    "List of instructions in current recording.", NULL },
+  { "function_call_history", recpy_function_call_history, NULL,
+    "List of function calls in current recording.", NULL },
+  { "begin", recpy_begin, NULL,
+    "First instruction in current recording.", NULL },
+  { "end", recpy_end, NULL,
+    "One past the last instruction in current recording.  This is typically \
+the current instruction and is used for e.g. record.goto (record.end).", NULL },
+  { NULL }
+};
+
+/* Sets up the record API in the gdb module.  */
+
+int
+gdbpy_initialize_record (void)
+{
+  recpy_record_type.tp_new = PyType_GenericNew;
+  recpy_record_type.tp_flags = Py_TPFLAGS_DEFAULT;
+  recpy_record_type.tp_basicsize = sizeof (recpy_record_object);
+  recpy_record_type.tp_name = "gdb.Record";
+  recpy_record_type.tp_doc = "GDB record object";
+  recpy_record_type.tp_methods = recpy_record_methods;
+  recpy_record_type.tp_getset = recpy_record_getset;
+
+  return PyType_Ready (&recpy_record_type);
+}
+
+/* Implementation of gdb.start_recording (method) -> gdb.Record.  */
+
+PyObject *
+gdbpy_start_recording (PyObject *self, PyObject *args)
+{
+  const char *method = NULL;
+  const char *format = NULL;
+
+  if (!PyArg_ParseTuple (args, "|ss", &method, &format))
+    return NULL;
+
+  TRY
+    {
+      record_start (method, format, 0);
+      return gdbpy_current_recording (self, args);
+    }
+  CATCH (except, RETURN_MASK_ALL)
+    {
+      gdbpy_convert_exception (except);
+    }
+  END_CATCH
+
+  return NULL;
+}
+
+/* Implementation of gdb.current_recording (self) -> gdb.Record.  */
+
+PyObject *
+gdbpy_current_recording (PyObject *self, PyObject *args)
+{
+  recpy_record_object* obj;
+
+  obj = PyObject_New (recpy_record_object, &recpy_record_type);
+  if (obj == NULL)
+    return NULL;
+
+  memset (&obj->interface, 0, sizeof (struct record_python_interface));
+
+  if (!target_record_python_interface (&obj->interface))
+    {
+      Py_DecRef ((PyObject *) obj);
+      Py_RETURN_NONE;
+    }
+
+  return (PyObject *) obj;
+}
+
+/* Implementation of gdb.stop_recording (self) -> None.  */
+
+PyObject *
+gdbpy_stop_recording (PyObject *self, PyObject *args)
+{
+  TRY
+    {
+      execute_command_to_string ("record stop", 0);
+      Py_RETURN_NONE;
+    }
+  CATCH (except, RETURN_MASK_ALL)
+    {
+      gdbpy_convert_exception (except);
+    }
+  END_CATCH
+
+  return NULL;
+}
diff --git a/gdb/python/py-record.h b/gdb/python/py-record.h
new file mode 100644
index 0000000..fb56182
--- /dev/null
+++ b/gdb/python/py-record.h
@@ -0,0 +1,57 @@
+/* Python interface to record targets.
+
+   Copyright 2016 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp. <tim.wiederhake@intel.com>
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#ifndef GDB_PY_RECORD_H
+#define GDB_PY_RECORD_H
+
+#include "python-internal.h"
+
+/* Record interface for Python bindings.  Any field may be NULL if unknown
+   or not supported.  */
+
+struct record_python_interface
+{
+  /* Recording method, e.g. "full" or "btrace".  */
+  const char *method;
+
+  /* Recording format, e.g. "bts" or "pt".  */
+  const char *format;
+
+  /* Implementation of gdb.Record.goto (instruction) -> None.  */
+  binaryfunc goto_position;
+
+  /* Implementation of gdb.Record.replay_position [instruction]  */
+  getter replay_position;
+
+  /* Implementation of gdb.Record.instruction_history [list].  */
+  getter instruction_history;
+
+  /* Implementation of gdb.Record.function_call_history [list].  */
+  getter function_call_history;
+
+  /* Implementation of gdb.Record.begin [instruction].  */
+  getter begin;
+
+  /* Implementation of gdb.Record.end [instruction].  */
+  getter end;
+};
+
+#endif /* GDB_PY_RECORD_H */
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 5f0abf8..e2bb9a8 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -362,6 +362,9 @@ PyObject *gdbpy_frame_stop_reason_string (PyObject *, PyObject *);
 PyObject *gdbpy_lookup_symbol (PyObject *self, PyObject *args, PyObject *kw);
 PyObject *gdbpy_lookup_global_symbol (PyObject *self, PyObject *args,
 				      PyObject *kw);
+PyObject *gdbpy_start_recording (PyObject *self, PyObject *args);
+PyObject *gdbpy_current_recording (PyObject *self, PyObject *args);
+PyObject *gdbpy_stop_recording (PyObject *self, PyObject *args);
 PyObject *gdbpy_newest_frame (PyObject *self, PyObject *args);
 PyObject *gdbpy_selected_frame (PyObject *self, PyObject *args);
 PyObject *gdbpy_block_for_pc (PyObject *self, PyObject *args);
@@ -429,6 +432,8 @@ int gdbpy_initialize_values (void)
   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 int gdbpy_initialize_frames (void)
   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+int gdbpy_initialize_record (void)
+  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 int gdbpy_initialize_symtabs (void)
   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 int gdbpy_initialize_commands (void)
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 321479b..7f031ae 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1786,6 +1786,7 @@ message == an error message without a stack will be printed."),
       || gdbpy_initialize_values () < 0
       || gdbpy_initialize_frames () < 0
       || gdbpy_initialize_commands () < 0
+      || gdbpy_initialize_record () < 0
       || gdbpy_initialize_symbols () < 0
       || gdbpy_initialize_symtabs () < 0
       || gdbpy_initialize_blocks () < 0
@@ -1998,6 +1999,18 @@ Return the selected frame object." },
     "stop_reason_string (Integer) -> String.\n\
 Return a string explaining unwind stop reason." },
 
+  { "start_recording", gdbpy_start_recording, METH_VARARGS,
+    "start_recording ([method] [, format]) -> gdb.Record.\n\
+Start recording with the given method.  If no method is given, will fall back\n\
+to the system default method.  If no format is given, will fall back to the\n\
+default format for the given method."},
+  { "current_recording", gdbpy_current_recording, METH_NOARGS,
+    "current_recording () -> gdb.Record.\n\
+Return current recording object." },
+  { "stop_recording", gdbpy_stop_recording, METH_NOARGS,
+    "stop_recording () -> None.\n\
+Stop current recording." },
+
   { "lookup_type", (PyCFunction) gdbpy_lookup_type,
     METH_VARARGS | METH_KEYWORDS,
     "lookup_type (name [, block]) -> type\n\
diff --git a/gdb/target-debug.h b/gdb/target-debug.h
index ef7e14d..8790016 100644
--- a/gdb/target-debug.h
+++ b/gdb/target-debug.h
@@ -160,6 +160,8 @@
   target_debug_do_print (host_address_to_string (X))
 #define target_debug_print_enum_remove_bp_reason(X) \
   target_debug_do_print (plongest (X))
+#define target_debug_print_struct_record_python_interface_p(X) \
+  target_debug_do_print (host_address_to_string (X))
 
 static void
 target_debug_print_struct_target_waitstatus_p (struct target_waitstatus *status)
diff --git a/gdb/target-delegates.c b/gdb/target-delegates.c
index 73e45dd..66e137b 100644
--- a/gdb/target-delegates.c
+++ b/gdb/target-delegates.c
@@ -3995,6 +3995,35 @@ debug_call_history_range (struct target_ops *self, ULONGEST arg1, ULONGEST arg2,
 }
 
 static int
+delegate_record_python_interface (struct target_ops *self, struct record_python_interface *arg1)
+{
+  self = self->beneath;
+  return self->to_record_python_interface (self, arg1);
+}
+
+static int
+tdefault_record_python_interface (struct target_ops *self, struct record_python_interface *arg1)
+{
+  return 0;
+}
+
+static int
+debug_record_python_interface (struct target_ops *self, struct record_python_interface *arg1)
+{
+  int result;
+  fprintf_unfiltered (gdb_stdlog, "-> %s->to_record_python_interface (...)\n", debug_target.to_shortname);
+  result = debug_target.to_record_python_interface (&debug_target, arg1);
+  fprintf_unfiltered (gdb_stdlog, "<- %s->to_record_python_interface (", debug_target.to_shortname);
+  target_debug_print_struct_target_ops_p (&debug_target);
+  fputs_unfiltered (", ", gdb_stdlog);
+  target_debug_print_struct_record_python_interface_p (arg1);
+  fputs_unfiltered (") = ", gdb_stdlog);
+  target_debug_print_int (result);
+  fputs_unfiltered ("\n", gdb_stdlog);
+  return result;
+}
+
+static int
 delegate_augmented_libraries_svr4_read (struct target_ops *self)
 {
   self = self->beneath;
@@ -4418,6 +4447,8 @@ install_delegators (struct target_ops *ops)
     ops->to_call_history_from = delegate_call_history_from;
   if (ops->to_call_history_range == NULL)
     ops->to_call_history_range = delegate_call_history_range;
+  if (ops->to_record_python_interface == NULL)
+    ops->to_record_python_interface = delegate_record_python_interface;
   if (ops->to_augmented_libraries_svr4_read == NULL)
     ops->to_augmented_libraries_svr4_read = delegate_augmented_libraries_svr4_read;
   if (ops->to_get_unwinder == NULL)
@@ -4581,6 +4612,7 @@ install_dummy_methods (struct target_ops *ops)
   ops->to_call_history = tdefault_call_history;
   ops->to_call_history_from = tdefault_call_history_from;
   ops->to_call_history_range = tdefault_call_history_range;
+  ops->to_record_python_interface = tdefault_record_python_interface;
   ops->to_augmented_libraries_svr4_read = tdefault_augmented_libraries_svr4_read;
   ops->to_get_unwinder = tdefault_get_unwinder;
   ops->to_get_tailcall_unwinder = tdefault_get_tailcall_unwinder;
@@ -4739,6 +4771,7 @@ init_debug_target (struct target_ops *ops)
   ops->to_call_history = debug_call_history;
   ops->to_call_history_from = debug_call_history_from;
   ops->to_call_history_range = debug_call_history_range;
+  ops->to_record_python_interface = debug_record_python_interface;
   ops->to_augmented_libraries_svr4_read = debug_augmented_libraries_svr4_read;
   ops->to_get_unwinder = debug_get_unwinder;
   ops->to_get_tailcall_unwinder = debug_get_tailcall_unwinder;
diff --git a/gdb/target.c b/gdb/target.c
index 246d292..416a6ab 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -3812,6 +3812,13 @@ target_record_stop_replaying (void)
 }
 
 /* See target.h.  */
+int
+target_record_python_interface (struct record_python_interface *iface)
+{
+  return current_target.to_record_python_interface (&current_target, iface);
+}
+
+/* See target.h.  */
 
 void
 target_goto_record_begin (void)
diff --git a/gdb/target.h b/gdb/target.h
index a54b3db..70f10fb 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -39,6 +39,7 @@ struct traceframe_info;
 struct expression;
 struct dcache_struct;
 struct inferior;
+struct record_python_interface;
 
 #include "infrun.h" /* For enum exec_direction_kind.  */
 #include "breakpoint.h" /* For enum bptype.  */
@@ -1232,6 +1233,12 @@ struct target_ops
 				   ULONGEST begin, ULONGEST end, int flags)
       TARGET_DEFAULT_NORETURN (tcomplain ());
 
+    /* Fill in the record python interface object and return non-zero.
+       Return zero on failure or if no recording is active.  */
+    int (*to_record_python_interface) (struct target_ops *,
+				       struct record_python_interface *)
+       TARGET_DEFAULT_RETURN (0);
+
     /* Nonzero if TARGET_OBJECT_LIBRARIES_SVR4 may be read with a
        non-empty annex.  */
     int (*to_augmented_libraries_svr4_read) (struct target_ops *)
@@ -2528,6 +2535,9 @@ 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_record_python_interface.  */
+extern int target_record_python_interface (struct record_python_interface *);
+
 /* See to_prepare_to_generate_core.  */
 extern void target_prepare_to_generate_core (void);
 
-- 
2.7.4

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

* RE: [PATCH v3 1/8] btrace: Count gaps as one instruction explicitly.
  2016-11-21 15:49 ` [PATCH v3 1/8] btrace: Count gaps as one instruction explicitly Tim Wiederhake
@ 2016-11-29 15:47   ` Metzger, Markus T
  0 siblings, 0 replies; 20+ messages in thread
From: Metzger, Markus T @ 2016-11-29 15:47 UTC (permalink / raw)
  To: Wiederhake, Tim, gdb-patches; +Cc: palves

> -----Original Message-----
> From: Wiederhake, Tim
> Sent: Monday, November 21, 2016 4:49 PM
> To: gdb-patches@sourceware.org
> Cc: palves@redhat.com; Metzger, Markus T <markus.t.metzger@intel.com>
> Subject: [PATCH v3 1/8] btrace: Count gaps as one instruction explicitly.
> 
> This gives all instructions, including gaps, a unique number.  Add a function
> to retrieve the error code if a btrace instruction iterator points to an
> invalid instruction.
> 
> 2016-11-21  Tim Wiederhake  <tim.wiederhake@intel.com>
> 
> gdb/ChangeLog:
> 
> 	* btrace.c (ftrace_call_num_insn, btrace_insn_get_error): New function.
> 	(ftrace_new_function, btrace_insn_number, btrace_insn_cmp,
> 	btrace_find_insn_by_number): Remove special case for gaps.
> 	* btrace.h (btrace_insn_get_error): New export.
> 	(btrace_insn_number, btrace_find_insn_by_number): Adjust comment.
> 	* record-btrace.c (btrace_insn_history): Print number for gaps.
> 	(record_btrace_info, record_btrace_goto): Handle gaps.


Looks good to me.

Thanks,
Markus.

Intel Deutschland GmbH
Registered Address: Am Campeon 10-12, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Christin Eisenschmid, Christian Lamprechter
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

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

* RE: [PATCH v3 4/8] Add record_start function.
  2016-11-21 15:49 ` [PATCH v3 4/8] Add record_start function Tim Wiederhake
@ 2016-11-29 15:47   ` Metzger, Markus T
  0 siblings, 0 replies; 20+ messages in thread
From: Metzger, Markus T @ 2016-11-29 15:47 UTC (permalink / raw)
  To: Wiederhake, Tim, gdb-patches; +Cc: palves

> -----Original Message-----
> From: Wiederhake, Tim
> Sent: Monday, November 21, 2016 4:49 PM
> To: gdb-patches@sourceware.org
> Cc: palves@redhat.com; Metzger, Markus T <markus.t.metzger@intel.com>
> Subject: [PATCH v3 4/8] Add record_start function.
> 
> 2016-11-21  Tim Wiederhake  <tim.wiederhake@intel.com>
> 
> gdb/ChangeLog
> 
> 	* record.h (record_start): New export.
> 	* record.c (record_start): New function.


> +void
> +record_start (const char *method, const char *format, int from_tty)
> +{
> +  if (method == NULL)
> +    {
> +      if (format == NULL)
> +	return execute_command ("record", from_tty);
> +    }
> +  else if (strcmp (method, "full") == 0)
> +    {
> +      if (format == NULL)
> +	return execute_command ("record full", from_tty);
> +    }
> +  else if (strcmp (method, "btrace") == 0)
> +    {
> +      if (format == NULL)
> +	return execute_command ("record btrace", from_tty);
> +      if (strcmp (format, "bts") == 0)
> +	return execute_command ("record btrace bts", from_tty);
> +      if (strcmp (format, "pt") == 0)
> +	return execute_command ("record btrace pt", from_tty);
> +    }
> +
> +  error (_("Invalid argument."));

Should we split this into "Invalid recording method" and "Invalid
recording format"?

 
> +/* Internal function that starts recording with the given METHOD and FORMAT.
> +   NULL means default method or format.  */

What does 'internal' mean in this context?

> +extern void record_start (const char *method, const char *format,
> +			  int from_tty);
> +
>  #endif /* _RECORD_H_ */
> --
> 2.7.4

thanks,
Markus.

Intel Deutschland GmbH
Registered Address: Am Campeon 10-12, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Christin Eisenschmid, Christian Lamprechter
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

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

* RE: [PATCH v3 3/8] btrace: Use binary search to find instruction.
  2016-11-21 15:49 ` [PATCH v3 3/8] btrace: Use binary search to find instruction Tim Wiederhake
@ 2016-11-29 15:47   ` Metzger, Markus T
  0 siblings, 0 replies; 20+ messages in thread
From: Metzger, Markus T @ 2016-11-29 15:47 UTC (permalink / raw)
  To: Wiederhake, Tim, gdb-patches; +Cc: palves

> -----Original Message-----
> From: Wiederhake, Tim
> Sent: Monday, November 21, 2016 4:49 PM
> To: gdb-patches@sourceware.org
> Cc: palves@redhat.com; Metzger, Markus T <markus.t.metzger@intel.com>
> Subject: [PATCH v3 3/8] btrace: Use binary search to find instruction.
> 
> Currently, btrace_find_insn_by_number will iterate over all function call
> segments to find the one that contains the needed instruction.  This linear
> search is too slow for the upcoming Python bindings that will use this
> function to access instructions.  This patch introduces a vector in struct
> btrace_thread_info that holds pointers to all recorded function segments and
> allows to use binary search.
> 
> The proper solution is to turn the underlying tree into a vector of objects
> and use indices for access.  This requires more work.  A patch set is
> currently being worked on and will be published later.
> 
> 2016-11-21  Tim Wiederhake  <tim.wiederhake@intel.com>
> 
> gdb/ChangeLog:
> 	* btrace.c (btrace_fetch): Copy function call segments pointer
> 	into a vector.
> 	(btrace_clear): Clear the vector.
> 	(btrace_find_insn_by_number): Use binary search to find the correct
> 	function call segment.
> 	* btrace.h (brace_fun_p): New typedef.
> 	(struct btrace_thread_info) <functions>: New field.


> -  if (bfun == NULL)
> +  lower = 0;
> +  bfun = VEC_index (btrace_fun_p, btinfo->functions, lower);
> +  if (number < bfun->insn_offset)
>      return 0;
> 
> -  if (bfun->insn_offset + ftrace_call_num_insn (bfun) <= number)
> +  upper = VEC_length (btrace_fun_p, btinfo->functions) - 1;
> +  bfun = VEC_index (btrace_fun_p, btinfo->functions, upper);
> +  if (number >= bfun->insn_offset + ftrace_call_num_insn (bfun))
>      return 0;
> 
> +  average = lower + (upper - lower) / 2;
> +  bfun = VEC_index (btrace_fun_p, btinfo->functions, average);
> +
> +  while ((number >= bfun->insn_offset + ftrace_call_num_insn (bfun))
> +      || (number < bfun->insn_offset))
> +    {
> +      if (number < bfun->insn_offset)
> +	upper = average - 1;
> +      else
> +	lower = average + 1;
> +
> +      average = lower + (upper - lower) / 2;
> +      bfun = VEC_index (btrace_fun_p, btinfo->functions, average);
> +    }
> +
>    it->function = bfun;
>    it->index = number - bfun->insn_offset;

This could be simplified a bit.  Instead of checking the very small (i.e. zero
in practice) and very big numbers upfront, we can let them fall through the
binary search loop - both cases are unlikely.

Also the loop could be simplified by merging the termination check and the
check in the loop body so we end up with only one of the checks for low
numbers.

The algorithm looks good, however, and IIUC we're going to change that
again with an in-progress patch series.  So we might as well leave it as-is
and consider this in the final version.


Looks good to me.

Thanks,
Markus.

Intel Deutschland GmbH
Registered Address: Am Campeon 10-12, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Christin Eisenschmid, Christian Lamprechter
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

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

* RE: [PATCH v3 2/8] btrace: Export btrace_decode_error function.
  2016-11-21 15:49 ` [PATCH v3 2/8] btrace: Export btrace_decode_error function Tim Wiederhake
@ 2016-11-29 15:47   ` Metzger, Markus T
  0 siblings, 0 replies; 20+ messages in thread
From: Metzger, Markus T @ 2016-11-29 15:47 UTC (permalink / raw)
  To: Wiederhake, Tim, gdb-patches; +Cc: palves

> -----Original Message-----
> From: Wiederhake, Tim
> Sent: Monday, November 21, 2016 4:49 PM
> To: gdb-patches@sourceware.org
> Cc: palves@redhat.com; Metzger, Markus T <markus.t.metzger@intel.com>
> Subject: [PATCH v3 2/8] btrace: Export btrace_decode_error function.
> 
> 2016-11-21  Tim Wiederhake  <tim.wiederhake@intel.com>
> 
> gdb/ChangeLog:
> 
> 	* record-btrace.c (btrace_ui_out_decode_error): Move most of it ...
> 	* btrace.c (btrace_decode_error): ... here.  New function.
> 	* btrace.h (btrace_decode_error): New export.

> +const char *
> +btrace_decode_error (enum btrace_format format, int errcode)
> +{
> +  switch (format)
> +  {
> +    case BTRACE_FORMAT_BTS:

The { is indented and thus goes onto the same column than the case.


Looks good to me, otherwise.

Thanks,
Markus.

Intel Deutschland GmbH
Registered Address: Am Campeon 10-12, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Christin Eisenschmid, Christian Lamprechter
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

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

* RE: [PATCH v3 8/8] Add documentation for new instruction record Python bindings.
  2016-11-21 15:49 ` [PATCH v3 8/8] Add documentation for new instruction record Python bindings Tim Wiederhake
@ 2016-11-29 15:48   ` Metzger, Markus T
  2016-11-29 17:32     ` Eli Zaretskii
  0 siblings, 1 reply; 20+ messages in thread
From: Metzger, Markus T @ 2016-11-29 15:48 UTC (permalink / raw)
  To: Wiederhake, Tim, gdb-patches; +Cc: palves

> -----Original Message-----
> From: Wiederhake, Tim
> Sent: Monday, November 21, 2016 4:49 PM
> To: gdb-patches@sourceware.org
> Cc: palves@redhat.com; Metzger, Markus T <markus.t.metzger@intel.com>
> Subject: [PATCH v3 8/8] Add documentation for new instruction record Python
> bindings.
> 
> 2016-11-21  Tim Wiederhake  <tim.wiederhake@intel.com>
> 
> gdb/ChangeLog:
> 
> 	* NEWS: Add record Python bindings entry.
> 
> gdb/doc/ChangeLog:
> 
> 	* python.texi (Recordings In Python): New section.



> +@defvar Record.format
> +A string with the current recording format, e.g.@: @code{bt} or
> +@code{pts}.  Can be empty.

This should be "bts" instead of "pts".  IIRC the patches don't really allow
it to be empty.


> +@end defvar
> +
> +@defvar Record.begin
> +A format specific instruction object representing the first instruction

Should this be "method-specific" instead of "format-specific"?


> +The attributes and methods of instruction objects depend on the current
> +recording format.  Currently, only btrace instructions are supported.

Should this be "method" instead of "format"?


> +@defvar BtraceInstruction.number
> +An integer identifying this instruction.  @var{number} corresponds to
> +the numbers seen in @code{record instruction-history}
> +(@pxref{Process Record and Replay}).
> +@end defvar
> +
> +@defvar BtraceInstruction.error
> +An integer identifying the error code for gaps in the record.

Should this be "history" instead of "record"?


> +@defvar BtraceInstruction.decoded
> +A human readable string with the decoded instruction.  Contains the

Should this be "disassembled" instead of "decoded"?


> +@defvar BtraceFunctionCall.instructions
> +A list of instructions associated with this function call.

Should this be "a list of gdb.BtraceInstruction objects"?


> +The following example demonstrates the usage of these objects and
> +functions to create a function that will rewind a record to the last
> +time a function in a different file was executed.  This would typically
> +be used to track the execution of user provided callback functions in a
> +library which typically are not visible in a back trace.
> +
> +@smallexample
> +def bringback ():
> +    rec = gdb.current_recording ()
> +    if not rec:
> +        return
> +
> +    insn = rec.instruction_history
> +    if len (insn) == 0:
> +        return
> +
> +    try:
> +        position = insn.index (rec.replay_position)
> +    except:
> +        position = -1
> +    try:
> +        filename = insn[position].symbol.symtab.fullname ()
> +    except:
> +        filename = None
> +
> +    for i in reversed (insn[:position]):
> +	try:
> +            current = i.symbol.symtab.fullname ()
> +	except:
> +            current = None
> +
> +        if filename == current:
> +            continue
> +
> +        rec.goto (i)
> +        return
> +@end smallexample
> +
> +Another possible application is to write a function that counts the
> +number of code executions in a given line range.  This line range can
> +contain parts of functions or span across several functions and is not
> +limited to be contiguous.
> +
> +@smallexample
> +def countrange (filename, linerange):
> +    count = 0
> +
> +    def filter_only (file_name):
> +        for call in gdb.current_recording ().function_call_history:
> +            try:
> +                if file_name in call.symbol.symtab.fullname ():
> +                    yield call
> +            except:
> +                pass
> +
> +    for c in filter_only (filename):
> +        for i in c.instructions:
> +            try:
> +                if i.symbol.line in linerange:
> +                    count += 1
> +                    break;
> +            except:
> +                    pass
> +
> +    return count
> +@end smallexample
> +
>  @node Commands In Python
>  @subsubsection Commands In Python

The examples are really helpful.

The patch looks good to me but we need Eli to approve it.

Thanks,
Markus.
Intel Deutschland GmbH
Registered Address: Am Campeon 10-12, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Christin Eisenschmid, Christian Lamprechter
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

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

* RE: [PATCH v3 7/8] python: Add tests for record Python bindings
  2016-11-21 15:49 ` [PATCH v3 7/8] python: Add tests for record Python bindings Tim Wiederhake
@ 2016-11-29 15:48   ` Metzger, Markus T
  0 siblings, 0 replies; 20+ messages in thread
From: Metzger, Markus T @ 2016-11-29 15:48 UTC (permalink / raw)
  To: Wiederhake, Tim, gdb-patches; +Cc: palves

> -----Original Message-----
> From: Wiederhake, Tim
> Sent: Monday, November 21, 2016 4:49 PM
> To: gdb-patches@sourceware.org
> Cc: palves@redhat.com; Metzger, Markus T <markus.t.metzger@intel.com>
> Subject: [PATCH v3 7/8] python: Add tests for record Python bindings
> 
> 2016-11-21  Tim Wiederhake  <tim.wiederhake@intel.com>
> 
> gdb/testsuite/ChangeLog:
> 
>         * gdb.python/py-record-btrace.c: New file.
>         * gdb.python/py-record-btrace.exp: New file.

The tests cover start/stop for record-full.  Would it make sense to move
them to a separate test file?


> +with_test_prefix "preopened record full" {
> +    gdb_test_no_output "record full"
> +    gdb_test "python print(gdb.current_recording().method)" "full"
> +    gdb_test "python print(gdb.current_recording().format)" "full"

The "info record" command does not list a format for method "full".

IIRC we don't return None so in this regard, "full" is certainly better
than "unknown".


The patch looks good to me but we need to find a maintainer to approve it.

thanks,
Markus.
Intel Deutschland GmbH
Registered Address: Am Campeon 10-12, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Christin Eisenschmid, Christian Lamprechter
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

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

* RE: [PATCH v3 6/8] python: Implement btrace Python bindings for record history.
  2016-11-21 15:49 ` [PATCH v3 6/8] python: Implement btrace Python bindings for record history Tim Wiederhake
@ 2016-11-29 15:48   ` Metzger, Markus T
  0 siblings, 0 replies; 20+ messages in thread
From: Metzger, Markus T @ 2016-11-29 15:48 UTC (permalink / raw)
  To: Wiederhake, Tim, gdb-patches; +Cc: palves

> -----Original Message-----
> From: Wiederhake, Tim
> Sent: Monday, November 21, 2016 4:49 PM
> To: gdb-patches@sourceware.org
> Cc: palves@redhat.com; Metzger, Markus T <markus.t.metzger@intel.com>
> Subject: [PATCH v3 6/8] python: Implement btrace Python bindings for record
> history.
> 
> This patch implements the gdb.Record Python object methods and fields for
> record target btrace.  Additionally, add a stub for record target full.
> 
> 2016-11-21  Tim Wiederhake  <tim.wiederhake@intel.com>
> 
> gdb/ChangeLog:
> 
> 	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-btrace.o.
> 	(SUBDIR_PYTHON_SRCS): Add py-btrace.c.
> 	* python/py-btrace.c: New file.
> 	* python/py-btrace.h: New file.
> 	* python/python-internal.h (gdbpy_initialize_btrace): New export.
> 	* python/python.c (_initialize_python): Add gdbpy_initialize_btrace.
> 	* record-btrace.c: Add conditional includes for python/py-record.h
> 	and python/py-btrace.h
> 	(record_btrace_python_interface): New function.
> 	(init_record_btrace_ops): Add record_btrace_python_interface.
> 	* record-full.c: Add conditional include for python/py-record.h.
> 	(record_full_python_interface): New function.
> 	(init_record_full_ops): Add record_full_python_interface.


> +/* Python object for btrace records.  This can either be an instruction or a
> +   function call segment, depending on the chosen type.  */

That's not the object for a btrace recording, right?  Maybe just omit the first sentence
and say right away that this can either be a btrace instruction or function segment.


> +  if (!PySlice_Check (value))
> +    return PyErr_Format (PyExc_TypeError, _("Index must be int or slice."));
> +
> +  if (0 != PySlice_GetIndicesEx (BTPY_PYSLICE (value), length, &start, &stop,
> +				 &step, &slicelength))
> +    return NULL;
> +
> +  return btpy_list_new (obj->ptid, obj->first + obj->step * start,
> +			obj->first + obj->step * stop, obj->step * step,
> +			obj->element_type);

Is PySlice_GetIndicesEx checking that the above operations can't overflow?


> +/* Implementation of BtraceList.count (self, value) -> int.  */
> +
> +static PyObject *
> +btpy_list_count (PyObject *self, PyObject *value)
> +{
> +  const LONGEST index = btpy_list_position (self, value);
> +
> +  if (index < 0)
> +    return PyInt_FromLong (0);
> +  else
> +    return PyInt_FromLong (1);
> +}

Hmmm, we need instruction instances to be unique for "record goto".  OTOH it
might be nice to count the number of occurrences of a given instruction at a given
PC.  Would it make sense to define equality as "same PC" for the purpose of count ()?


> +/* Implementation of
> +   BtraceRecord.instruction_history [list].  */
> +
> +static PyObject *
> +recpy_bt_instruction_history (PyObject* self, void* closure)
> +{
> +  struct thread_info * const tinfo = find_thread_ptid (inferior_ptid);
> +  struct btrace_insn_iterator iterator;
> +  unsigned long first = 0;
> +  unsigned long last = 0;
> +
> +   if (tinfo == NULL)
> +     Py_RETURN_NONE;
> +
> +   btrace_fetch (tinfo);
> +
> +   if (btrace_is_empty (tinfo))
> +     Py_RETURN_NONE;
> +
> +   btrace_insn_begin (&iterator, &tinfo->btrace);
> +   first = btrace_insn_number (&iterator);
> +
> +   btrace_insn_end (&iterator, &tinfo->btrace);
> +   last = btrace_insn_number (&iterator);
> +
> +   return btpy_list_new (inferior_ptid, first, last, 1, &btpy_insn_type);

IIRC last was included in the list.  The btrace end iterator points one past the
end of the list.  Is this intentional to include the current instruction?


> +/* Implementation of
> +   BtraceRecord.function_call_history [list].  */
> +
> +static PyObject *
> +recpy_bt_function_call_history (PyObject* self, void* closure)
> +{
> +    struct thread_info * const tinfo = find_thread_ptid (inferior_ptid);
> +    struct btrace_call_iterator iterator;
> +    unsigned long first = 0;
> +    unsigned long last = 0;
> +
> +    if (tinfo == NULL)
> +      Py_RETURN_NONE;
> +
> +    btrace_fetch (tinfo);
> +
> +    if (btrace_is_empty (tinfo))
> +      Py_RETURN_NONE;
> +
> +    btrace_call_begin (&iterator, &tinfo->btrace);
> +    first = btrace_call_number (&iterator);
> +
> +    btrace_call_end (&iterator, &tinfo->btrace);
> +    last = btrace_call_number (&iterator);
> +
> +    return btpy_list_new (inferior_ptid, first, last, 1, &btpy_call_type);

Same here.


> +struct PyGetSetDef btpy_insn_getset[] = {
> +  { "number", btpy_number, NULL, "instruction number", NULL},
> +  { "error", btpy_insn_error, NULL, "error number for gaps", NULL},
> +  { "symbol", btpy_insn_symbol, NULL, "associated symbol", NULL},
> +  { "pc", btpy_insn_pc, NULL, "instruction address", NULL},
> +  { "data", btpy_insn_data, NULL, "raw instruction data", NULL},
> +  { "decoded", btpy_insn_decode, NULL, "decoded instruction", NULL},

Doesn't this also give the error string for gaps?


> +  { "size", btpy_insn_size, NULL, "instruction size in byte", NULL},
> +  { "is_speculative", btpy_insn_is_speculative, NULL, "if the instruction was \
> +executed speculatively", NULL},
> +  {NULL}
> +};
> +
> +/* BtraceFunctionCall members.  */
> +
> +static PyGetSetDef btpy_call_getset[] = {
> +  { "number", btpy_number, NULL, "function call number", NULL},
> +  { "level", btpy_call_level, NULL, "call stack level", NULL},
> +  { "symbol", btpy_call_symbol, NULL, "associated line and symbol", NULL},
> +  { "instructions", btpy_call_instructions, NULL, "list of instructions in \
> +this function call", NULL},

Shouldn't this be "function segment" instead of "function call"?



The patch looks otherwise good to me but we need to find a maintainer to
approve the patch.

thanks,
Markus.

Intel Deutschland GmbH
Registered Address: Am Campeon 10-12, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Christin Eisenschmid, Christian Lamprechter
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

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

* RE: [PATCH v3 5/8] python: Create Python bindings for record history.
  2016-11-21 15:49 ` [PATCH v3 5/8] python: Create Python bindings for record history Tim Wiederhake
@ 2016-11-29 15:48   ` Metzger, Markus T
  0 siblings, 0 replies; 20+ messages in thread
From: Metzger, Markus T @ 2016-11-29 15:48 UTC (permalink / raw)
  To: Wiederhake, Tim, gdb-patches; +Cc: palves

> -----Original Message-----
> From: Wiederhake, Tim
> Sent: Monday, November 21, 2016 4:49 PM
> To: gdb-patches@sourceware.org
> Cc: palves@redhat.com; Metzger, Markus T <markus.t.metzger@intel.com>
> Subject: [PATCH v3 5/8] python: Create Python bindings for record history.
> 
> This patch adds three new functions to the gdb module in Python:
> 	- start_recording
> 	- stop_recording
> 	- current_recording
> start_recording and current_recording return an object of the new type
> gdb.Record, which can be used to access the recorded data.
> 
> 2016-11-21  Tim Wiederhake  <tim.wiederhake@intel.com>
> 
> gdb/ChangeLog
> 
> 	* Makefile.in (SUBDIR_PYTHON_OBS): Add python/py-record.o.
> 	(SUBDIR_PYTHON_SRCS): Add python/py-record.c.
> 	* python/py-record.c: New file.
> 	* python/py-record.h: New file.
> 	* python/python-internal.h (gdbpy_start_recording,
> 	gdbpy_current_recording, gdpy_stop_recording,
> 	gdbpy_initialize_record): New export.
> 	* python/python.c (_initialize_python): Add gdbpy_initialize_record.
> 	(python_GdbMethods): Add gdbpy_start_recording,
> 	gdbpy_current_recording and gdbpy_stop_recording.
> 	* target-debug.h (target_debug_print_struct_record_python_interface):
> 	New define.
> 	* target-delegates.c: Regenerate.
> 	* target.c (target_record_python_interface): New function.
> 	* target.h: Added struct record_python_interface forward declaration.
> 	Export target_record_python_interface.
> 	(struct target_ops): Add to_record_python_interface function.



> +/* Python Record object.  */
> +
> +typedef struct
> +{
> +  PyObject_HEAD
> +
> +  struct record_python_interface interface;

You may want to document every field.


> +static PyObject *
> +recpy_method (PyObject *self, void* closure)
> +{
> +  recpy_record_object *obj = (recpy_record_object *) self;
> +
> +  if (obj->interface.method == NULL)
> +    return PyString_FromString (_("unknown"));

Should this return None, instead?

> +
> +  return PyString_FromString (obj->interface.method);
> +}
> +
> +/* Implementation of record.format.  */
> +
> +static PyObject *
> +recpy_format (PyObject *self, void* closure)
> +{
> +  recpy_record_object *obj = (recpy_record_object *) self;
> +
> +  if (obj->interface.format == NULL)
> +    return PyString_FromString (_("unknown"));

Same here.


> +/* Implementation of gdb.current_recording (self) -> gdb.Record.  */
> +
> +PyObject *
> +gdbpy_current_recording (PyObject *self, PyObject *args)
> +{
> +  recpy_record_object* obj;
> +
> +  obj = PyObject_New (recpy_record_object, &recpy_record_type);
> +  if (obj == NULL)
> +    return NULL;
> +
> +  memset (&obj->interface, 0, sizeof (struct record_python_interface));

How about "sizeof (obj->interface)" instead?



> +/* Implementation of gdb.stop_recording (self) -> None.  */
> +
> +PyObject *
> +gdbpy_stop_recording (PyObject *self, PyObject *args)
> +{
> +  TRY
> +    {
> +      execute_command_to_string ("record stop", 0);
> +      Py_RETURN_NONE;
> +    }
> +  CATCH (except, RETURN_MASK_ALL)
> +    {
> +      gdbpy_convert_exception (except);
> +    }
> +  END_CATCH
> +
> +  return NULL;

We shouldn't really get here, should we? 


Thanks,
Markus.

Intel Deutschland GmbH
Registered Address: Am Campeon 10-12, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Christin Eisenschmid, Christian Lamprechter
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

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

* RE: [PATCH v3 0/8] Python bindings for btrace recordings
  2016-11-21 15:49 [PATCH v3 0/8] Python bindings for btrace recordings Tim Wiederhake
                   ` (7 preceding siblings ...)
  2016-11-21 15:49 ` [PATCH v3 4/8] Add record_start function Tim Wiederhake
@ 2016-11-29 15:49 ` Metzger, Markus T
  8 siblings, 0 replies; 20+ messages in thread
From: Metzger, Markus T @ 2016-11-29 15:49 UTC (permalink / raw)
  To: Wiederhake, Tim, gdb-patches; +Cc: palves

> -----Original Message-----
> From: gdb-patches-owner@sourceware.org [mailto:gdb-patches-
> owner@sourceware.org] On Behalf Of Tim Wiederhake
> Sent: Monday, November 21, 2016 4:49 PM
> To: gdb-patches@sourceware.org
> Cc: palves@redhat.com; Metzger, Markus T <markus.t.metzger@intel.com>
> Subject: [PATCH v3 0/8] Python bindings for btrace recordings
> 
> This patch series adds Python bindings for btrace recordings.
> 
> V1 of this series can be found here:
> https://sourceware.org/ml/gdb-patches/2016-10/msg00733.html
> 
> V2 of this series can be found here:
> https://sourceware.org/ml/gdb-patches/2016-11/msg00084.html
> 
> Changes in V3:
> - Rebased to current master.
> - Replaced some strncmp with strcmp in patch 4 ("Add record_start function").
> - Incorporated Eli's feedback regarding the documentation part.
> 
> Tim Wiederhake (8):
>   btrace: Count gaps as one instruction explicitly.
>   btrace: Export btrace_decode_error function.
>   btrace: Use binary search to find instruction.
>   Add record_start function.
>   python: Create Python bindings for record history.
>   python: Implement btrace Python bindings for record history.
>   python: Add tests for record Python bindings
>   Add documentation for new instruction record Python bindings.

Thanks for this patch series.  This adds a very nice feature IMHO.

I'm OK with the record-btrace bits.  See feedback on individual patches for
a few nits.

You need to find a maintainer to approve the Python bits.

Thanks,
Markus.

Intel Deutschland GmbH
Registered Address: Am Campeon 10-12, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Christin Eisenschmid, Christian Lamprechter
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

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

* Re: [PATCH v3 8/8] Add documentation for new instruction record Python bindings.
  2016-11-29 15:48   ` Metzger, Markus T
@ 2016-11-29 17:32     ` Eli Zaretskii
  0 siblings, 0 replies; 20+ messages in thread
From: Eli Zaretskii @ 2016-11-29 17:32 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: tim.wiederhake, gdb-patches, palves

> From: "Metzger, Markus T" <markus.t.metzger@intel.com>
> CC: "palves@redhat.com" <palves@redhat.com>
> Date: Tue, 29 Nov 2016 15:48:37 +0000
> 
> The patch looks good to me but we need Eli to approve it.

I believe I already did.

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

* Re: [PATCH v3 7/8] python: Add tests for record Python bindings
@ 2016-12-01  0:52 Doug Evans
  0 siblings, 0 replies; 20+ messages in thread
From: Doug Evans @ 2016-12-01  0:52 UTC (permalink / raw)
  To: Tim Wiederhake; +Cc: gdb-patches, palves, markus.t.metzger

Tim Wiederhake writes:
  > 2016-11-21  Tim Wiederhake  <tim.wiederhake@intel.com>
  >
  > gdb/testsuite/ChangeLog:
  >
  >         * gdb.python/py-record-btrace.c: New file.
  >         * gdb.python/py-record-btrace.exp: New file.

LGTM
[modulo other comments and any updating needed of course]

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

end of thread, other threads:[~2016-12-01  0:52 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-11-21 15:49 [PATCH v3 0/8] Python bindings for btrace recordings Tim Wiederhake
2016-11-21 15:49 ` [PATCH v3 1/8] btrace: Count gaps as one instruction explicitly Tim Wiederhake
2016-11-29 15:47   ` Metzger, Markus T
2016-11-21 15:49 ` [PATCH v3 7/8] python: Add tests for record Python bindings Tim Wiederhake
2016-11-29 15:48   ` Metzger, Markus T
2016-11-21 15:49 ` [PATCH v3 6/8] python: Implement btrace Python bindings for record history Tim Wiederhake
2016-11-29 15:48   ` Metzger, Markus T
2016-11-21 15:49 ` [PATCH v3 8/8] Add documentation for new instruction record Python bindings Tim Wiederhake
2016-11-29 15:48   ` Metzger, Markus T
2016-11-29 17:32     ` Eli Zaretskii
2016-11-21 15:49 ` [PATCH v3 3/8] btrace: Use binary search to find instruction Tim Wiederhake
2016-11-29 15:47   ` Metzger, Markus T
2016-11-21 15:49 ` [PATCH v3 2/8] btrace: Export btrace_decode_error function Tim Wiederhake
2016-11-29 15:47   ` Metzger, Markus T
2016-11-21 15:49 ` [PATCH v3 5/8] python: Create Python bindings for record history Tim Wiederhake
2016-11-29 15:48   ` Metzger, Markus T
2016-11-21 15:49 ` [PATCH v3 4/8] Add record_start function Tim Wiederhake
2016-11-29 15:47   ` Metzger, Markus T
2016-11-29 15:49 ` [PATCH v3 0/8] Python bindings for btrace recordings Metzger, Markus T
2016-12-01  0:52 [PATCH v3 7/8] python: Add tests for record Python bindings Doug Evans

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