From 913aeadd9d6b8bdea5131b16dc7ede395097656d Mon Sep 17 00:00:00 2001 From: Tim Wiederhake Date: Tue, 2 May 2017 11:35:54 +0200 Subject: [PATCH] Python: Introduce gdb.RecordGap class As discussed here: https://sourceware.org/ml/gdb-patches/2017-04/msg00157.html A gap is not an instruction and it should not pretend to be one. gdb.Record.instruction_history is now a list of gdb.RecordInstruction and gdb.RecordGap objects. This allows the user to deal with Gaps in the record in a more sane way. --- gdb/ChangeLog | 18 ++++ gdb/doc/ChangeLog | 6 ++ gdb/doc/python.texi | 47 ++++++---- gdb/python/py-record-btrace.c | 72 +++++++-------- gdb/python/py-record.c | 91 ++++++++++++++++++- gdb/python/py-record.h | 4 + gdb/testsuite/ChangeLog | 5 + gdb/testsuite/gdb.python/py-record-btrace.exp | 1 - 8 files changed, 182 insertions(+), 62 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index de0b6d47e3a..9b80425807c 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,21 @@ +2017-05-01 Tim Wiederhake + + * py-record-btrace.c (btpy_insn_new): Removed. + (btpy_insn_or_gap_new): New function. + (btpy_insn_error): Removed. + (btpy_insn_sal, btpy_insn_pc, btpy_insn_size, btpy_insn_is_speculative, + btpy_insn_data, btpy_insn_decode): Remove code path for gaps. + (recpy_bt_replay_position, recpy_bt_begin, recpy_bt_end): Call + btpy_insn_or_gap_new instead of btpy_insn_new. + (btpy_insn_getset): Remove btpy_insn_error. + * py-record.c (recpy_gap_type): New static object. + (recpy_gap_object): New typedef. + (recpy_gap_new, recpy_gap_number, recpy_gap_reason_code, + recpy_gap_reason_string): New function. + (recpy_gap_getset): New static object. + (gdbpy_initialize_record): Initialize gdb.RecordGap type. + * py-record.h (recpy_gap_new): New export. + 2017-05-01 Tim Wiederhake * python/py-record.c (recpy_ptid): Remove. diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index c13418a07cd..202fbe320c1 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,9 @@ +2017-05-01 Tim Wiederhake + + * python.texi (Recording in Python): Add documentation for + gdb.RecordGap and remove documentation of special cases for + gdb.BtraceInstruction. + 2017-05-01 Tim Wiederhake * python.texi (Recording in Python): Remove Record.ptid defvar. diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 8c246a43719..828806a2307 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -3160,41 +3160,50 @@ the numbers seen in @code{record instruction-history} (@pxref{Process Record and Replay}). @end defvar -@defvar BtraceInstruction.error -An integer identifying the error code for gaps in the history. -@code{None} for regular instructions. -@end defvar - @defvar BtraceInstruction.sal A @code{gdb.Symtab_and_line} object representing the associated symtab -and line of this instruction. May be @code{None} if the instruction -is a gap. +and line of this instruction. May be @code{None} if no debug information is +available. @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. +An integer representing this instruction's address. @end defvar @defvar BtraceInstruction.data -A buffer with the raw instruction data. May be @code{None} if the -instruction is a gap. In Python 3, the return value is a @code{memoryview} -object. +A buffer with the raw instruction data. In Python 3, the return value is a +@code{memoryview} object. @end defvar @defvar BtraceInstruction.decoded -A human readable string with the disassembled instruction. Contains the -error message for gaps. +A human readable string with the disassembled instruction. @end defvar @defvar BtraceInstruction.size -The size of the instruction in bytes. Will be @code{None} if the -instruction is a gap. +The size of the instruction in bytes. @end defvar @defvar BtraceInstruction.is_speculative A boolean indicating whether the instruction was executed -speculatively. Will be @code{None} for gaps. +speculatively. +@end defvar + +If an error occured during recording or decoding a recording, this error is +represented by a @code{gdb.RecordGap} object in the instruction list. It has +the following attributes: + +@defvar RecordGap.number +An integer identifying this gap. @code{number} corresponds to the numbers seen +in @code{record instruction-history} (@pxref{Process Record and Replay}). +@end defvar + +@defvar RecordGap.error_code +A numerical representation of the reason for the gap. The value is specific to +the current recording method. +@end defvar + +@defvar RecordGap.error_string +A human readable string with the reason for the gap. @end defvar The attributes and methods of function call objects depend on the @@ -3221,8 +3230,8 @@ An integer representing the function call's stack level. May be @end defvar @defvar BtraceFunctionCall.instructions -A list of @code{gdb.BtraceInstruction} objects associated with this function -call. +A list of @code{gdb.BtraceInstruction} or @code{gdb.RecordGap} objects +associated with this function call. @end defvar @defvar BtraceFunctionCall.up diff --git a/gdb/python/py-record-btrace.c b/gdb/python/py-record-btrace.c index 5f9264d4e89..1e463ffe0fe 100644 --- a/gdb/python/py-record-btrace.c +++ b/gdb/python/py-record-btrace.c @@ -126,12 +126,30 @@ btpy_new (ptid_t ptid, Py_ssize_t number, PyTypeObject* type) return (PyObject *) obj; } -/* Create a new gdb.BtraceInstruction object. */ +/* Looks at the recorded item with the number NUMBER and create a + gdb.BtraceInstruction or gdb.RecordGap object for it accordingly. */ static PyObject * -btpy_insn_new (ptid_t ptid, Py_ssize_t number) +btpy_insn_or_gap_new (const thread_info *tinfo, Py_ssize_t number) { - return btpy_new (ptid, number, &btpy_insn_type); + btrace_insn_iterator iter; + int err_code; + + btrace_find_insn_by_number (&iter, &tinfo->btrace, number); + err_code = btrace_insn_get_error (&iter); + + if (err_code != 0) + { + const btrace_config *config; + const char *err_string; + + config = btrace_conf (&tinfo->btrace); + err_string = btrace_decode_error (config->format, err_code); + + return recpy_gap_new (err_code, err_string, number); + } + + return btpy_new (tinfo->ptid, number, &btpy_insn_type); } /* Create a new gdb.BtraceFunctionCall object. */ @@ -185,26 +203,6 @@ btpy_hash (PyObject *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.sal [gdb.Symtab_and_line]. Return the SAL associated with this instruction. */ @@ -220,7 +218,7 @@ btpy_insn_sal (PyObject *self, void *closure) insn = btrace_insn_get (&iter); if (insn == NULL) - Py_RETURN_NONE; + return PyErr_Format (gdbpy_gdb_error, _("No such instruction.")); TRY { @@ -249,7 +247,7 @@ btpy_insn_pc (PyObject *self, void *closure) insn = btrace_insn_get (&iter); if (insn == NULL) - Py_RETURN_NONE; + return PyErr_Format (gdbpy_gdb_error, _("No such instruction.")); return gdb_py_long_from_ulongest (insn->pc); } @@ -268,7 +266,7 @@ btpy_insn_size (PyObject *self, void *closure) insn = btrace_insn_get (&iter); if (insn == NULL) - Py_RETURN_NONE; + return PyErr_Format (gdbpy_gdb_error, _("No such instruction.")); return PyInt_FromLong (insn->size); } @@ -287,7 +285,7 @@ btpy_insn_is_speculative (PyObject *self, void *closure) insn = btrace_insn_get (&iter); if (insn == NULL) - Py_RETURN_NONE; + return PyErr_Format (gdbpy_gdb_error, _("No such instruction.")); if (insn->flags & BTRACE_INSN_FLAG_SPECULATIVE) Py_RETURN_TRUE; @@ -311,7 +309,7 @@ btpy_insn_data (PyObject *self, void *closure) insn = btrace_insn_get (&iter); if (insn == NULL) - Py_RETURN_NONE; + return PyErr_Format (gdbpy_gdb_error, _("No such instruction.")); TRY { @@ -354,14 +352,7 @@ btpy_insn_decode (PyObject *self, void *closure) 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)); - } + return PyErr_Format (gdbpy_gdb_error, _("No such instruction.")); TRY { @@ -765,8 +756,8 @@ recpy_bt_replay_position (PyObject *self, void *closure) if (tinfo->btrace.replay == NULL) Py_RETURN_NONE; - return btpy_insn_new (record->ptid, - btrace_insn_number (tinfo->btrace.replay)); + return btpy_insn_or_gap_new (tinfo, + btrace_insn_number (tinfo->btrace.replay)); } /* Implementation of @@ -788,7 +779,7 @@ recpy_bt_begin (PyObject *self, void *closure) Py_RETURN_NONE; btrace_insn_begin (&iterator, &tinfo->btrace); - return btpy_insn_new (record->ptid, btrace_insn_number (&iterator)); + return btpy_insn_or_gap_new (tinfo, btrace_insn_number (&iterator)); } /* Implementation of @@ -810,7 +801,7 @@ recpy_bt_end (PyObject *self, void *closure) Py_RETURN_NONE; btrace_insn_end (&iterator, &tinfo->btrace); - return btpy_insn_new (record->ptid, btrace_insn_number (&iterator)); + return btpy_insn_or_gap_new (tinfo, btrace_insn_number (&iterator)); } /* Implementation of @@ -914,7 +905,6 @@ recpy_bt_goto (PyObject *self, PyObject *args) struct gdb_PyGetSetDef btpy_insn_getset[] = { { "number", btpy_number, NULL, "instruction number", NULL}, - { "error", btpy_insn_error, NULL, "error number for gaps", NULL}, { "sal", btpy_insn_sal, NULL, "associated symbol and line", NULL}, { "pc", btpy_insn_pc, NULL, "instruction address", NULL}, { "data", btpy_insn_data, NULL, "raw instruction data", NULL}, diff --git a/gdb/python/py-record.c b/gdb/python/py-record.c index 05f028c033c..1be66a2de80 100644 --- a/gdb/python/py-record.c +++ b/gdb/python/py-record.c @@ -29,6 +29,27 @@ static PyTypeObject recpy_record_type = { PyVarObject_HEAD_INIT (NULL, 0) }; +/* Python RecordGap type. */ + +PyTypeObject recpy_gap_type = { + PyVarObject_HEAD_INIT (NULL, 0) +}; + +/* Python RecordGap object. */ +typedef struct +{ + PyObject_HEAD + + /* Reason code. */ + int reason_code; + + /* Reason message. */ + const char *reason_string; + + /* Element number. */ + Py_ssize_t number; +} recpy_gap_object; + /* Implementation of record.method. */ static PyObject * @@ -139,6 +160,54 @@ recpy_end (PyObject *self, void* closure) return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); } +/* Create a new gdb.RecordGap object. */ + +PyObject * +recpy_gap_new (int reason_code, const char *reason_string, Py_ssize_t number) +{ + recpy_gap_object * const obj = PyObject_New (recpy_gap_object, + &recpy_gap_type); + + if (obj == NULL) + return NULL; + + obj->reason_code = reason_code; + obj->reason_string = reason_string; + obj->number = number; + + return (PyObject *) obj; +} + +/* Implementation of RecordGap.number [int]. */ + +static PyObject * +recpy_gap_number (PyObject *self, void *closure) +{ + const recpy_gap_object * const obj = (const recpy_gap_object *) self; + + return PyInt_FromSsize_t (obj->number); +} + +/* Implementation of RecordGap.error_code [int]. */ + +static PyObject * +recpy_gap_reason_code (PyObject *self, void *closure) +{ + const recpy_gap_object * const obj = (const recpy_gap_object *) self; + + return PyInt_FromLong (obj->reason_code); +} + +/* Implementation of RecordGap.error_string [str]. */ + +static PyObject * +recpy_gap_reason_string (PyObject *self, void *closure) +{ + const recpy_gap_object * const obj = (const recpy_gap_object *) self; + + return PyString_FromString (obj->reason_string); +} + /* Record method list. */ static PyMethodDef recpy_record_methods[] = { @@ -167,6 +236,15 @@ the current instruction and is used for e.g. record.goto (record.end).", NULL }, { NULL } }; +/* RecordGap member list. */ + +static gdb_PyGetSetDef recpy_gap_getset[] = { + { "number", recpy_gap_number, NULL, "element number", NULL}, + { "reason_code", recpy_gap_reason_code, NULL, "reason code", NULL}, + { "reason_string", recpy_gap_reason_string, NULL, "reason string", NULL}, + { NULL } +}; + /* Sets up the record API in the gdb module. */ int @@ -180,7 +258,18 @@ gdbpy_initialize_record (void) recpy_record_type.tp_methods = recpy_record_methods; recpy_record_type.tp_getset = recpy_record_getset; - return PyType_Ready (&recpy_record_type); + recpy_gap_type.tp_new = PyType_GenericNew; + recpy_gap_type.tp_flags = Py_TPFLAGS_DEFAULT; + recpy_gap_type.tp_basicsize = sizeof (recpy_gap_object); + recpy_gap_type.tp_name = "gdb.RecordGap"; + recpy_gap_type.tp_doc = "GDB recorded gap object"; + recpy_gap_type.tp_getset = recpy_gap_getset; + + if (PyType_Ready (&recpy_record_type) < 0 + || PyType_Ready (&recpy_gap_type) < 0) + return -1; + else + return 0; } /* Implementation of gdb.start_recording (method) -> gdb.Record. */ diff --git a/gdb/python/py-record.h b/gdb/python/py-record.h index c386ba23b0c..5cf7a028937 100644 --- a/gdb/python/py-record.h +++ b/gdb/python/py-record.h @@ -37,4 +37,8 @@ typedef struct enum record_method method; } recpy_record_object; +/* Create a new gdb.RecordGap object. */ +extern PyObject *recpy_gap_new (int reason_code, const char *reason_string, + Py_ssize_t number); + #endif /* GDB_PY_RECORD_H */ diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 5312707e78a..f30fa48e6c0 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2017-05-01 Tim Wiederhake + + * gdb.python/py-record-btrace.exp: Remove test for + gdb.BtraceInstruction.error. + 2017-05-01 Tim Wiederhake * gdb.python/py-record-btrace.exp: Remove Record.ptid test. diff --git a/gdb/testsuite/gdb.python/py-record-btrace.exp b/gdb/testsuite/gdb.python/py-record-btrace.exp index ecc8bdf5081..045e63eb389 100644 --- a/gdb/testsuite/gdb.python/py-record-btrace.exp +++ b/gdb/testsuite/gdb.python/py-record-btrace.exp @@ -81,7 +81,6 @@ with_test_prefix "replay end" { with_test_prefix "instruction " { gdb_test "python print(i.number)" "1" - gdb_test "python print(i.error)" "None" gdb_test "python print(i.sal)" "symbol and line for .*" gdb_test "python print(i.pc)" "$decimal" if { $gdb_py_is_py3k == 0 } { -- 2.30.2