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

Message ID 20230718115637.3531-6-felix.willgerodt@intel.com
State New
Headers
Series Extensions for PTWRITE |

Commit Message

Willgerodt, Felix July 18, 2023, 11:56 a.m. UTC
  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(-)
  

Comments

Terekhov, Mikhail via Gdb-patches July 25, 2023, 1:30 p.m. UTC | #1
Hello Felix,

>-  btrace_find_insn_by_number (&iter, &tinfo->btrace, number);
>+  if (btrace_find_insn_by_number (&iter, &tinfo->btrace, number) == 0)

Nit: how about storing the result in a local variable to avoid side-effects
in the if expression?

>+  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 ());

Shouldn't we check the insn class, too?


>@@ -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);
> }

Why do we need to change the order?


Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
  
Terekhov, Mikhail via Gdb-patches Aug. 7, 2023, 9:08 a.m. UTC | #2
> -----Original Message-----
> From: Metzger, Markus T <markus.t.metzger@intel.com>
> Sent: Dienstag, 25. Juli 2023 15:30
> To: Willgerodt, Felix <felix.willgerodt@intel.com>; gdb-patches@sourceware.org;
> simark@simark.ca
> Subject: RE: [PATCH v10 05/10] python: Introduce gdb.RecordAuxiliary class.
> 
> Hello Felix,
> 
> >-  btrace_find_insn_by_number (&iter, &tinfo->btrace, number);
> >+  if (btrace_find_insn_by_number (&iter, &tinfo->btrace, number) == 0)
> 
> Nit: how about storing the result in a local variable to avoid side-effects
> in the if expression?

What side effects do you have in mind? I checked the function and couldn't find
any. And I just copied this from the existing btrace_insn_from_recpy_insn.

> >+  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 ());
> 
> Shouldn't we check the insn class, too?

We checked the python object type above already.
So this would only catch cases in which something went horribly wrong
and the python trace doesn't match the internal trace.
I think right now that can never be the case, but it might be a good
sanity check, so I will add it.

> 
> >@@ -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);
> > }
> 
> Why do we need to change the order?

Because we no longer call recpy_insn_new only for recpy_insn_type but also for
recpy_aux_type.

Regards,
Felix

 


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
  
Terekhov, Mikhail via Gdb-patches Aug. 8, 2023, 9:37 a.m. UTC | #3
Hello Felix,

>> >@@ -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);
>> > }
>>
>> Why do we need to change the order?
>
>Because we no longer call recpy_insn_new only for recpy_insn_type but also for
>recpy_aux_type.

I think it would be clearer if we checked each type separately and errored
out by default.  Like a switch.

Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
  
Terekhov, Mikhail via Gdb-patches Aug. 9, 2023, 9:32 a.m. UTC | #4
> >> >@@ -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);
> >> > }
> >>
> >> Why do we need to change the order?
> >
> >Because we no longer call recpy_insn_new only for recpy_insn_type but also for
> >recpy_aux_type.
> 
> I think it would be clearer if we checked each type separately and errored
> out by default.  Like a switch.

I can put something like this:

  if (obj->element_type == &recpy_func_type)
    return recpy_func_new (obj->thread, RECORD_METHOD_BTRACE, number);
  else if (obj->element_type == &recpy_insn_type
	   || obj->element_type == &recpy_aux_type)
    return btpy_item_new (obj->thread, number);
  else
    return PyErr_Format (gdbpy_gdb_error, _("Not a valid BtraceList object."));

I don't think we can reach the error case right now though.

Felix



Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
  
Terekhov, Mikhail via Gdb-patches Aug. 9, 2023, 10 a.m. UTC | #5
Hello Felix,

>> >> >@@ -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);
>> >> > }
>> >>
>> >> Why do we need to change the order?
>> >
>> >Because we no longer call recpy_insn_new only for recpy_insn_type but also for
>> >recpy_aux_type.
>>
>> I think it would be clearer if we checked each type separately and errored
>> out by default.  Like a switch.
>
>I can put something like this:
>
>  if (obj->element_type == &recpy_func_type)
>    return recpy_func_new (obj->thread, RECORD_METHOD_BTRACE, number);
>  else if (obj->element_type == &recpy_insn_type
>	   || obj->element_type == &recpy_aux_type)
>    return btpy_item_new (obj->thread, number);
>  else
>    return PyErr_Format (gdbpy_gdb_error, _("Not a valid BtraceList object."));
>
>I don't think we can reach the error case right now though.

We will reach the error case when we add a new element type and forget to
update this function.

LGTM.

Maybe it makes sense to split btpy_item_new() again so we do not accidentally
create an insn object with a recpy_aux_type or vice versa.

How about aux objects in the function call history?  This is supported on CLI if
I read patch 3 correctly.

Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
  
Terekhov, Mikhail via Gdb-patches Aug. 30, 2023, 8:39 a.m. UTC | #6
> -----Original Message-----
> From: Metzger, Markus T <markus.t.metzger@intel.com>
> Sent: Mittwoch, 9. August 2023 12:01
> To: Willgerodt, Felix <felix.willgerodt@intel.com>
> Cc: gdb-patches@sourceware.org; simark@simark.ca
> Subject: RE: [PATCH v10 05/10] python: Introduce gdb.RecordAuxiliary class.
> 
> Hello Felix,
> 
> >> >> >@@ -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);
> >> >> > }
> >> >>
> >> >> Why do we need to change the order?
> >> >
> >> >Because we no longer call recpy_insn_new only for recpy_insn_type but also
> for
> >> >recpy_aux_type.
> >>
> >> I think it would be clearer if we checked each type separately and errored
> >> out by default.  Like a switch.
> >
> >I can put something like this:
> >
> >  if (obj->element_type == &recpy_func_type)
> >    return recpy_func_new (obj->thread, RECORD_METHOD_BTRACE, number);
> >  else if (obj->element_type == &recpy_insn_type
> >	   || obj->element_type == &recpy_aux_type)
> >    return btpy_item_new (obj->thread, number);
> >  else
> >    return PyErr_Format (gdbpy_gdb_error, _("Not a valid BtraceList object."));
> >
> >I don't think we can reach the error case right now though.
> 
> We will reach the error case when we add a new element type and forget to
> update this function.
> 
> LGTM.
> 
> Maybe it makes sense to split btpy_item_new() again so we do not accidentally
> create an insn object with a recpy_aux_type or vice versa.
> 
> How about aux objects in the function call history?  This is supported on CLI if
> I read patch 3 correctly.

Yes, it is showing in CLI. In Python it is a bit more hidden, but also accessible.
The function call history is a list of RecoredFunctionSegment objects. No aux
objects there. But, the RecordFunctionSegment objects all have the instructions
listed as a member:

>>> r = gdb.current_recording()
>>> h = r.function_call_history
>>> h[1]
<gdb.RecordFunctionSegment object at 0x7f76b4587210>
>>> h[1].instructions
<gdb.BtraceObjectList object at 0x7f76ac0abb30>
>>> h[1].instructions[7]
<gdb.RecordInstruction object at 0x7f769c257a50>
>>> h[1].instructions[8]
<gdb.RecordAuxiliary object at 0x7f76b455c8d0>

So a user can still access all the same information. In GDB code, we basically do
the same, just that in the CLI variant of record function-call-history GDB also
searches the insn list for aux objects, and prints them. In Python the user has to
do that himself right now.

I don't think putting aux objects also in the function_call_history would be a
good idea. That would duplicate things.

Anyway, I will change it as we agreed on above.

Felix
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
  

Patch

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 */