Home | History | Annotate | Line # | Download | only in python
      1 /* Python interface to btrace instruction history.
      2 
      3    Copyright 2016-2024 Free Software Foundation, Inc.
      4 
      5    This file is part of GDB.
      6 
      7    This program is free software; you can redistribute it and/or modify
      8    it under the terms of the GNU General Public License as published by
      9    the Free Software Foundation; either version 3 of the License, or
     10    (at your option) any later version.
     11 
     12    This program is distributed in the hope that it will be useful,
     13    but WITHOUT ANY WARRANTY; without even the implied warranty of
     14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15    GNU General Public License for more details.
     16 
     17    You should have received a copy of the GNU General Public License
     18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     19 
     20 #include "gdbcore.h"
     21 #include "cli/cli-cmds.h"
     22 #include "gdbthread.h"
     23 #include "btrace.h"
     24 #include "py-record.h"
     25 #include "py-record-btrace.h"
     26 #include "record-btrace.h"
     27 #include "disasm.h"
     28 #include "gdbarch.h"
     29 
     30 /* Python object for btrace record lists.  */
     31 
     32 struct btpy_list_object {
     33   PyObject_HEAD
     34 
     35   /* The thread this list belongs to.  */
     36   thread_info *thread;
     37 
     38   /* The first index being part of this list.  */
     39   Py_ssize_t first;
     40 
     41   /* The last index begin part of this list.  */
     42   Py_ssize_t last;
     43 
     44   /* Stride size.  */
     45   Py_ssize_t step;
     46 
     47   /* Either &BTPY_CALL_TYPE or &RECPY_INSN_TYPE.  */
     48   PyTypeObject* element_type;
     49 };
     50 
     51 /* Python type for btrace lists.  */
     52 
     53 static PyTypeObject btpy_list_type = {
     54   PyVarObject_HEAD_INIT (NULL, 0)
     55 };
     56 
     57 /* Returns either a btrace_insn for the given Python gdb.RecordInstruction
     58    object or sets an appropriate Python exception and returns NULL.  */
     59 
     60 static const btrace_insn *
     61 btrace_insn_from_recpy_insn (const PyObject * const pyobject)
     62 {
     63   const btrace_insn *insn;
     64   const recpy_element_object *obj;
     65   thread_info *tinfo;
     66   btrace_insn_iterator iter;
     67 
     68   if (Py_TYPE (pyobject) != &recpy_insn_type)
     69     {
     70       PyErr_Format (gdbpy_gdb_error, _("Must be gdb.RecordInstruction"));
     71       return NULL;
     72     }
     73 
     74   obj = (const recpy_element_object *) pyobject;
     75   tinfo = obj->thread;
     76 
     77   if (tinfo == NULL || btrace_is_empty (tinfo))
     78     {
     79       PyErr_Format (gdbpy_gdb_error, _("No such instruction."));
     80       return NULL;
     81     }
     82 
     83   if (btrace_find_insn_by_number (&iter, &tinfo->btrace, obj->number) == 0)
     84     {
     85       PyErr_Format (gdbpy_gdb_error, _("No such instruction."));
     86       return NULL;
     87     }
     88 
     89   insn = btrace_insn_get (&iter);
     90   if (insn == NULL)
     91     {
     92       PyErr_Format (gdbpy_gdb_error, _("Not a valid instruction."));
     93       return NULL;
     94     }
     95 
     96   return insn;
     97 }
     98 
     99 /* Returns either a btrace_function for the given Python
    100    gdb.RecordFunctionSegment object or sets an appropriate Python exception and
    101    returns NULL.  */
    102 
    103 static const btrace_function *
    104 btrace_func_from_recpy_func (const PyObject * const pyobject)
    105 {
    106   const btrace_function *func;
    107   const recpy_element_object *obj;
    108   thread_info *tinfo;
    109   btrace_call_iterator iter;
    110 
    111   if (Py_TYPE (pyobject) != &recpy_func_type)
    112     {
    113       PyErr_Format (gdbpy_gdb_error, _("Must be gdb.RecordFunctionSegment"));
    114       return NULL;
    115     }
    116 
    117   obj = (const recpy_element_object *) pyobject;
    118   tinfo = obj->thread;
    119 
    120   if (tinfo == NULL || btrace_is_empty (tinfo))
    121     {
    122       PyErr_Format (gdbpy_gdb_error, _("No such function segment."));
    123       return NULL;
    124     }
    125 
    126   if (btrace_find_call_by_number (&iter, &tinfo->btrace, obj->number) == 0)
    127     {
    128       PyErr_Format (gdbpy_gdb_error, _("No such function segment."));
    129       return NULL;
    130     }
    131 
    132   func = btrace_call_get (&iter);
    133   if (func == NULL)
    134     {
    135       PyErr_Format (gdbpy_gdb_error, _("Not a valid function segment."));
    136       return NULL;
    137     }
    138 
    139   return func;
    140 }
    141 
    142 /* Looks at the recorded item with the number NUMBER and create a
    143    gdb.RecordInstruction or gdb.RecordGap object for it accordingly.  */
    144 
    145 static PyObject *
    146 btpy_insn_or_gap_new (thread_info *tinfo, Py_ssize_t number)
    147 {
    148   btrace_insn_iterator iter;
    149   int err_code;
    150 
    151   btrace_find_insn_by_number (&iter, &tinfo->btrace, number);
    152   err_code = btrace_insn_get_error (&iter);
    153 
    154   if (err_code != 0)
    155     {
    156       const btrace_config *config;
    157       const char *err_string;
    158 
    159       config = btrace_conf (&tinfo->btrace);
    160       err_string = btrace_decode_error (config->format, err_code);
    161 
    162       return recpy_gap_new (err_code, err_string, number);
    163     }
    164 
    165   return recpy_insn_new (tinfo, RECORD_METHOD_BTRACE, number);
    166 }
    167 
    168 /* Create a new gdb.BtraceList object.  */
    169 
    170 static PyObject *
    171 btpy_list_new (thread_info *thread, Py_ssize_t first, Py_ssize_t last,
    172 	       Py_ssize_t step, PyTypeObject *element_type)
    173 {
    174   btpy_list_object * const obj = PyObject_New (btpy_list_object,
    175 					       &btpy_list_type);
    176 
    177   if (obj == NULL)
    178     return NULL;
    179 
    180   obj->thread = thread;
    181   obj->first = first;
    182   obj->last = last;
    183   obj->step = step;
    184   obj->element_type = element_type;
    185 
    186   return (PyObject *) obj;
    187 }
    188 
    189 /* Implementation of RecordInstruction.sal [gdb.Symtab_and_line] for btrace.
    190    Returns the SAL associated with this instruction.  */
    191 
    192 PyObject *
    193 recpy_bt_insn_sal (PyObject *self, void *closure)
    194 {
    195   const btrace_insn * const insn = btrace_insn_from_recpy_insn (self);
    196   PyObject *result = NULL;
    197 
    198   if (insn == NULL)
    199     return NULL;
    200 
    201   try
    202     {
    203       result = symtab_and_line_to_sal_object (find_pc_line (insn->pc, 0));
    204     }
    205   catch (const gdb_exception &except)
    206     {
    207       GDB_PY_HANDLE_EXCEPTION (except);
    208     }
    209 
    210   return result;
    211 }
    212 
    213 /* Implementation of RecordInstruction.pc [int] for btrace.
    214    Returns the instruction address.  */
    215 
    216 PyObject *
    217 recpy_bt_insn_pc (PyObject *self, void *closure)
    218 {
    219   const btrace_insn * const insn = btrace_insn_from_recpy_insn (self);
    220 
    221   if (insn == NULL)
    222     return NULL;
    223 
    224   return gdb_py_object_from_ulongest (insn->pc).release ();
    225 }
    226 
    227 /* Implementation of RecordInstruction.size [int] for btrace.
    228    Returns the instruction size.  */
    229 
    230 PyObject *
    231 recpy_bt_insn_size (PyObject *self, void *closure)
    232 {
    233   const btrace_insn * const insn = btrace_insn_from_recpy_insn (self);
    234 
    235   if (insn == NULL)
    236     return NULL;
    237 
    238   return gdb_py_object_from_longest (insn->size).release ();
    239 }
    240 
    241 /* Implementation of RecordInstruction.is_speculative [bool] for btrace.
    242    Returns if this instruction was executed speculatively.  */
    243 
    244 PyObject *
    245 recpy_bt_insn_is_speculative (PyObject *self, void *closure)
    246 {
    247   const btrace_insn * const insn = btrace_insn_from_recpy_insn (self);
    248 
    249   if (insn == NULL)
    250     return NULL;
    251 
    252   if (insn->flags & BTRACE_INSN_FLAG_SPECULATIVE)
    253     Py_RETURN_TRUE;
    254   else
    255     Py_RETURN_FALSE;
    256 }
    257 
    258 /* Implementation of RecordInstruction.data [buffer] for btrace.
    259    Returns raw instruction data.  */
    260 
    261 PyObject *
    262 recpy_bt_insn_data (PyObject *self, void *closure)
    263 {
    264   const btrace_insn * const insn = btrace_insn_from_recpy_insn (self);
    265   gdb::byte_vector buffer;
    266   PyObject *object;
    267 
    268   if (insn == NULL)
    269     return NULL;
    270 
    271   try
    272     {
    273       buffer.resize (insn->size);
    274       read_memory (insn->pc, buffer.data (), insn->size);
    275     }
    276   catch (const gdb_exception &except)
    277     {
    278       GDB_PY_HANDLE_EXCEPTION (except);
    279     }
    280 
    281   object = PyBytes_FromStringAndSize ((const char *) buffer.data (),
    282 				      insn->size);
    283 
    284   if (object == NULL)
    285     return NULL;
    286 
    287   return PyMemoryView_FromObject (object);
    288 }
    289 
    290 /* Implementation of RecordInstruction.decoded [str] for btrace.
    291    Returns the instruction as human readable string.  */
    292 
    293 PyObject *
    294 recpy_bt_insn_decoded (PyObject *self, void *closure)
    295 {
    296   const btrace_insn * const insn = btrace_insn_from_recpy_insn (self);
    297   string_file strfile;
    298 
    299   if (insn == NULL)
    300     return NULL;
    301 
    302   try
    303     {
    304       gdb_print_insn (current_inferior ()->arch (), insn->pc, &strfile, NULL);
    305     }
    306   catch (const gdb_exception &except)
    307     {
    308       gdbpy_convert_exception (except);
    309       return NULL;
    310     }
    311 
    312   return PyBytes_FromString (strfile.string ().c_str ());
    313 }
    314 
    315 /* Implementation of RecordFunctionSegment.level [int] for btrace.
    316    Returns the call level.  */
    317 
    318 PyObject *
    319 recpy_bt_func_level (PyObject *self, void *closure)
    320 {
    321   const btrace_function * const func = btrace_func_from_recpy_func (self);
    322   thread_info *tinfo;
    323 
    324   if (func == NULL)
    325     return NULL;
    326 
    327   tinfo = ((recpy_element_object *) self)->thread;
    328   return gdb_py_object_from_longest (tinfo->btrace.level
    329 				     + func->level).release ();
    330 }
    331 
    332 /* Implementation of RecordFunctionSegment.symbol [gdb.Symbol] for btrace.
    333    Returns the symbol associated with this function call.  */
    334 
    335 PyObject *
    336 recpy_bt_func_symbol (PyObject *self, void *closure)
    337 {
    338   const btrace_function * const func = btrace_func_from_recpy_func (self);
    339 
    340   if (func == NULL)
    341     return NULL;
    342 
    343   if (func->sym == NULL)
    344     Py_RETURN_NONE;
    345 
    346   return symbol_to_symbol_object (func->sym);
    347 }
    348 
    349 /* Implementation of RecordFunctionSegment.instructions [list] for btrace.
    350    Returns the list of instructions that belong to this function call.  */
    351 
    352 PyObject *
    353 recpy_bt_func_instructions (PyObject *self, void *closure)
    354 {
    355   const btrace_function * const func = btrace_func_from_recpy_func (self);
    356   unsigned int len;
    357 
    358   if (func == NULL)
    359     return NULL;
    360 
    361   len = func->insn.size ();
    362 
    363   /* Gaps count as one instruction.  */
    364   if (len == 0)
    365     len = 1;
    366 
    367   return btpy_list_new (((recpy_element_object *) self)->thread,
    368 			func->insn_offset, func->insn_offset + len, 1,
    369 			&recpy_insn_type);
    370 }
    371 
    372 /* Implementation of RecordFunctionSegment.up [RecordFunctionSegment] for
    373    btrace.  Returns the caller / returnee of this function.  */
    374 
    375 PyObject *
    376 recpy_bt_func_up (PyObject *self, void *closure)
    377 {
    378   const btrace_function * const func = btrace_func_from_recpy_func (self);
    379 
    380   if (func == NULL)
    381     return NULL;
    382 
    383   if (func->up == 0)
    384     Py_RETURN_NONE;
    385 
    386   return recpy_func_new (((recpy_element_object *) self)->thread,
    387 			 RECORD_METHOD_BTRACE, func->up);
    388 }
    389 
    390 /* Implementation of RecordFunctionSegment.prev [RecordFunctionSegment] for
    391    btrace.  Returns a previous segment of this function.  */
    392 
    393 PyObject *
    394 recpy_bt_func_prev (PyObject *self, void *closure)
    395 {
    396   const btrace_function * const func = btrace_func_from_recpy_func (self);
    397 
    398   if (func == NULL)
    399     return NULL;
    400 
    401   if (func->prev == 0)
    402     Py_RETURN_NONE;
    403 
    404   return recpy_func_new (((recpy_element_object *) self)->thread,
    405 			 RECORD_METHOD_BTRACE, func->prev);
    406 }
    407 
    408 /* Implementation of RecordFunctionSegment.next [RecordFunctionSegment] for
    409    btrace.  Returns a following segment of this function.  */
    410 
    411 PyObject *
    412 recpy_bt_func_next (PyObject *self, void *closure)
    413 {
    414   const btrace_function * const func = btrace_func_from_recpy_func (self);
    415 
    416   if (func == NULL)
    417     return NULL;
    418 
    419   if (func->next == 0)
    420     Py_RETURN_NONE;
    421 
    422   return recpy_func_new (((recpy_element_object *) self)->thread,
    423 			 RECORD_METHOD_BTRACE, func->next);
    424 }
    425 
    426 /* Implementation of BtraceList.__len__ (self) -> int.  */
    427 
    428 static Py_ssize_t
    429 btpy_list_length (PyObject *self)
    430 {
    431   const btpy_list_object * const obj = (btpy_list_object *) self;
    432   const Py_ssize_t distance = obj->last - obj->first;
    433   const Py_ssize_t result = distance / obj->step;
    434 
    435   if ((distance % obj->step) == 0)
    436     return result;
    437 
    438   return result + 1;
    439 }
    440 
    441 /* Implementation of
    442    BtraceList.__getitem__ (self, key) -> BtraceInstruction and
    443    BtraceList.__getitem__ (self, key) -> BtraceFunctionCall.  */
    444 
    445 static PyObject *
    446 btpy_list_item (PyObject *self, Py_ssize_t index)
    447 {
    448   const btpy_list_object * const obj = (btpy_list_object *) self;
    449   Py_ssize_t number;
    450 
    451   if (index < 0 || index >= btpy_list_length (self))
    452     return PyErr_Format (PyExc_IndexError, _("Index out of range: %zd."),
    453 			 index);
    454 
    455   number = obj->first + (obj->step * index);
    456 
    457   if (obj->element_type == &recpy_insn_type)
    458     return recpy_insn_new (obj->thread, RECORD_METHOD_BTRACE, number);
    459   else
    460     return recpy_func_new (obj->thread, RECORD_METHOD_BTRACE, number);
    461 }
    462 
    463 /* Implementation of BtraceList.__getitem__ (self, slice) -> BtraceList.  */
    464 
    465 static PyObject *
    466 btpy_list_slice (PyObject *self, PyObject *value)
    467 {
    468   const btpy_list_object * const obj = (btpy_list_object *) self;
    469   const Py_ssize_t length = btpy_list_length (self);
    470   Py_ssize_t start, stop, step, slicelength;
    471 
    472   if (PyLong_Check (value))
    473     {
    474       Py_ssize_t index = PyLong_AsSsize_t (value);
    475 
    476       /* Emulate Python behavior for negative indices.  */
    477       if (index < 0)
    478 	index += length;
    479 
    480       return btpy_list_item (self, index);
    481     }
    482 
    483   if (!PySlice_Check (value))
    484     return PyErr_Format (PyExc_TypeError, _("Index must be int or slice."));
    485 
    486   if (0 != PySlice_GetIndicesEx (value, length, &start, &stop,
    487 				 &step, &slicelength))
    488     return NULL;
    489 
    490   return btpy_list_new (obj->thread, obj->first + obj->step * start,
    491 			obj->first + obj->step * stop, obj->step * step,
    492 			obj->element_type);
    493 }
    494 
    495 /* Helper function that returns the position of an element in a BtraceList
    496    or -1 if the element is not in the list.  */
    497 
    498 static LONGEST
    499 btpy_list_position (PyObject *self, PyObject *value)
    500 {
    501   const btpy_list_object * const list_obj = (btpy_list_object *) self;
    502   const recpy_element_object * const obj = (const recpy_element_object *) value;
    503   Py_ssize_t index = obj->number;
    504 
    505   if (list_obj->element_type != Py_TYPE (value))
    506     return -1;
    507 
    508   if (list_obj->thread != obj->thread)
    509     return -1;
    510 
    511   if (index < list_obj->first || index > list_obj->last)
    512     return -1;
    513 
    514   index -= list_obj->first;
    515 
    516   if (index % list_obj->step != 0)
    517     return -1;
    518 
    519   return index / list_obj->step;
    520 }
    521 
    522 /* Implementation of "in" operator for BtraceLists.  */
    523 
    524 static int
    525 btpy_list_contains (PyObject *self, PyObject *value)
    526 {
    527   if (btpy_list_position (self, value) < 0)
    528     return 0;
    529 
    530   return 1;
    531 }
    532 
    533 /* Implementation of BtraceLists.index (self, value) -> int.  */
    534 
    535 static PyObject *
    536 btpy_list_index (PyObject *self, PyObject *value)
    537 {
    538   const LONGEST index = btpy_list_position (self, value);
    539 
    540   if (index < 0)
    541     return PyErr_Format (PyExc_ValueError, _("Not in list."));
    542 
    543   return gdb_py_object_from_longest (index).release ();
    544 }
    545 
    546 /* Implementation of BtraceList.count (self, value) -> int.  */
    547 
    548 static PyObject *
    549 btpy_list_count (PyObject *self, PyObject *value)
    550 {
    551   /* We know that if an element is in the list, it is so exactly one time,
    552      enabling us to reuse the "is element of" check.  */
    553   return gdb_py_object_from_longest (btpy_list_contains (self,
    554 							 value)).release ();
    555 }
    556 
    557 /* Python rich compare function to allow for equality and inequality checks
    558    in Python.  */
    559 
    560 static PyObject *
    561 btpy_list_richcompare (PyObject *self, PyObject *other, int op)
    562 {
    563   const btpy_list_object * const obj1 = (btpy_list_object *) self;
    564   const btpy_list_object * const obj2 = (btpy_list_object *) other;
    565 
    566   if (Py_TYPE (self) != Py_TYPE (other))
    567     {
    568       Py_INCREF (Py_NotImplemented);
    569       return Py_NotImplemented;
    570     }
    571 
    572   switch (op)
    573   {
    574     case Py_EQ:
    575       if (obj1->thread == obj2->thread
    576 	  && obj1->element_type == obj2->element_type
    577 	  && obj1->first == obj2->first
    578 	  && obj1->last == obj2->last
    579 	  && obj1->step == obj2->step)
    580 	Py_RETURN_TRUE;
    581       else
    582 	Py_RETURN_FALSE;
    583 
    584     case Py_NE:
    585       if (obj1->thread != obj2->thread
    586 	  || obj1->element_type != obj2->element_type
    587 	  || obj1->first != obj2->first
    588 	  || obj1->last != obj2->last
    589 	  || obj1->step != obj2->step)
    590 	Py_RETURN_TRUE;
    591       else
    592 	Py_RETURN_FALSE;
    593 
    594     default:
    595       break;
    596   }
    597 
    598   Py_INCREF (Py_NotImplemented);
    599   return Py_NotImplemented;
    600 }
    601 
    602 /* Implementation of
    603    BtraceRecord.method [str].  */
    604 
    605 PyObject *
    606 recpy_bt_method (PyObject *self, void *closure)
    607 {
    608   return PyUnicode_FromString ("btrace");
    609 }
    610 
    611 /* Implementation of
    612    BtraceRecord.format [str].  */
    613 
    614 PyObject *
    615 recpy_bt_format (PyObject *self, void *closure)
    616 {
    617   const recpy_record_object * const record = (recpy_record_object *) self;
    618   const struct thread_info * const tinfo = record->thread;
    619   const struct btrace_config * config;
    620 
    621   if (tinfo == NULL)
    622     Py_RETURN_NONE;
    623 
    624   config = btrace_conf (&tinfo->btrace);
    625 
    626   if (config == NULL)
    627     Py_RETURN_NONE;
    628 
    629   return PyUnicode_FromString (btrace_format_short_string (config->format));
    630 }
    631 
    632 /* Implementation of
    633    BtraceRecord.replay_position [BtraceInstruction].  */
    634 
    635 PyObject *
    636 recpy_bt_replay_position (PyObject *self, void *closure)
    637 {
    638   const recpy_record_object * const record = (recpy_record_object *) self;
    639   thread_info * tinfo = record->thread;
    640 
    641   if (tinfo == NULL)
    642     Py_RETURN_NONE;
    643 
    644   if (tinfo->btrace.replay == NULL)
    645     Py_RETURN_NONE;
    646 
    647   return btpy_insn_or_gap_new (tinfo,
    648 			       btrace_insn_number (tinfo->btrace.replay));
    649 }
    650 
    651 /* Implementation of
    652    BtraceRecord.begin [BtraceInstruction].  */
    653 
    654 PyObject *
    655 recpy_bt_begin (PyObject *self, void *closure)
    656 {
    657   const recpy_record_object * const record = (recpy_record_object *) self;
    658   thread_info *const tinfo = record->thread;
    659   struct btrace_insn_iterator iterator;
    660 
    661   if (tinfo == NULL)
    662     Py_RETURN_NONE;
    663 
    664   btrace_fetch (tinfo, record_btrace_get_cpu ());
    665 
    666   if (btrace_is_empty (tinfo))
    667     Py_RETURN_NONE;
    668 
    669   btrace_insn_begin (&iterator, &tinfo->btrace);
    670   return btpy_insn_or_gap_new (tinfo, btrace_insn_number (&iterator));
    671 }
    672 
    673 /* Implementation of
    674    BtraceRecord.end [BtraceInstruction].  */
    675 
    676 PyObject *
    677 recpy_bt_end (PyObject *self, void *closure)
    678 {
    679   const recpy_record_object * const record = (recpy_record_object *) self;
    680   thread_info *const tinfo = record->thread;
    681   struct btrace_insn_iterator iterator;
    682 
    683   if (tinfo == NULL)
    684     Py_RETURN_NONE;
    685 
    686   btrace_fetch (tinfo, record_btrace_get_cpu ());
    687 
    688   if (btrace_is_empty (tinfo))
    689     Py_RETURN_NONE;
    690 
    691   btrace_insn_end (&iterator, &tinfo->btrace);
    692   return btpy_insn_or_gap_new (tinfo, btrace_insn_number (&iterator));
    693 }
    694 
    695 /* Implementation of
    696    BtraceRecord.instruction_history [list].  */
    697 
    698 PyObject *
    699 recpy_bt_instruction_history (PyObject *self, void *closure)
    700 {
    701   const recpy_record_object * const record = (recpy_record_object *) self;
    702   thread_info *const tinfo = record->thread;
    703   struct btrace_insn_iterator iterator;
    704   unsigned long first = 0;
    705   unsigned long last = 0;
    706 
    707    if (tinfo == NULL)
    708      Py_RETURN_NONE;
    709 
    710    btrace_fetch (tinfo, record_btrace_get_cpu ());
    711 
    712    if (btrace_is_empty (tinfo))
    713      Py_RETURN_NONE;
    714 
    715    btrace_insn_begin (&iterator, &tinfo->btrace);
    716    first = btrace_insn_number (&iterator);
    717 
    718    btrace_insn_end (&iterator, &tinfo->btrace);
    719    last = btrace_insn_number (&iterator);
    720 
    721    return btpy_list_new (tinfo, first, last, 1, &recpy_insn_type);
    722 }
    723 
    724 /* Implementation of
    725    BtraceRecord.function_call_history [list].  */
    726 
    727 PyObject *
    728 recpy_bt_function_call_history (PyObject *self, void *closure)
    729 {
    730   const recpy_record_object * const record = (recpy_record_object *) self;
    731   thread_info *const tinfo = record->thread;
    732   struct btrace_call_iterator iterator;
    733   unsigned long first = 0;
    734   unsigned long last = 0;
    735 
    736   if (tinfo == NULL)
    737     Py_RETURN_NONE;
    738 
    739   btrace_fetch (tinfo, record_btrace_get_cpu ());
    740 
    741   if (btrace_is_empty (tinfo))
    742     Py_RETURN_NONE;
    743 
    744   btrace_call_begin (&iterator, &tinfo->btrace);
    745   first = btrace_call_number (&iterator);
    746 
    747   btrace_call_end (&iterator, &tinfo->btrace);
    748   last = btrace_call_number (&iterator);
    749 
    750   return btpy_list_new (tinfo, first, last, 1, &recpy_func_type);
    751 }
    752 
    753 /* Implementation of BtraceRecord.goto (self, BtraceInstruction) -> None.  */
    754 
    755 PyObject *
    756 recpy_bt_goto (PyObject *self, PyObject *args)
    757 {
    758   const recpy_record_object * const record = (recpy_record_object *) self;
    759   thread_info *const tinfo = record->thread;
    760   const recpy_element_object *obj;
    761   PyObject *parse_obj;
    762 
    763   if (tinfo == NULL || btrace_is_empty (tinfo))
    764 	return PyErr_Format (gdbpy_gdb_error, _("Empty branch trace."));
    765 
    766   if (!PyArg_ParseTuple (args, "O", &parse_obj))
    767     return NULL;
    768 
    769   if (Py_TYPE (parse_obj) != &recpy_insn_type)
    770     return PyErr_Format (PyExc_TypeError, _("Argument must be instruction."));
    771   obj = (const recpy_element_object *) parse_obj;
    772 
    773   try
    774     {
    775       struct btrace_insn_iterator iter;
    776 
    777       btrace_insn_end (&iter, &tinfo->btrace);
    778 
    779       if (btrace_insn_number (&iter) == obj->number)
    780 	target_goto_record_end ();
    781       else
    782 	target_goto_record (obj->number);
    783     }
    784   catch (const gdb_exception &except)
    785     {
    786       GDB_PY_HANDLE_EXCEPTION (except);
    787     }
    788 
    789   Py_RETURN_NONE;
    790 }
    791 
    792 /* BtraceList methods.  */
    793 
    794 static PyMethodDef btpy_list_methods[] =
    795 {
    796   { "count", btpy_list_count, METH_O, "count number of occurrences"},
    797   { "index", btpy_list_index, METH_O, "index of entry"},
    798   {NULL}
    799 };
    800 
    801 /* BtraceList sequence methods.  */
    802 
    803 static PySequenceMethods btpy_list_sequence_methods =
    804 {
    805   NULL
    806 };
    807 
    808 /* BtraceList mapping methods.  Necessary for slicing.  */
    809 
    810 static PyMappingMethods btpy_list_mapping_methods =
    811 {
    812   NULL
    813 };
    814 
    815 /* Sets up the btrace record API.  */
    816 
    817 static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
    818 gdbpy_initialize_btrace (void)
    819 {
    820   btpy_list_type.tp_new = PyType_GenericNew;
    821   btpy_list_type.tp_flags = Py_TPFLAGS_DEFAULT;
    822   btpy_list_type.tp_basicsize = sizeof (btpy_list_object);
    823   btpy_list_type.tp_name = "gdb.BtraceObjectList";
    824   btpy_list_type.tp_doc = "GDB btrace list object";
    825   btpy_list_type.tp_methods = btpy_list_methods;
    826   btpy_list_type.tp_as_sequence = &btpy_list_sequence_methods;
    827   btpy_list_type.tp_as_mapping = &btpy_list_mapping_methods;
    828   btpy_list_type.tp_richcompare = btpy_list_richcompare;
    829 
    830   btpy_list_sequence_methods.sq_item = btpy_list_item;
    831   btpy_list_sequence_methods.sq_length = btpy_list_length;
    832   btpy_list_sequence_methods.sq_contains = btpy_list_contains;
    833 
    834   btpy_list_mapping_methods.mp_subscript = btpy_list_slice;
    835 
    836   return PyType_Ready (&btpy_list_type);
    837 }
    838 
    839 GDBPY_INITIALIZE_FILE (gdbpy_initialize_btrace);
    840