Python: Introduce gdb.RecordGap class
authorTim Wiederhake <tim.wiederhake@intel.com>
Tue, 2 May 2017 09:35:54 +0000 (11:35 +0200)
committerTim Wiederhake <tim.wiederhake@intel.com>
Tue, 2 May 2017 09:35:54 +0000 (11:35 +0200)
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
gdb/doc/ChangeLog
gdb/doc/python.texi
gdb/python/py-record-btrace.c
gdb/python/py-record.c
gdb/python/py-record.h
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.python/py-record-btrace.exp

index de0b6d47e3afca2191f63f88d2cad20f351c6118..9b80425807c587017f5ef1bc42d125bc6b318df6 100644 (file)
@@ -1,3 +1,21 @@
+2017-05-01  Tim Wiederhake  <tim.wiederhake@intel.com>
+
+       * 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  <tim.wiederhake@intel.com>
 
        * python/py-record.c (recpy_ptid): Remove.
index c13418a07cd3309c444300233bbeb0dbcc133e61..202fbe320c1353c99c59216d3bb748fcf84ecea1 100644 (file)
@@ -1,3 +1,9 @@
+2017-05-01  Tim Wiederhake  <tim.wiederhake@intel.com>
+
+       * python.texi (Recording in Python): Add documentation for
+       gdb.RecordGap and remove documentation of special cases for
+       gdb.BtraceInstruction.
+
 2017-05-01  Tim Wiederhake  <tim.wiederhake@intel.com>
 
        * python.texi (Recording in Python): Remove Record.ptid defvar.
index 8c246a43719b685c99d1704c83d878c9cbdaf6e6..828806a2307c8b9dc99e34f0a23ae031ab93d7f9 100644 (file)
@@ -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
index 5f9264d4e89990f32a30f65fc924f2a06fe6e741..1e463ffe0fe74be45e8b0de37bf59dc60e6d8934 100644 (file)
@@ -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},
index 05f028c033c80e0f85f2415c32bbc5fc92ef9553..1be66a2de80d24aa94394c587d14dc7c7b6471cc 100644 (file)
@@ -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.  */
index c386ba23b0c01c434b78c1db7fba78703dc91395..5cf7a0289372370cd6bcada634572839294c3e47 100644 (file)
@@ -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 */
index 5312707e78a2fe497a603e63158fd88e5f5e5f13..f30fa48e6c007895d9489239ea8041af6c7d1313 100644 (file)
@@ -1,3 +1,8 @@
+2017-05-01  Tim Wiederhake  <tim.wiederhake@intel.com>
+
+       * gdb.python/py-record-btrace.exp: Remove test for
+       gdb.BtraceInstruction.error.
+
 2017-05-01  Tim Wiederhake  <tim.wiederhake@intel.com>
 
        * gdb.python/py-record-btrace.exp: Remove Record.ptid test.
index ecc8bdf5081b067807dc044cbcff5575204d08fc..045e63eb389983fd07f094e3c173a3d163594a6a 100644 (file)
@@ -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 } {