[PATCH v10 05/10] python: Introduce gdb.RecordAuxiliary class.

Felix Willgerodt via Gdb-patches
2023-07-18T11:57:57Z

Auxiliary instructions are no real instructions and get their own object
class, similar to gaps. gdb.Record.instruction_history is now possibly a
list of gdb.RecordInstruction, gdb.RecordGap or gdb.RecordAuxiliary
objects.

This patch is in preparation for the new ptwrite feature, which is based on
auxiliary instructions.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
gdb/doc/python.texi | 13 ++++++
gdb/python/py-record-btrace.c | 81 +++++++++++++++++++++++++++++------
gdb/python/py-record-btrace.h | 3 ++
gdb/python/py-record.c | 62 +++++++++++++++++++++++++--
gdb/python/py-record.h | 7 +++
5 files changed, 150 insertions(+), 16 deletions(-)

diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 9a342f34bf0..46d56464644 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -4106,6 +4106,19 @@ the current recording method.
A human readable string with the reason for the gap.
@end defvar

+Some @value{GDBN} features write auxiliary information into the execution
+history. This information is represented by a @code{gdb.RecordAuxiliary} object
+in the instruction list. It has the following attributes:
+
+@defvar RecordAuxiliary.number
+An integer identifying this auxiliary. @var{number} corresponds to the numbers
+seen in @code{record instruction-history} (@pxref{Process Record and Replay}).
+@end defvar
+
+@defvar RecordAuxiliary.data
+A string representation of the auxiliary data.
+@end defvar
+
A @code{gdb.RecordFunctionSegment} object has the following attributes:

@defvar RecordFunctionSegment.number
diff --git a/gdb/python/py-record-btrace.c b/gdb/python/py-record-btrace.c
index 7978f3332c6..4a2a61e9b91 100644
--- a/gdb/python/py-record-btrace.c
+++ b/gdb/python/py-record-btrace.c
@@ -45,7 +45,8 @@ struct btpy_list_object {
/* Stride size. */
Py_ssize_t step;

- /* Either &BTPY_CALL_TYPE or &RECPY_INSN_TYPE. */
+ /* Either &recpy_func_type, &recpy_insn_type, &recpy_aux_type or
+ &recpy_gap_type. */
PyTypeObject* element_type;
};

@@ -141,15 +142,21 @@ btrace_func_from_recpy_func (const PyObject * const pyobject)
}

/* Looks at the recorded item with the number NUMBER and create a
- gdb.RecordInstruction or gdb.RecordGap object for it accordingly. */
+ gdb.RecordInstruction, gdb.RecordGap or gdb.RecordAuxiliary object
+ for it accordingly. */

static PyObject *
-btpy_insn_or_gap_new (thread_info *tinfo, Py_ssize_t number)
+btpy_item_new (thread_info *tinfo, Py_ssize_t number)
{
btrace_insn_iterator iter;
int err_code;

- btrace_find_insn_by_number (&iter, &tinfo->btrace, number);
+ if (btrace_find_insn_by_number (&iter, &tinfo->btrace, number) == 0)
+ {
+ PyErr_Format (gdbpy_gdb_error, _("No such instruction."));
+ return nullptr;
+ }
+
err_code = btrace_insn_get_error (&iter);

if (err_code != 0)
@@ -163,6 +170,12 @@ btpy_insn_or_gap_new (thread_info *tinfo, Py_ssize_t number)
return recpy_gap_new (err_code, err_string, number);
}

+ const struct btrace_insn *insn = btrace_insn_get (&iter);
+ gdb_assert (insn != nullptr);
+
+ if (insn->iclass == BTRACE_INSN_AUX)
+ return recpy_aux_new (tinfo, RECORD_METHOD_BTRACE, number);
+
return recpy_insn_new (tinfo, RECORD_METHOD_BTRACE, number);
}

@@ -424,6 +437,48 @@ recpy_bt_func_next (PyObject *self, void *closure)
RECORD_METHOD_BTRACE, func->next);
}

+/* Implementation of Auxiliary.data [str] for btrace. */
+
+PyObject *
+recpy_bt_aux_data (PyObject *self, void *closure)
+{
+ const btrace_insn *insn;
+ const recpy_element_object *obj;
+ thread_info *tinfo;
+ btrace_insn_iterator iter;
+
+ if (Py_TYPE (self) != &recpy_aux_type)
+ {
+ PyErr_Format (gdbpy_gdb_error, _("Must be a gdb.Auxiliary."));
+ return NULL;
+ }
+
+ obj = (const recpy_element_object *) self;
+ tinfo = obj->thread;
+
+ if (tinfo == NULL || btrace_is_empty (tinfo))
+ {
+ PyErr_Format (gdbpy_gdb_error, _("No such auxiliary object."));
+ return NULL;
+ }
+
+ if (btrace_find_insn_by_number (&iter, &tinfo->btrace, obj->number) == 0)
+ {
+ PyErr_Format (gdbpy_gdb_error, _("No such auxiliary object."));
+ return NULL;
+ }
+
+ insn = btrace_insn_get (&iter);
+ if (insn == NULL)
+ {
+ PyErr_Format (gdbpy_gdb_error, _("Not a valid auxiliary object."));
+ return NULL;
+ }
+
+ return PyUnicode_FromString
+ (iter.btinfo->aux_data.at (insn->aux_data_index).c_str ());
+}
+
/* Implementation of BtraceList.__len__ (self) -> int. */

static Py_ssize_t
@@ -440,8 +495,9 @@ btpy_list_length (PyObject *self)
}

/* Implementation of
- BtraceList.__getitem__ (self, key) -> BtraceInstruction and
- BtraceList.__getitem__ (self, key) -> BtraceFunctionCall. */
+ BtraceList.__getitem__ (self, key) -> BtraceInstruction,
+ BtraceList.__getitem__ (self, key) -> BtraceFunctionCall,
+ BtraceList.__getitem__ (self, key) -> BtraceAuxiliary. */

static PyObject *
btpy_list_item (PyObject *self, Py_ssize_t index)
@@ -455,10 +511,10 @@ btpy_list_item (PyObject *self, Py_ssize_t index)

number = obj->first + (obj->step * index);

- if (obj->element_type == &recpy_insn_type)
- return recpy_insn_new (obj->thread, RECORD_METHOD_BTRACE, number);
- else
+ if (obj->element_type == &recpy_func_type)
return recpy_func_new (obj->thread, RECORD_METHOD_BTRACE, number);
+ else
+ return btpy_item_new (obj->thread, number);
}

/* Implementation of BtraceList.__getitem__ (self, slice) -> BtraceList. */
@@ -645,8 +701,7 @@ recpy_bt_replay_position (PyObject *self, void *closure)
if (tinfo->btrace.replay == NULL)
Py_RETURN_NONE;

- return btpy_insn_or_gap_new (tinfo,
- btrace_insn_number (tinfo->btrace.replay));
+ return btpy_item_new (tinfo, btrace_insn_number (tinfo->btrace.replay));
}

/* Implementation of
@@ -668,7 +723,7 @@ recpy_bt_begin (PyObject *self, void *closure)
Py_RETURN_NONE;

btrace_insn_begin (&iterator, &tinfo->btrace);
- return btpy_insn_or_gap_new (tinfo, btrace_insn_number (&iterator));
+ return btpy_item_new (tinfo, btrace_insn_number (&iterator));
}

/* Implementation of
@@ -690,7 +745,7 @@ recpy_bt_end (PyObject *self, void *closure)
Py_RETURN_NONE;

btrace_insn_end (&iterator, &tinfo->btrace);
- return btpy_insn_or_gap_new (tinfo, btrace_insn_number (&iterator));
+ return btpy_item_new (tinfo, btrace_insn_number (&iterator));
}

/* Implementation of
diff --git a/gdb/python/py-record-btrace.h b/gdb/python/py-record-btrace.h
index 0af8153b715..0ca3da8e86f 100644
--- a/gdb/python/py-record-btrace.h
+++ b/gdb/python/py-record-btrace.h
@@ -88,4 +88,7 @@ extern PyObject *recpy_bt_func_prev (PyObject *self, void *closure);
/* Implementation of RecordFunctionSegment.next [RecordFunctionSegment]. */
extern PyObject *recpy_bt_func_next (PyObject *self, void *closure);

+/* Implementation of RecordAuxiliary.decoded [str]. */
+extern PyObject *recpy_bt_aux_data (PyObject *self, void *closure);
+
#endif /* PYTHON_PY_RECORD_BTRACE_H */
diff --git a/gdb/python/py-record.c b/gdb/python/py-record.c
index c96c649b29c..c093cdaf3d6 100644
--- a/gdb/python/py-record.c
+++ b/gdb/python/py-record.c
@@ -49,6 +49,12 @@ static PyTypeObject recpy_gap_type = {
PyVarObject_HEAD_INIT (NULL, 0)
};

+/* Python RecordAuxiliary type. */
+
+PyTypeObject recpy_aux_type = {
+ PyVarObject_HEAD_INIT (nullptr, 0)
+};
+
/* Python RecordGap object. */
struct recpy_gap_object
{
@@ -390,8 +396,8 @@ recpy_element_hash (PyObject *self)
return obj->number;
}

-/* Implementation of operator == and != of RecordInstruction and
- RecordFunctionSegment. */
+/* Implementation of operator == and != of RecordInstruction,
+ RecordFunctionSegment and RecordAuxiliary. */

static PyObject *
recpy_element_richcompare (PyObject *self, PyObject *other, int op)
@@ -479,6 +485,38 @@ recpy_gap_reason_string (PyObject *self, void *closure)
return PyUnicode_FromString (obj->reason_string);
}

+/* Create a new gdb.Auxiliary object. */
+
+PyObject *
+recpy_aux_new (thread_info *thread, enum record_method method,
+ Py_ssize_t number)
+{
+ recpy_element_object * const obj = PyObject_New (recpy_element_object,
+ &recpy_aux_type);
+
+ if (obj == NULL)
+ return NULL;
+
+ obj->thread = thread;
+ obj->method = method;
+ obj->number = number;
+
+ return (PyObject *) obj;
+}
+
+/* Implementation of Auxiliary.data [buffer]. */
+
+static PyObject *
+recpy_aux_data (PyObject *self, void *closure)
+{
+ const recpy_element_object * const obj = (recpy_element_object *) self;
+
+ if (obj->method == RECORD_METHOD_BTRACE)
+ return recpy_bt_aux_data (self, closure);
+
+ return PyErr_Format (PyExc_NotImplementedError, _("Not implemented."));
+}
+
/* Record method list. */

static PyMethodDef recpy_record_methods[] = {
@@ -544,6 +582,14 @@ static gdb_PyGetSetDef recpy_gap_getset[] = {
{ NULL }
};

+/* RecordAuxiliary member list. */
+
+static gdb_PyGetSetDef recpy_aux_getset[] = {
+ { "number", recpy_element_number, nullptr, "element number", nullptr},
+ { "data", recpy_aux_data, nullptr, "data", nullptr},
+ { nullptr }
+};
+
/* Sets up the record API in the gdb module. */

static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
@@ -583,10 +629,20 @@ gdbpy_initialize_record (void)
recpy_gap_type.tp_doc = "GDB recorded gap object";
recpy_gap_type.tp_getset = recpy_gap_getset;

+ recpy_aux_type.tp_new = PyType_GenericNew;
+ recpy_aux_type.tp_flags = Py_TPFLAGS_DEFAULT;
+ recpy_aux_type.tp_basicsize = sizeof (recpy_element_object);
+ recpy_aux_type.tp_name = "gdb.RecordAuxiliary";
+ recpy_aux_type.tp_doc = "GDB recorded auxiliary object";
+ recpy_aux_type.tp_getset = recpy_aux_getset;
+ recpy_aux_type.tp_richcompare = recpy_element_richcompare;
+ recpy_aux_type.tp_hash = recpy_element_hash;
+
if (PyType_Ready (&recpy_record_type) < 0
|| PyType_Ready (&recpy_insn_type) < 0
|| PyType_Ready (&recpy_func_type) < 0
- || PyType_Ready (&recpy_gap_type) < 0)
+ || PyType_Ready (&recpy_gap_type) < 0
+ || PyType_Ready (&recpy_aux_type) < 0)
return -1;
else
return 0;
diff --git a/gdb/python/py-record.h b/gdb/python/py-record.h
index 6eec71e06e7..63e59798a02 100644
--- a/gdb/python/py-record.h
+++ b/gdb/python/py-record.h
@@ -59,6 +59,9 @@ extern PyTypeObject recpy_insn_type;
/* Python RecordFunctionSegment type. */
extern PyTypeObject recpy_func_type;

+/* Python RecordAuxiliary type. */
+extern PyTypeObject recpy_aux_type;
+
/* Create a new gdb.RecordInstruction object. */
extern PyObject *recpy_insn_new (thread_info *thread, enum record_method method,
Py_ssize_t number);
@@ -71,4 +74,8 @@ extern PyObject *recpy_func_new (thread_info *thread, enum record_method method,
extern PyObject *recpy_gap_new (int reason_code, const char *reason_string,
Py_ssize_t number);

+/* Create a new gdb.RecordGap object. */
+extern PyObject *recpy_aux_new (thread_info *thread, enum record_method method,
+ Py_ssize_t number);
+
#endif /* PYTHON_PY_RECORD_H */
--
2.34.1