* [PATCH 7/7] Add documentation for new instruction record Python bindings.
2016-10-27 6:29 [PATCH 0/7] Python bindings for btrace recordings Tim Wiederhake
@ 2016-10-27 6:29 ` Tim Wiederhake
2016-10-27 15:02 ` Eli Zaretskii
2016-10-27 16:10 ` Simon Marchi
2016-10-27 6:29 ` [PATCH 5/7] python: Implement btrace Python bindings for record history Tim Wiederhake
` (5 subsequent siblings)
6 siblings, 2 replies; 18+ messages in thread
From: Tim Wiederhake @ 2016-10-27 6:29 UTC (permalink / raw)
To: gdb-patches; +Cc: palves, markus.t.metzger
2016-10-26 Tim Wiederhake <tim.wiederhake@intel.com>
gdb/ChangeLog:
* NEWS: Add record Python bindings entry.
gdb/doc/ChangeLog:
* python.texi: Add record Python bindings entry.
---
gdb/NEWS | 4 +
gdb/doc/python.texi | 239 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 243 insertions(+)
diff --git a/gdb/NEWS b/gdb/NEWS
index 4a61438..6da2784 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.
+
* GDB and GDBserver now require building with a C++ compiler.
It is no longer possible to build GDB or GDBserver with a C
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index d6507e5..f396914 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,244 @@ 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 are available in the
+@code{gdb} module:
+
+@defun gdb.start_recording ([method])
+Starts a recording using the given @var{method}. If no @var{method} is
+given, the default method will be used. Returns a @code{gdb.Record}
+object on success. Throws an exception on failure. The following
+methods are currently supported:
+
+@itemize @bullet
+@item
+@code{"full"}
+@item
+@code{"btrace"}: Will use @code{"pt"} or @code{"bts"}, depending on
+availability.
+@item
+@code{"btrace pt"}
+@item
+@code{"pt"}: Alias for @code{"btrace pt"}
+@item
+@code{"btrace bts"}
+@item
+@code{"bts"}: Alias for @code{"btrace bts"}
+@end itemize
+@end defun
+
+@defun gdb.current_recording ()
+Access a currently running recording. Returns a @code{gdb.Record}
+object on success. Returns @code{None} if no recording is currently
+active.
+@end defun
+
+@defun gdb.stop_recording ()
+Stops the current recording. Throws 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}.
+@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}.
+@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] 18+ messages in thread* Re: [PATCH 7/7] Add documentation for new instruction record Python bindings.
2016-10-27 6:29 ` [PATCH 7/7] Add documentation for new instruction record Python bindings Tim Wiederhake
@ 2016-10-27 15:02 ` Eli Zaretskii
2016-10-27 16:10 ` Simon Marchi
1 sibling, 0 replies; 18+ messages in thread
From: Eli Zaretskii @ 2016-10-27 15:02 UTC (permalink / raw)
To: Tim Wiederhake; +Cc: gdb-patches, palves, markus.t.metzger
> From: Tim Wiederhake <tim.wiederhake@intel.com>
> Cc: palves@redhat.com, markus.t.metzger@intel.com
> Date: Thu, 27 Oct 2016 08:28:31 +0200
>
> 2016-10-26 Tim Wiederhake <tim.wiederhake@intel.com>
>
> gdb/ChangeLog:
>
> * NEWS: Add record Python bindings entry.
>
> gdb/doc/ChangeLog:
>
> * python.texi: Add record Python bindings entry.
Thanks, I have a few comments on this part of the patch.
> +@node Recordings In Python
> +@subsubsection Recordings In Python
> +@cindex recordings in python
> +
> +The following recordings-related functions are available in the
> +@code{gdb} module:
Please add here a sentence that references the place in the manual
where recording and replaying facilities are described.
> +@defun gdb.start_recording ([method])
Please use "@r{[}method@r{]}" instead of "[method]", so that the
brackets are typeset in the roman typeface.
> +Starts a recording using the given @var{method}. If no @var{method} is
Elsewhere in the manual we say "Start", not "Starts". Please use that
form for consistency (here and elsewhere in your documentation patch).
> +@itemize @bullet
> +@item
> +@code{"full"}
I suggest to lose the quotes, they don't look good inside @code, and
also might mislead the reader to think that these are strings, which I
don't think is true. You could use @samp instead of @code if you want
to have quotes in the printed/PDF manual (in the Info output this will
be quoted with @code as well).
> +@defvar BtraceInstruction.number
> +An integer identifying this instruction. @var{number} corresponds to
> +the numbers seen in @code{record instruction-history}.
A cross-reference to the place which describes that command would be
good here.
This part is okay with the above issues fixed.
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH 7/7] Add documentation for new instruction record Python bindings.
2016-10-27 6:29 ` [PATCH 7/7] Add documentation for new instruction record Python bindings Tim Wiederhake
2016-10-27 15:02 ` Eli Zaretskii
@ 2016-10-27 16:10 ` Simon Marchi
1 sibling, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2016-10-27 16:10 UTC (permalink / raw)
To: Tim Wiederhake; +Cc: gdb-patches, palves, markus.t.metzger
On 2016-10-27 02:28, Tim Wiederhake wrote:
> +@defun gdb.start_recording ([method])
> +Starts a recording using the given @var{method}. If no @var{method}
> is
> +given, the default method will be used. Returns a @code{gdb.Record}
> +object on success. Throws an exception on failure. The following
> +methods are currently supported:
Would it be better to separate the method and format? Like:
gdb.start_recording([method], [format]);
Omitting the format would let the method choose the best format (like pt
vs bts for btrace).
Also, for a programmatic API such as this one, I don't really see the
advantage of allowing shorthands like "pt" instead of "btrace pt". It
makes the code and API more complex with not much gain.
Thanks,
Simon
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 5/7] python: Implement btrace Python bindings for record history.
2016-10-27 6:29 [PATCH 0/7] Python bindings for btrace recordings Tim Wiederhake
2016-10-27 6:29 ` [PATCH 7/7] Add documentation for new instruction record Python bindings Tim Wiederhake
@ 2016-10-27 6:29 ` Tim Wiederhake
2016-10-27 6:29 ` [PATCH 2/7] btrace: Export btrace_decode_error function Tim Wiederhake
` (4 subsequent siblings)
6 siblings, 0 replies; 18+ messages in thread
From: Tim Wiederhake @ 2016-10-27 6:29 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-10-26 Tim Wiederhake <tim.wiederhake@intel.com>
gdb/ChangeLog:
* Makefile.in (SUBDIR_PYTHON_OBS): Add py-btrace.o.
(SUBDIR_PYTHON_SRCS): Add py-btrace.c.
(py-btrace.o): New rule.
* 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 | 6 +
gdb/python/py-btrace.c | 996 +++++++++++++++++++++++++++++++++++++++++++
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, 1095 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 844a648..64789fd 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -380,6 +380,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 \
@@ -421,6 +422,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 \
@@ -2604,6 +2606,10 @@ py-breakpoint.o: $(srcdir)/python/py-breakpoint.c
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-breakpoint.c
$(POSTCOMPILE)
+py-btrace.o: $(srcdir)/python/py-btrace.c
+ $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-btrace.c
+ $(POSTCOMPILE)
+
py-cmd.o: $(srcdir)/python/py-cmd.c
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-cmd.c
$(POSTCOMPILE)
diff --git a/gdb/python/py-btrace.c b/gdb/python/py-btrace.c
new file mode 100644
index 0000000..1ad22e0
--- /dev/null
+++ b/gdb/python/py-btrace.c
@@ -0,0 +1,996 @@
+/* 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);
+ char *result = NULL;
+ 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)
+ result = execute_command_to_string ("record goto end", 0);
+ else
+ {
+ snprintf (buffer, sizeof (buffer), "record goto %zi", obj->number);
+ result = execute_command_to_string (buffer, 0);
+ }
+ }
+ CATCH (except, RETURN_MASK_ALL)
+ {
+ GDB_PY_HANDLE_EXCEPTION (except);
+ }
+ END_CATCH
+
+ xfree (result);
+ 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 3828b4c..ffbb3dc 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 39e411d..c5fccb7 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1800,6 +1800,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 e57678b..f73bd46 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;
@@ -2752,6 +2757,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
@@ -2805,6 +2842,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 e4dd55b..abe415e 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
@@ -1964,6 +1968,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)
{
@@ -2006,6 +2025,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] 18+ messages in thread* [PATCH 2/7] btrace: Export btrace_decode_error function.
2016-10-27 6:29 [PATCH 0/7] Python bindings for btrace recordings Tim Wiederhake
2016-10-27 6:29 ` [PATCH 7/7] Add documentation for new instruction record Python bindings Tim Wiederhake
2016-10-27 6:29 ` [PATCH 5/7] python: Implement btrace Python bindings for record history Tim Wiederhake
@ 2016-10-27 6:29 ` Tim Wiederhake
2016-10-27 6:29 ` [PATCH 4/7] python: Create Python bindings for record history Tim Wiederhake
` (3 subsequent siblings)
6 siblings, 0 replies; 18+ messages in thread
From: Tim Wiederhake @ 2016-10-27 6:29 UTC (permalink / raw)
To: gdb-patches; +Cc: palves, markus.t.metzger
2016-10-26 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 16ce2ef..0d1ba39 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -1278,6 +1278,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 84ce3ea..e57678b 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] 18+ messages in thread* [PATCH 4/7] python: Create Python bindings for record history.
2016-10-27 6:29 [PATCH 0/7] Python bindings for btrace recordings Tim Wiederhake
` (2 preceding siblings ...)
2016-10-27 6:29 ` [PATCH 2/7] btrace: Export btrace_decode_error function Tim Wiederhake
@ 2016-10-27 6:29 ` Tim Wiederhake
2016-10-27 15:53 ` Simon Marchi
2016-10-27 6:29 ` [PATCH 1/7] btrace: Count gaps as one instruction explicitly Tim Wiederhake
` (2 subsequent siblings)
6 siblings, 1 reply; 18+ messages in thread
From: Tim Wiederhake @ 2016-10-27 6:29 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-10-26 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.
(py-record.o): New rule.
* 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: Regenerated.
* 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): Added to_record_python_interface function.
---
gdb/Makefile.in | 6 +
gdb/python/py-record.c | 291 +++++++++++++++++++++++++++++++++++++++++++
gdb/python/py-record.h | 57 +++++++++
gdb/python/python-internal.h | 5 +
gdb/python/python.c | 12 ++
gdb/target-debug.h | 2 +
gdb/target-delegates.c | 33 +++++
gdb/target.c | 7 ++
gdb/target.h | 10 ++
9 files changed, 423 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 2c88434..844a648 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -402,6 +402,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 \
@@ -442,6 +443,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 \
@@ -2690,6 +2692,10 @@ py-progspace.o: $(srcdir)/python/py-progspace.c
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-progspace.c
$(POSTCOMPILE)
+py-record.o: $(srcdir)/python/py-record.c
+ $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-record.c
+ $(POSTCOMPILE)
+
py-signalevent.o: $(srcdir)/python/py-signalevent.c
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-signalevent.c
$(POSTCOMPILE)
diff --git a/gdb/python/py-record.c b/gdb/python/py-record.c
new file mode 100644
index 0000000..2fe6e13
--- /dev/null
+++ b/gdb/python/py-record.c
@@ -0,0 +1,291 @@
+/* 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-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);
+}
+
+/* Executes a command silently. Returns non-zero on success; returns zero and
+ sets Python exception on failure. */
+
+static int
+gdbpy_execute_silenty (char *command)
+{
+ char *output = NULL;
+ int success = 1;
+
+ TRY
+ {
+ output = execute_command_to_string (command, 0);
+ }
+ CATCH (except, RETURN_MASK_ALL)
+ {
+ if (except.reason < 0)
+ gdbpy_convert_exception (except);
+
+ success = 0;
+ }
+ END_CATCH
+
+ xfree (output);
+ return success;
+}
+
+/* Implementation of gdb.start_recording (method) -> gdb.Record. */
+
+PyObject *
+gdbpy_start_recording (PyObject *self, PyObject *args)
+{
+ char *method = "full";
+
+ if (!PyArg_ParseTuple (args, "|s", &method))
+ return NULL;
+
+ if (strncmp (method, "full", sizeof ("full")) == 0)
+ {
+ if (!gdbpy_execute_silenty ("record full"))
+ return NULL;
+ }
+ else if (strncmp (method, "btrace", sizeof ("btrace")) == 0)
+ {
+ if (!gdbpy_execute_silenty ("record btrace pt"))
+ {
+ PyErr_Clear ();
+ if (!gdbpy_execute_silenty ("record btrace bts"))
+ return NULL;
+ }
+ }
+ else if ((strncmp (method, "pt", sizeof ("pt")) == 0)
+ || (strncmp (method, "btrace pt", sizeof ("btrace pt")) == 0))
+ {
+ if (!gdbpy_execute_silenty ("record btrace pt"))
+ return NULL;
+ }
+ else if ((strncmp (method, "bts", sizeof ("bts")) == 0)
+ || (strncmp (method, "btrace bts", sizeof ("btrace bts")) == 0))
+ {
+ if (!gdbpy_execute_silenty ("record btrace bts"))
+ return NULL;
+ }
+ else
+ return PyErr_Format (PyExc_TypeError, _("Unknown recording method."));
+
+ return gdbpy_current_recording (self, args);
+}
+
+/* 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)
+{
+ if (!gdbpy_execute_silenty ("record stop"))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
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 8545c7b..3828b4c 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 7e34d26..39e411d 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1799,6 +1799,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
@@ -2011,6 +2012,17 @@ 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]) -> gdb.Record.\n\
+Start recording with the given method. If no method is given, will fall back\n\
+to the system default metdhod."},
+ { "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 57e7939..d7edfdb 100644
--- a/gdb/target-delegates.c
+++ b/gdb/target-delegates.c
@@ -3973,6 +3973,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;
@@ -4394,6 +4423,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)
@@ -4556,6 +4587,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;
@@ -4713,6 +4745,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 cb89e75..50a192b 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -3784,6 +3784,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 (¤t_target, iface);
+}
+
+/* See target.h. */
void
target_goto_record_begin (void)
diff --git a/gdb/target.h b/gdb/target.h
index 176f332..faaed0e 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. */
@@ -1230,6 +1231,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 *)
@@ -2503,6 +2510,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] 18+ messages in thread* Re: [PATCH 4/7] python: Create Python bindings for record history.
2016-10-27 6:29 ` [PATCH 4/7] python: Create Python bindings for record history Tim Wiederhake
@ 2016-10-27 15:53 ` Simon Marchi
2016-10-28 14:12 ` Wiederhake, Tim
0 siblings, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2016-10-27 15:53 UTC (permalink / raw)
To: Tim Wiederhake; +Cc: gdb-patches, palves, markus.t.metzger
On 2016-10-27 02:28, Tim Wiederhake wrote:
> 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.
Hi Tim,
Thanks for these patches, I think that offering a Python API for
controlling and querying process record is very interesting.
> 2016-10-26 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.
> (py-record.o): New rule.
> * 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: Regenerated.
Little detail: consistently use the present tense in the ChangeLog
entries (e.g. Regenerate instead of Regenerated).
> * 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): Added to_record_python_interface function.
Same.
> +/* Executes a command silently. Returns non-zero on success; returns
> zero and
> + sets Python exception on failure. */
> +
> +static int
> +gdbpy_execute_silenty (char *command)
silenty -> silently?
Ideally we should really be using internal APIs instead of using this
function... unfortunately they don't seem to exist yet for these tasks.
Having GDB build command strings, only to parse them itself right away
feels like a waste of resources, but it also makes the code harder to
follow.
> +/* Implementation of gdb.start_recording (method) -> gdb.Record. */
> +
> +PyObject *
> +gdbpy_start_recording (PyObject *self, PyObject *args)
> +{
> + char *method = "full";
Should probably be const.
> +
> + if (!PyArg_ParseTuple (args, "|s", &method))
> + return NULL;
> +
> + if (strncmp (method, "full", sizeof ("full")) == 0)
> + {
> + if (!gdbpy_execute_silenty ("record full"))
> + return NULL;
> + }
> + else if (strncmp (method, "btrace", sizeof ("btrace")) == 0)
> + {
> + if (!gdbpy_execute_silenty ("record btrace pt"))
> + {
> + PyErr_Clear ();
> + if (!gdbpy_execute_silenty ("record btrace bts"))
> + return NULL;
> + }
> + }
> + else if ((strncmp (method, "pt", sizeof ("pt")) == 0)
> + || (strncmp (method, "btrace pt", sizeof ("btrace pt")) == 0))
> + {
> + if (!gdbpy_execute_silenty ("record btrace pt"))
> + return NULL;
> + }
> + else if ((strncmp (method, "bts", sizeof ("bts")) == 0)
> + || (strncmp (method, "btrace bts", sizeof ("btrace bts")) == 0))
> + {
> + if (!gdbpy_execute_silenty ("record btrace bts"))
> + return NULL;
> + }
> + else
> + return PyErr_Format (PyExc_TypeError, _("Unknown recording
> method."));
> +
> + return gdbpy_current_recording (self, args);
> +}
This function seems to have too much knowledge of the record formats,
and will need to be updated for every new record format. And if we want
to offer a similar API in the Guile bindings, the knowledge will need to
be duplicated. This is where an internal API would be interesting.
Assuming there would be an internal function
record_start (const char *method, const char *format);
Then gdbpy_start_recording could be a simple wrapper around that. If
the method/format doesn't exist, it will throw an exception, which you
can catch and format correctly.
> diff --git a/gdb/target.h b/gdb/target.h
> index 176f332..faaed0e 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. */
> @@ -1230,6 +1231,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);
> +
I am going to nag you again with "internal API" stuff :). Introducing
some Python-specific stuff here doesn't feel right. It should probably
use a structure (or class) similar to record_python_interface, but which
doesn't know anything about Python. Then it's up to the Python layer to
query whatever it needs and convert that into whatever structure it
wants. But at least, other parts of GDB could use it too.
Thanks,
Simon
^ permalink raw reply [flat|nested] 18+ messages in thread* RE: [PATCH 4/7] python: Create Python bindings for record history.
2016-10-27 15:53 ` Simon Marchi
@ 2016-10-28 14:12 ` Wiederhake, Tim
0 siblings, 0 replies; 18+ messages in thread
From: Wiederhake, Tim @ 2016-10-28 14:12 UTC (permalink / raw)
To: Simon Marchi; +Cc: gdb-patches, palves, Metzger, Markus T
Hi Simon,
Thanks for the comments!
> -----Original Message-----
> From: Simon Marchi [mailto:simon.marchi@polymtl.ca]
> Sent: Thursday, October 27, 2016 5:53 PM
> To: Wiederhake, Tim <tim.wiederhake@intel.com>
> Cc: gdb-patches@sourceware.org; palves@redhat.com; Metzger, Markus T
> <markus.t.metzger@intel.com>
> Subject: Re: [PATCH 4/7] python: Create Python bindings for record history.
>
> On 2016-10-27 02:28, Tim Wiederhake wrote:
> > 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.
>
> Hi Tim,
>
> Thanks for these patches, I think that offering a Python API for controlling
> and querying process record is very interesting.
>
> > 2016-10-26 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.
> > (py-record.o): New rule.
> > * 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: Regenerated.
>
> Little detail: consistently use the present tense in the ChangeLog entries (e.g.
> Regenerate instead of Regenerated).
>
> > * 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): Added to_record_python_interface function.
>
> Same.
>
> > +/* Executes a command silently. Returns non-zero on success; returns
> > zero and
> > + sets Python exception on failure. */
> > +
> > +static int
> > +gdbpy_execute_silenty (char *command)
>
> silenty -> silently?
Oops.
> Ideally we should really be using internal APIs instead of using this function...
> unfortunately they don't seem to exist yet for these tasks.
> Having GDB build command strings, only to parse them itself right away feels
> like a waste of resources, but it also makes the code harder to follow.
>
> > +/* Implementation of gdb.start_recording (method) -> gdb.Record. */
> > +
> > +PyObject *
> > +gdbpy_start_recording (PyObject *self, PyObject *args) {
> > + char *method = "full";
>
> Should probably be const.
>
> > +
> > + if (!PyArg_ParseTuple (args, "|s", &method))
> > + return NULL;
> > +
> > + if (strncmp (method, "full", sizeof ("full")) == 0)
> > + {
> > + if (!gdbpy_execute_silenty ("record full"))
> > + return NULL;
> > + }
> > + else if (strncmp (method, "btrace", sizeof ("btrace")) == 0)
> > + {
> > + if (!gdbpy_execute_silenty ("record btrace pt"))
> > + {
> > + PyErr_Clear ();
> > + if (!gdbpy_execute_silenty ("record btrace bts"))
> > + return NULL;
> > + }
> > + }
> > + else if ((strncmp (method, "pt", sizeof ("pt")) == 0)
> > + || (strncmp (method, "btrace pt", sizeof ("btrace pt")) == 0))
> > + {
> > + if (!gdbpy_execute_silenty ("record btrace pt"))
> > + return NULL;
> > + }
> > + else if ((strncmp (method, "bts", sizeof ("bts")) == 0)
> > + || (strncmp (method, "btrace bts", sizeof ("btrace bts")) == 0))
> > + {
> > + if (!gdbpy_execute_silenty ("record btrace bts"))
> > + return NULL;
> > + }
> > + else
> > + return PyErr_Format (PyExc_TypeError, _("Unknown recording
> > method."));
> > +
> > + return gdbpy_current_recording (self, args); }
>
> This function seems to have too much knowledge of the record formats, and
> will need to be updated for every new record format. And if we want to
> offer a similar API in the Guile bindings, the knowledge will need to be
> duplicated. This is where an internal API would be interesting.
> Assuming there would be an internal function
>
> record_start (const char *method, const char *format);
>
> Then gdbpy_start_recording could be a simple wrapper around that. If the
> method/format doesn't exist, it will throw an exception, which you can catch
> and format correctly.
>
> > diff --git a/gdb/target.h b/gdb/target.h index 176f332..faaed0e 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. */
> > @@ -1230,6 +1231,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);
> > +
>
> I am going to nag you again with "internal API" stuff :). Introducing
> some Python-specific stuff here doesn't feel right. It should probably
> use a structure (or class) similar to record_python_interface, but which
> doesn't know anything about Python. Then it's up to the Python layer to
> query whatever it needs and convert that into whatever structure it
> wants. But at least, other parts of GDB could use it too.
The gdb.Record Python object is recording method agnostic. It calls to the current record target to create instruction or function call segment objects (or lists of those).
Btrace recording will generally create different objects than full recording (they might have the same interface to the Python world, but they might have to store different data to identify an instruction or function call segment), and the record target is the only instance to know what type of object to create. Even if we stuff all information gdb.Record would need in a non-Python-y struct, gdb.Record still lacks the information of the instruction / call segment object type.
Making gdb.Record aware of the current recording method does not solve this problem, rather it moves the problem from instruction / call segment object creation time to gdb.Record creation time. If you (or others) have any idea about this, please let me know.
>
> Thanks,
>
> Simon
Tim
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] 18+ messages in thread
* [PATCH 1/7] btrace: Count gaps as one instruction explicitly.
2016-10-27 6:29 [PATCH 0/7] Python bindings for btrace recordings Tim Wiederhake
` (3 preceding siblings ...)
2016-10-27 6:29 ` [PATCH 4/7] python: Create Python bindings for record history Tim Wiederhake
@ 2016-10-27 6:29 ` Tim Wiederhake
2016-10-27 6:29 ` [PATCH 6/7] python: Add tests for record Python bindings Tim Wiederhake
2016-10-27 6:29 ` [PATCH 3/7] btrace: Use binary search to find instruction Tim Wiederhake
6 siblings, 0 replies; 18+ messages in thread
From: Tim Wiederhake @ 2016-10-27 6:29 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-10-26 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 d1af128..16ce2ef 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -137,6 +137,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. */
@@ -212,8 +226,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;
}
@@ -1729,18 +1742,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. */
@@ -1935,37 +1948,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);
}
@@ -1977,26 +1959,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 257d0b0..84ce3ea 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
@@ -2796,7 +2784,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] 18+ messages in thread* [PATCH 6/7] python: Add tests for record Python bindings
2016-10-27 6:29 [PATCH 0/7] Python bindings for btrace recordings Tim Wiederhake
` (4 preceding siblings ...)
2016-10-27 6:29 ` [PATCH 1/7] btrace: Count gaps as one instruction explicitly Tim Wiederhake
@ 2016-10-27 6:29 ` Tim Wiederhake
2016-10-27 15:59 ` Simon Marchi
2016-10-27 6:29 ` [PATCH 3/7] btrace: Use binary search to find instruction Tim Wiederhake
6 siblings, 1 reply; 18+ messages in thread
From: Tim Wiederhake @ 2016-10-27 6:29 UTC (permalink / raw)
To: gdb-patches; +Cc: palves, markus.t.metzger
2016-10-26 Tim Wiederhake <tim.wiederhake@intel.com>
gdb/testsuite/ChangeLog:
* gdb.python/py-record.c: New file.
* gdb.python/py-record.exp: New file.
---
gdb/testsuite/gdb.python/py-record.c | 48 ++++++++++
gdb/testsuite/gdb.python/py-record.exp | 160 +++++++++++++++++++++++++++++++++
2 files changed, 208 insertions(+)
create mode 100644 gdb/testsuite/gdb.python/py-record.c
create mode 100644 gdb/testsuite/gdb.python/py-record.exp
diff --git a/gdb/testsuite/gdb.python/py-record.c b/gdb/testsuite/gdb.python/py-record.c
new file mode 100644
index 0000000..e12c132
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-record.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.exp b/gdb/testsuite/gdb.python/py-record.exp
new file mode 100644
index 0000000..f0bb035
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-record.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()"
+ gdb_test "python gdb.stop_recording()" ".*gdb\.error: No record target is currently active\..*"
+}
+
+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] 18+ messages in thread* Re: [PATCH 6/7] python: Add tests for record Python bindings
2016-10-27 6:29 ` [PATCH 6/7] python: Add tests for record Python bindings Tim Wiederhake
@ 2016-10-27 15:59 ` Simon Marchi
2016-10-28 13:49 ` Wiederhake, Tim
0 siblings, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2016-10-27 15:59 UTC (permalink / raw)
To: Tim Wiederhake; +Cc: gdb-patches, palves, markus.t.metzger
On 2016-10-27 02:28, Tim Wiederhake wrote:
> +# Skip this test if btrace is disabled.
> +
> +if { [skip_btrace_tests] } { return -1 }
Would it be helpful to have at least some minimal tests using the "full"
method, even if btrace is not available?
> +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()"
> + gdb_test "python gdb.stop_recording()" ".*gdb\.error: No record
> target is currently active\..*"
These last two tests give the same test name (which must be unique).
You could give a specific name to one of them.
^ permalink raw reply [flat|nested] 18+ messages in thread* RE: [PATCH 6/7] python: Add tests for record Python bindings
2016-10-27 15:59 ` Simon Marchi
@ 2016-10-28 13:49 ` Wiederhake, Tim
2016-10-28 17:47 ` Simon Marchi
0 siblings, 1 reply; 18+ messages in thread
From: Wiederhake, Tim @ 2016-10-28 13:49 UTC (permalink / raw)
To: Simon Marchi; +Cc: gdb-patches, palves, Metzger, Markus T
Hi Simon,
Thanks for the comments!
> -----Original Message-----
> From: Simon Marchi [mailto:simon.marchi@polymtl.ca]
> Sent: Thursday, October 27, 2016 6:00 PM
> To: Wiederhake, Tim <tim.wiederhake@intel.com>
> Cc: gdb-patches@sourceware.org; palves@redhat.com; Metzger, Markus T
> <markus.t.metzger@intel.com>
> Subject: Re: [PATCH 6/7] python: Add tests for record Python bindings
>
> On 2016-10-27 02:28, Tim Wiederhake wrote:
> > +# Skip this test if btrace is disabled.
> > +
> > +if { [skip_btrace_tests] } { return -1 }
>
> Would it be helpful to have at least some minimal tests using the "full"
> method, even if btrace is not available?
If you are recording using the "full" method and try to access the data from Python, you get a not-yet-implemented exception. All we can test in this case is that the recording method in the gdb.Record object is displayed properly, i.e. gdb.current_recording().method == "full". I could rename py-record.exp to py-record-btrace.exp and add a py-record-full.exp with that exact test in it, but I don't see too much value in that. Your opinion?
> > +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()"
> > + gdb_test "python gdb.stop_recording()" ".*gdb\.error: No record
> > target is currently active\..*"
>
> These last two tests give the same test name (which must be unique).
> You could give a specific name to one of them.
Tim
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] 18+ messages in thread* Re: [PATCH 6/7] python: Add tests for record Python bindings
2016-10-28 13:49 ` Wiederhake, Tim
@ 2016-10-28 17:47 ` Simon Marchi
0 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2016-10-28 17:47 UTC (permalink / raw)
To: Wiederhake, Tim; +Cc: gdb-patches, palves, Metzger, Markus T
On 2016-10-28 09:48, Wiederhake, Tim wrote:
> If you are recording using the "full" method and try to access the
> data from Python, you get a not-yet-implemented exception. All we can
> test in this case is that the recording method in the gdb.Record
> object is displayed properly, i.e. gdb.current_recording().method ==
> "full". I could rename py-record.exp to py-record-btrace.exp and add a
> py-record-full.exp with that exact test in it, but I don't see too
> much value in that. Your opinion?
I think that at least renaming it to py-record-btrace.exp as you
suggested would be good, since it tests specifically btrace. That will
leave room for an eventual py-record.exp including more generic tests.
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 3/7] btrace: Use binary search to find instruction.
2016-10-27 6:29 [PATCH 0/7] Python bindings for btrace recordings Tim Wiederhake
` (5 preceding siblings ...)
2016-10-27 6:29 ` [PATCH 6/7] python: Add tests for record Python bindings Tim Wiederhake
@ 2016-10-27 6:29 ` Tim Wiederhake
2016-10-27 14:28 ` Simon Marchi
6 siblings, 1 reply; 18+ messages in thread
From: Tim Wiederhake @ 2016-10-27 6:29 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-10-26 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 0d1ba39..84cf55a 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -1389,6 +1389,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);
@@ -1396,6 +1398,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);
@@ -1418,6 +1425,8 @@ btrace_clear (struct thread_info *tp)
btinfo = &tp->btrace;
+ VEC_free (btrace_fun_p, btinfo->functions);
+
it = btinfo->begin;
while (it != NULL)
{
@@ -2008,20 +2017,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] 18+ messages in thread* Re: [PATCH 3/7] btrace: Use binary search to find instruction.
2016-10-27 6:29 ` [PATCH 3/7] btrace: Use binary search to find instruction Tim Wiederhake
@ 2016-10-27 14:28 ` Simon Marchi
2016-11-02 10:01 ` Wiederhake, Tim
0 siblings, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2016-10-27 14:28 UTC (permalink / raw)
To: Tim Wiederhake; +Cc: gdb-patches, palves, markus.t.metzger
On 2016-10-27 02:28, Tim Wiederhake wrote:
> 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.
Hi Tim,
Unless you expect to backport this patch in the 7.12 branch (which I
don't think is the case here), you can use C++ standard containers and
functions. Specifically, std::vector instead of DEF_VEC, and probably
std::lower_bound (if I understand correctly what the code is doing).
Also, since we now have btrace_function pointers in a sorted vector, do
we still need them to be linked together as a linked list?
Simon
^ permalink raw reply [flat|nested] 18+ messages in thread
* RE: [PATCH 3/7] btrace: Use binary search to find instruction.
2016-10-27 14:28 ` Simon Marchi
@ 2016-11-02 10:01 ` Wiederhake, Tim
2016-11-02 11:24 ` Simon Marchi
0 siblings, 1 reply; 18+ messages in thread
From: Wiederhake, Tim @ 2016-11-02 10:01 UTC (permalink / raw)
To: 'Simon Marchi'; +Cc: gdb-patches, palves, Metzger, Markus T
Hi Simon,
Thanks for your input!
> Unless you expect to backport this patch in the 7.12 branch (which I don't think is the case here), you can use C++ standard containers and functions. Specifically, std::vector instead of DEF_VEC, and probably std::lower_bound (if I understand correctly what the code is doing).
struct btrace_thread_info is initialized by memset'ing it to zero. If I'm not mistaken we can't do that if the struct contains a C++ member with a constructor.
> Also, since we now have btrace_function pointers in a sorted vector, do we still need them to be linked together as a linked list?
The btrace_function pointers will go away in the next patch series.
Tim
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] 18+ messages in thread
* Re: [PATCH 3/7] btrace: Use binary search to find instruction.
2016-11-02 10:01 ` Wiederhake, Tim
@ 2016-11-02 11:24 ` Simon Marchi
0 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2016-11-02 11:24 UTC (permalink / raw)
To: Wiederhake, Tim; +Cc: gdb-patches, palves, Metzger, Markus T
On 2016-11-02 06:01, Wiederhake, Tim wrote:
> struct btrace_thread_info is initialized by memset'ing it to zero. If
> I'm not mistaken we can't do that if the struct contains a C++ member
> with a constructor.
Indeed, the allocations/deallocations would need to be replaced with
new/delete, and a constructor to zero-initialize the other members.
^ permalink raw reply [flat|nested] 18+ messages in thread