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 *
btrace_insn_from_recpy_insn(const PyObject * const pyobject)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 *
btrace_func_from_recpy_func(const PyObject * const pyobject)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 *
btpy_insn_or_gap_new(thread_info * tinfo,Py_ssize_t number)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 *
btpy_list_new(thread_info * thread,Py_ssize_t first,Py_ssize_t last,Py_ssize_t step,PyTypeObject * element_type)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 *
recpy_bt_insn_sal(PyObject * self,void * closure)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 *
recpy_bt_insn_pc(PyObject * self,void * closure)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 *
recpy_bt_insn_size(PyObject * self,void * closure)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 *
recpy_bt_insn_is_speculative(PyObject * self,void * closure)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 *
recpy_bt_insn_data(PyObject * self,void * closure)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 *
recpy_bt_insn_decoded(PyObject * self,void * closure)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 *
recpy_bt_func_level(PyObject * self,void * closure)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 *
recpy_bt_func_symbol(PyObject * self,void * closure)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 *
recpy_bt_func_instructions(PyObject * self,void * closure)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 *
recpy_bt_func_up(PyObject * self,void * closure)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 *
recpy_bt_func_prev(PyObject * self,void * closure)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 *
recpy_bt_func_next(PyObject * self,void * closure)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
btpy_list_length(PyObject * self)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 *
btpy_list_item(PyObject * self,Py_ssize_t index)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 *
btpy_list_slice(PyObject * self,PyObject * value)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
btpy_list_position(PyObject * self,PyObject * value)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
btpy_list_contains(PyObject * self,PyObject * value)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 *
btpy_list_index(PyObject * self,PyObject * value)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 *
btpy_list_count(PyObject * self,PyObject * value)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 *
btpy_list_richcompare(PyObject * self,PyObject * other,int op)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 *
recpy_bt_method(PyObject * self,void * closure)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 *
recpy_bt_format(PyObject * self,void * closure)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 *
recpy_bt_replay_position(PyObject * self,void * closure)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 *
recpy_bt_begin(PyObject * self,void * closure)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 *
recpy_bt_end(PyObject * self,void * closure)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 *
recpy_bt_instruction_history(PyObject * self,void * closure)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 *
recpy_bt_function_call_history(PyObject * self,void * closure)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 *
recpy_bt_goto(PyObject * self,PyObject * args)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
gdbpy_initialize_btrace(void)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