Commit | Line | Data |
---|---|---|
75c0bdf4 TW |
1 | /* Python interface to btrace instruction history. |
2 | ||
3 | Copyright 2016-2017 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 "defs.h" | |
21 | #include "gdbcore.h" | |
22 | #include "gdbcmd.h" | |
23 | #include "gdbthread.h" | |
24 | #include "btrace.h" | |
ae20e79a | 25 | #include "py-record.h" |
75c0bdf4 TW |
26 | #include "py-record-btrace.h" |
27 | #include "disasm.h" | |
28 | ||
29 | #if defined (IS_PY3K) | |
30 | ||
31 | #define BTPY_PYSLICE(x) (x) | |
32 | ||
33 | #else | |
34 | ||
35 | #define BTPY_PYSLICE(x) ((PySliceObject *) x) | |
36 | ||
37 | #endif | |
38 | ||
75c0bdf4 | 39 | #define BTPY_REQUIRE_VALID_CALL(obj, iter) \ |
8d0050ea TW |
40 | do { \ |
41 | struct thread_info *tinfo = find_thread_ptid (obj->ptid); \ | |
42 | if (tinfo == NULL || btrace_is_empty (tinfo)) \ | |
43 | return PyErr_Format (gdbpy_gdb_error, _("Empty branch trace.")); \ | |
44 | if (0 == btrace_find_call_by_number (&iter, &tinfo->btrace, \ | |
45 | obj->number)) \ | |
46 | return PyErr_Format (gdbpy_gdb_error, _("No such call segment."));\ | |
47 | } while (0) | |
75c0bdf4 | 48 | |
75c0bdf4 TW |
49 | /* Python object for btrace record lists. */ |
50 | ||
51 | typedef struct { | |
52 | PyObject_HEAD | |
53 | ||
54 | /* The thread this list belongs to. */ | |
55 | ptid_t ptid; | |
56 | ||
57 | /* The first index being part of this list. */ | |
58 | Py_ssize_t first; | |
59 | ||
60 | /* The last index begin part of this list. */ | |
61 | Py_ssize_t last; | |
62 | ||
63 | /* Stride size. */ | |
64 | Py_ssize_t step; | |
65 | ||
0ed5da75 | 66 | /* Either &BTPY_CALL_TYPE or &RECPY_INSN_TYPE. */ |
75c0bdf4 TW |
67 | PyTypeObject* element_type; |
68 | } btpy_list_object; | |
69 | ||
75c0bdf4 TW |
70 | /* Python type for btrace function-calls. */ |
71 | ||
72 | static PyTypeObject btpy_call_type = { | |
73 | PyVarObject_HEAD_INIT (NULL, 0) | |
74 | }; | |
75 | ||
76 | /* Python type for btrace lists. */ | |
77 | ||
78 | static PyTypeObject btpy_list_type = { | |
79 | PyVarObject_HEAD_INIT (NULL, 0) | |
80 | }; | |
81 | ||
0ed5da75 TW |
82 | /* Returns either a btrace_insn for the given Python gdb.RecordInstruction |
83 | object or sets an appropriate Python exception and returns NULL. */ | |
75c0bdf4 | 84 | |
0ed5da75 TW |
85 | static const btrace_insn * |
86 | btrace_insn_from_recpy_insn (const PyObject * const pyobject) | |
75c0bdf4 | 87 | { |
0ed5da75 TW |
88 | const btrace_insn *insn; |
89 | const recpy_element_object *obj; | |
90 | thread_info *tinfo; | |
91 | btrace_insn_iterator iter; | |
75c0bdf4 | 92 | |
0ed5da75 TW |
93 | if (Py_TYPE (pyobject) != &recpy_insn_type) |
94 | { | |
95 | PyErr_Format (gdbpy_gdb_error, _("Must be gdb.RecordInstruction")); | |
96 | return NULL; | |
97 | } | |
75c0bdf4 | 98 | |
0ed5da75 TW |
99 | obj = (const recpy_element_object *) pyobject; |
100 | tinfo = find_thread_ptid (obj->ptid); | |
75c0bdf4 | 101 | |
0ed5da75 TW |
102 | if (tinfo == NULL || btrace_is_empty (tinfo)) |
103 | { | |
104 | PyErr_Format (gdbpy_gdb_error, _("No such instruction.")); | |
105 | return NULL; | |
106 | } | |
107 | ||
108 | if (btrace_find_insn_by_number (&iter, &tinfo->btrace, obj->number) == 0) | |
109 | { | |
110 | PyErr_Format (gdbpy_gdb_error, _("No such instruction.")); | |
111 | return NULL; | |
112 | } | |
113 | ||
114 | insn = btrace_insn_get (&iter); | |
115 | if (insn == NULL) | |
116 | { | |
117 | PyErr_Format (gdbpy_gdb_error, _("Not a valid instruction.")); | |
118 | return NULL; | |
119 | } | |
120 | ||
121 | return insn; | |
75c0bdf4 TW |
122 | } |
123 | ||
913aeadd | 124 | /* Looks at the recorded item with the number NUMBER and create a |
0ed5da75 | 125 | gdb.RecordInstruction or gdb.RecordGap object for it accordingly. */ |
75c0bdf4 TW |
126 | |
127 | static PyObject * | |
913aeadd | 128 | btpy_insn_or_gap_new (const thread_info *tinfo, Py_ssize_t number) |
75c0bdf4 | 129 | { |
913aeadd TW |
130 | btrace_insn_iterator iter; |
131 | int err_code; | |
132 | ||
133 | btrace_find_insn_by_number (&iter, &tinfo->btrace, number); | |
134 | err_code = btrace_insn_get_error (&iter); | |
135 | ||
136 | if (err_code != 0) | |
137 | { | |
138 | const btrace_config *config; | |
139 | const char *err_string; | |
140 | ||
141 | config = btrace_conf (&tinfo->btrace); | |
142 | err_string = btrace_decode_error (config->format, err_code); | |
143 | ||
144 | return recpy_gap_new (err_code, err_string, number); | |
145 | } | |
146 | ||
0ed5da75 | 147 | return recpy_insn_new (tinfo->ptid, RECORD_METHOD_BTRACE, number); |
75c0bdf4 TW |
148 | } |
149 | ||
150 | /* Create a new gdb.BtraceFunctionCall object. */ | |
151 | ||
152 | static PyObject * | |
153 | btpy_call_new (ptid_t ptid, Py_ssize_t number) | |
154 | { | |
0ed5da75 TW |
155 | recpy_element_object * const obj = PyObject_New (recpy_element_object, |
156 | &btpy_call_type); | |
157 | ||
158 | if (obj == NULL) | |
159 | return NULL; | |
160 | ||
161 | obj->ptid = ptid; | |
162 | obj->method = RECORD_METHOD_BTRACE; | |
163 | obj->number = number; | |
164 | ||
165 | return (PyObject *) obj; | |
75c0bdf4 TW |
166 | } |
167 | ||
168 | /* Create a new gdb.BtraceList object. */ | |
169 | ||
170 | static PyObject * | |
171 | btpy_list_new (ptid_t ptid, Py_ssize_t first, Py_ssize_t last, Py_ssize_t step, | |
172 | 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->ptid = ptid; | |
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 BtraceInstruction.number [int] and | |
190 | BtraceFunctionCall.number [int]. */ | |
191 | ||
192 | static PyObject * | |
193 | btpy_number (PyObject *self, void *closure) | |
194 | { | |
0ed5da75 | 195 | const recpy_element_object * const obj = (const recpy_element_object *) self; |
75c0bdf4 TW |
196 | |
197 | return PyInt_FromSsize_t (obj->number); | |
198 | } | |
199 | ||
200 | /* Implementation of BtraceInstruction.__hash__ () -> int and | |
201 | BtraceFunctionCall.__hash__ () -> int. */ | |
202 | ||
203 | static Py_hash_t | |
204 | btpy_hash (PyObject *self) | |
205 | { | |
0ed5da75 | 206 | const recpy_element_object * const obj = (const recpy_element_object *) self; |
75c0bdf4 TW |
207 | |
208 | return obj->number; | |
209 | } | |
210 | ||
0ed5da75 TW |
211 | /* Implementation of RecordInstruction.sal [gdb.Symtab_and_line] for btrace. |
212 | Returns the SAL associated with this instruction. */ | |
75c0bdf4 | 213 | |
0ed5da75 TW |
214 | PyObject * |
215 | recpy_bt_insn_sal (PyObject *self, void *closure) | |
75c0bdf4 | 216 | { |
0ed5da75 | 217 | const btrace_insn * const insn = btrace_insn_from_recpy_insn (self); |
75c0bdf4 TW |
218 | PyObject *result = NULL; |
219 | ||
75c0bdf4 | 220 | if (insn == NULL) |
0ed5da75 | 221 | return NULL; |
75c0bdf4 TW |
222 | |
223 | TRY | |
224 | { | |
225 | result = symtab_and_line_to_sal_object (find_pc_line (insn->pc, 0)); | |
226 | } | |
227 | CATCH (except, RETURN_MASK_ALL) | |
228 | { | |
229 | GDB_PY_HANDLE_EXCEPTION (except); | |
230 | } | |
231 | END_CATCH | |
232 | ||
233 | return result; | |
234 | } | |
235 | ||
0ed5da75 TW |
236 | /* Implementation of RecordInstruction.pc [int] for btrace. |
237 | Returns the instruction address. */ | |
75c0bdf4 | 238 | |
0ed5da75 TW |
239 | PyObject * |
240 | recpy_bt_insn_pc (PyObject *self, void *closure) | |
75c0bdf4 | 241 | { |
0ed5da75 | 242 | const btrace_insn * const insn = btrace_insn_from_recpy_insn (self); |
75c0bdf4 | 243 | |
75c0bdf4 | 244 | if (insn == NULL) |
0ed5da75 | 245 | return NULL; |
75c0bdf4 TW |
246 | |
247 | return gdb_py_long_from_ulongest (insn->pc); | |
248 | } | |
249 | ||
0ed5da75 TW |
250 | /* Implementation of RecordInstruction.size [int] for btrace. |
251 | Returns the instruction size. */ | |
75c0bdf4 | 252 | |
0ed5da75 TW |
253 | PyObject * |
254 | recpy_bt_insn_size (PyObject *self, void *closure) | |
75c0bdf4 | 255 | { |
0ed5da75 | 256 | const btrace_insn * const insn = btrace_insn_from_recpy_insn (self); |
75c0bdf4 | 257 | |
75c0bdf4 | 258 | if (insn == NULL) |
0ed5da75 | 259 | return NULL; |
75c0bdf4 TW |
260 | |
261 | return PyInt_FromLong (insn->size); | |
262 | } | |
263 | ||
0ed5da75 | 264 | /* Implementation of RecordInstruction.is_speculative [bool] for btrace. |
75c0bdf4 TW |
265 | Returns if this instruction was executed speculatively. */ |
266 | ||
0ed5da75 TW |
267 | PyObject * |
268 | recpy_bt_insn_is_speculative (PyObject *self, void *closure) | |
75c0bdf4 | 269 | { |
0ed5da75 | 270 | const btrace_insn * const insn = btrace_insn_from_recpy_insn (self); |
75c0bdf4 | 271 | |
75c0bdf4 | 272 | if (insn == NULL) |
0ed5da75 | 273 | return NULL; |
75c0bdf4 TW |
274 | |
275 | if (insn->flags & BTRACE_INSN_FLAG_SPECULATIVE) | |
276 | Py_RETURN_TRUE; | |
277 | else | |
278 | Py_RETURN_FALSE; | |
279 | } | |
280 | ||
0ed5da75 | 281 | /* Implementation of RecordInstruction.data [buffer] for btrace. |
75c0bdf4 TW |
282 | Returns raw instruction data. */ |
283 | ||
0ed5da75 TW |
284 | PyObject * |
285 | recpy_bt_insn_data (PyObject *self, void *closure) | |
75c0bdf4 | 286 | { |
0ed5da75 | 287 | const btrace_insn * const insn = btrace_insn_from_recpy_insn (self); |
75c0bdf4 TW |
288 | gdb_byte *buffer = NULL; |
289 | PyObject *object; | |
290 | ||
75c0bdf4 | 291 | if (insn == NULL) |
0ed5da75 | 292 | return NULL; |
75c0bdf4 TW |
293 | |
294 | TRY | |
295 | { | |
296 | buffer = (gdb_byte *) xmalloc (insn->size); | |
297 | read_memory (insn->pc, buffer, insn->size); | |
298 | } | |
299 | CATCH (except, RETURN_MASK_ALL) | |
300 | { | |
301 | xfree (buffer); | |
302 | GDB_PY_HANDLE_EXCEPTION (except); | |
303 | } | |
304 | END_CATCH | |
305 | ||
306 | object = PyBytes_FromStringAndSize ((const char*) buffer, insn->size); | |
307 | xfree (buffer); | |
308 | ||
309 | if (object == NULL) | |
310 | return NULL; | |
311 | ||
cee59b3f | 312 | #ifdef IS_PY3K |
75c0bdf4 | 313 | return PyMemoryView_FromObject (object); |
cee59b3f TW |
314 | #else |
315 | return PyBuffer_FromObject (object, 0, Py_END_OF_BUFFER); | |
316 | #endif | |
317 | ||
75c0bdf4 TW |
318 | } |
319 | ||
0ed5da75 TW |
320 | /* Implementation of RecordInstruction.decoded [str] for btrace. |
321 | Returns the instruction as human readable string. */ | |
75c0bdf4 | 322 | |
0ed5da75 TW |
323 | PyObject * |
324 | recpy_bt_insn_decoded (PyObject *self, void *closure) | |
75c0bdf4 | 325 | { |
0ed5da75 | 326 | const btrace_insn * const insn = btrace_insn_from_recpy_insn (self); |
75c0bdf4 TW |
327 | string_file strfile; |
328 | ||
75c0bdf4 | 329 | if (insn == NULL) |
0ed5da75 | 330 | return NULL; |
75c0bdf4 TW |
331 | |
332 | TRY | |
333 | { | |
334 | gdb_print_insn (target_gdbarch (), insn->pc, &strfile, NULL); | |
335 | } | |
336 | CATCH (except, RETURN_MASK_ALL) | |
337 | { | |
338 | gdbpy_convert_exception (except); | |
339 | return NULL; | |
340 | } | |
341 | END_CATCH | |
342 | ||
343 | ||
344 | return PyBytes_FromString (strfile.string ().c_str ()); | |
345 | } | |
346 | ||
347 | /* Implementation of BtraceFunctionCall.level [int]. Returns the | |
348 | call level. */ | |
349 | ||
350 | static PyObject * | |
351 | btpy_call_level (PyObject *self, void *closure) | |
352 | { | |
0ed5da75 | 353 | const recpy_element_object * const obj = (const recpy_element_object *) self; |
75c0bdf4 TW |
354 | const struct btrace_function *func; |
355 | struct btrace_call_iterator iter; | |
356 | ||
357 | BTPY_REQUIRE_VALID_CALL (obj, iter); | |
358 | ||
359 | func = btrace_call_get (&iter); | |
360 | if (func == NULL) | |
361 | Py_RETURN_NONE; | |
362 | ||
363 | return PyInt_FromLong (iter.btinfo->level + func->level); | |
364 | } | |
365 | ||
366 | /* Implementation of BtraceFunctionCall.symbol [gdb.Symbol]. Returns | |
367 | the symbol associated with this function call. */ | |
368 | ||
369 | static PyObject * | |
370 | btpy_call_symbol (PyObject *self, void *closure) | |
371 | { | |
0ed5da75 | 372 | const recpy_element_object * const obj = (const recpy_element_object *) self; |
75c0bdf4 TW |
373 | const struct btrace_function *func; |
374 | struct btrace_call_iterator iter; | |
375 | ||
376 | BTPY_REQUIRE_VALID_CALL (obj, iter); | |
377 | ||
378 | func = btrace_call_get (&iter); | |
379 | if (func == NULL) | |
380 | Py_RETURN_NONE; | |
381 | ||
382 | if (func->sym == NULL) | |
383 | Py_RETURN_NONE; | |
384 | ||
385 | return symbol_to_symbol_object (func->sym); | |
386 | } | |
387 | ||
388 | /* Implementation of BtraceFunctionCall.instructions [list]. | |
389 | Return the list of instructions that belong to this function call. */ | |
390 | ||
391 | static PyObject * | |
392 | btpy_call_instructions (PyObject *self, void *closure) | |
393 | { | |
0ed5da75 | 394 | const recpy_element_object * const obj = (const recpy_element_object *) self; |
75c0bdf4 TW |
395 | const struct btrace_function *func; |
396 | struct btrace_call_iterator iter; | |
397 | unsigned int len; | |
398 | ||
399 | BTPY_REQUIRE_VALID_CALL (obj, iter); | |
400 | ||
401 | func = btrace_call_get (&iter); | |
402 | if (func == NULL) | |
403 | Py_RETURN_NONE; | |
404 | ||
405 | len = VEC_length (btrace_insn_s, func->insn); | |
406 | ||
407 | /* Gaps count as one instruction. */ | |
408 | if (len == 0) | |
409 | len = 1; | |
410 | ||
411 | return btpy_list_new (obj->ptid, func->insn_offset, func->insn_offset + len, | |
0ed5da75 | 412 | 1, &recpy_insn_type); |
75c0bdf4 TW |
413 | } |
414 | ||
415 | /* Implementation of BtraceFunctionCall.up [gdb.BtraceRecordCall]. | |
416 | Return the caller / returnee of this function. */ | |
417 | ||
418 | static PyObject * | |
419 | btpy_call_up (PyObject *self, void *closure) | |
420 | { | |
0ed5da75 | 421 | const recpy_element_object * const obj = (const recpy_element_object *) self; |
75c0bdf4 TW |
422 | const struct btrace_function *func; |
423 | struct btrace_call_iterator iter; | |
424 | ||
425 | BTPY_REQUIRE_VALID_CALL (obj, iter); | |
426 | ||
427 | func = btrace_call_get (&iter); | |
428 | if (func == NULL) | |
429 | Py_RETURN_NONE; | |
430 | ||
431 | if (func->up == NULL) | |
432 | Py_RETURN_NONE; | |
433 | ||
434 | return btpy_call_new (obj->ptid, func->up->number); | |
435 | } | |
436 | ||
437 | /* Implementation of BtraceFunctionCall.prev_sibling [BtraceFunctionCall]. | |
438 | Return a previous segment of this function. */ | |
439 | ||
440 | static PyObject * | |
441 | btpy_call_prev_sibling (PyObject *self, void *closure) | |
442 | { | |
0ed5da75 | 443 | const recpy_element_object * const obj = (const recpy_element_object *) self; |
75c0bdf4 TW |
444 | const struct btrace_function *func; |
445 | struct btrace_call_iterator iter; | |
446 | ||
447 | BTPY_REQUIRE_VALID_CALL (obj, iter); | |
448 | ||
449 | func = btrace_call_get (&iter); | |
450 | if (func == NULL) | |
451 | Py_RETURN_NONE; | |
452 | ||
453 | if (func->segment.prev == NULL) | |
454 | Py_RETURN_NONE; | |
455 | ||
456 | return btpy_call_new (obj->ptid, func->segment.prev->number); | |
457 | } | |
458 | ||
459 | /* Implementation of BtraceFunctionCall.next_sibling [BtraceFunctionCall]. | |
460 | Return a following segment of this function. */ | |
461 | ||
462 | static PyObject * | |
463 | btpy_call_next_sibling (PyObject *self, void *closure) | |
464 | { | |
0ed5da75 | 465 | const recpy_element_object * const obj = (const recpy_element_object *) self; |
75c0bdf4 TW |
466 | const struct btrace_function *func; |
467 | struct btrace_call_iterator iter; | |
468 | ||
469 | BTPY_REQUIRE_VALID_CALL (obj, iter); | |
470 | ||
471 | func = btrace_call_get (&iter); | |
472 | if (func == NULL) | |
473 | Py_RETURN_NONE; | |
474 | ||
475 | if (func->segment.next == NULL) | |
476 | Py_RETURN_NONE; | |
477 | ||
478 | return btpy_call_new (obj->ptid, func->segment.next->number); | |
479 | } | |
480 | ||
481 | /* Python rich compare function to allow for equality and inequality checks | |
482 | in Python. */ | |
483 | ||
484 | static PyObject * | |
485 | btpy_richcompare (PyObject *self, PyObject *other, int op) | |
486 | { | |
0ed5da75 TW |
487 | const recpy_element_object * const obj1 = (recpy_element_object *) self; |
488 | const recpy_element_object * const obj2 = (recpy_element_object *) other; | |
75c0bdf4 TW |
489 | |
490 | if (Py_TYPE (self) != Py_TYPE (other)) | |
491 | { | |
492 | Py_INCREF (Py_NotImplemented); | |
493 | return Py_NotImplemented; | |
494 | } | |
495 | ||
496 | switch (op) | |
497 | { | |
498 | case Py_EQ: | |
499 | if (ptid_equal (obj1->ptid, obj2->ptid) && obj1->number == obj2->number) | |
500 | Py_RETURN_TRUE; | |
501 | else | |
502 | Py_RETURN_FALSE; | |
503 | ||
504 | case Py_NE: | |
505 | if (!ptid_equal (obj1->ptid, obj2->ptid) || obj1->number != obj2->number) | |
506 | Py_RETURN_TRUE; | |
507 | else | |
508 | Py_RETURN_FALSE; | |
509 | ||
510 | default: | |
511 | break; | |
512 | } | |
513 | ||
514 | Py_INCREF (Py_NotImplemented); | |
515 | return Py_NotImplemented; | |
516 | } | |
517 | ||
518 | /* Implementation of BtraceList.__len__ (self) -> int. */ | |
519 | ||
520 | static Py_ssize_t | |
521 | btpy_list_length (PyObject *self) | |
522 | { | |
523 | const btpy_list_object * const obj = (btpy_list_object *) self; | |
524 | const Py_ssize_t distance = obj->last - obj->first; | |
525 | const Py_ssize_t result = distance / obj->step; | |
526 | ||
527 | if ((distance % obj->step) == 0) | |
528 | return result; | |
529 | ||
530 | return result + 1; | |
531 | } | |
532 | ||
533 | /* Implementation of | |
534 | BtraceList.__getitem__ (self, key) -> BtraceInstruction and | |
535 | BtraceList.__getitem__ (self, key) -> BtraceFunctionCall. */ | |
536 | ||
537 | static PyObject * | |
538 | btpy_list_item (PyObject *self, Py_ssize_t index) | |
539 | { | |
540 | const btpy_list_object * const obj = (btpy_list_object *) self; | |
541 | struct thread_info * const tinfo = find_thread_ptid (obj->ptid); | |
0ed5da75 | 542 | Py_ssize_t number; |
75c0bdf4 TW |
543 | |
544 | if (index < 0 || index >= btpy_list_length (self)) | |
545 | return PyErr_Format (PyExc_IndexError, _("Index out of range: %zd."), | |
546 | index); | |
547 | ||
0ed5da75 TW |
548 | number = obj->first + (obj->step * index); |
549 | ||
550 | if (obj->element_type == &recpy_insn_type) | |
551 | return recpy_insn_new (obj->ptid, RECORD_METHOD_BTRACE, number); | |
552 | else | |
553 | return btpy_call_new (obj->ptid, number); | |
75c0bdf4 TW |
554 | } |
555 | ||
556 | /* Implementation of BtraceList.__getitem__ (self, slice) -> BtraceList. */ | |
557 | ||
558 | static PyObject * | |
559 | btpy_list_slice (PyObject *self, PyObject *value) | |
560 | { | |
561 | const btpy_list_object * const obj = (btpy_list_object *) self; | |
562 | const Py_ssize_t length = btpy_list_length (self); | |
563 | Py_ssize_t start, stop, step, slicelength; | |
564 | ||
565 | if (PyInt_Check (value)) | |
566 | { | |
567 | Py_ssize_t index = PyInt_AsSsize_t (value); | |
568 | ||
569 | /* Emulate Python behavior for negative indices. */ | |
570 | if (index < 0) | |
571 | index += length; | |
572 | ||
573 | return btpy_list_item (self, index); | |
574 | } | |
575 | ||
576 | if (!PySlice_Check (value)) | |
577 | return PyErr_Format (PyExc_TypeError, _("Index must be int or slice.")); | |
578 | ||
579 | if (0 != PySlice_GetIndicesEx (BTPY_PYSLICE (value), length, &start, &stop, | |
580 | &step, &slicelength)) | |
581 | return NULL; | |
582 | ||
583 | return btpy_list_new (obj->ptid, obj->first + obj->step * start, | |
584 | obj->first + obj->step * stop, obj->step * step, | |
585 | obj->element_type); | |
586 | } | |
587 | ||
588 | /* Helper function that returns the position of an element in a BtraceList | |
589 | or -1 if the element is not in the list. */ | |
590 | ||
591 | static LONGEST | |
592 | btpy_list_position (PyObject *self, PyObject *value) | |
593 | { | |
594 | const btpy_list_object * const list_obj = (btpy_list_object *) self; | |
0ed5da75 | 595 | const recpy_element_object * const obj = (const recpy_element_object *) value; |
75c0bdf4 TW |
596 | Py_ssize_t index = obj->number; |
597 | ||
598 | if (list_obj->element_type != Py_TYPE (value)) | |
599 | return -1; | |
600 | ||
601 | if (!ptid_equal (list_obj->ptid, obj->ptid)) | |
602 | return -1; | |
603 | ||
604 | if (index < list_obj->first || index > list_obj->last) | |
605 | return -1; | |
606 | ||
607 | index -= list_obj->first; | |
608 | ||
609 | if (index % list_obj->step != 0) | |
610 | return -1; | |
611 | ||
612 | return index / list_obj->step; | |
613 | } | |
614 | ||
615 | /* Implementation of "in" operator for BtraceLists. */ | |
616 | ||
617 | static int | |
618 | btpy_list_contains (PyObject *self, PyObject *value) | |
619 | { | |
620 | if (btpy_list_position (self, value) < 0) | |
621 | return 0; | |
622 | ||
623 | return 1; | |
624 | } | |
625 | ||
626 | /* Implementation of BtraceLists.index (self, value) -> int. */ | |
627 | ||
628 | static PyObject * | |
629 | btpy_list_index (PyObject *self, PyObject *value) | |
630 | { | |
631 | const LONGEST index = btpy_list_position (self, value); | |
632 | ||
633 | if (index < 0) | |
634 | return PyErr_Format (PyExc_ValueError, _("Not in list.")); | |
635 | ||
636 | return gdb_py_long_from_longest (index); | |
637 | } | |
638 | ||
639 | /* Implementation of BtraceList.count (self, value) -> int. */ | |
640 | ||
641 | static PyObject * | |
642 | btpy_list_count (PyObject *self, PyObject *value) | |
643 | { | |
644 | /* We know that if an element is in the list, it is so exactly one time, | |
645 | enabling us to reuse the "is element of" check. */ | |
646 | return PyInt_FromLong (btpy_list_contains (self, value)); | |
647 | } | |
648 | ||
649 | /* Python rich compare function to allow for equality and inequality checks | |
650 | in Python. */ | |
651 | ||
652 | static PyObject * | |
653 | btpy_list_richcompare (PyObject *self, PyObject *other, int op) | |
654 | { | |
655 | const btpy_list_object * const obj1 = (btpy_list_object *) self; | |
656 | const btpy_list_object * const obj2 = (btpy_list_object *) other; | |
657 | ||
658 | if (Py_TYPE (self) != Py_TYPE (other)) | |
659 | { | |
660 | Py_INCREF (Py_NotImplemented); | |
661 | return Py_NotImplemented; | |
662 | } | |
663 | ||
664 | switch (op) | |
665 | { | |
666 | case Py_EQ: | |
667 | if (ptid_equal (obj1->ptid, obj2->ptid) | |
668 | && obj1->element_type == obj2->element_type | |
669 | && obj1->first == obj2->first | |
670 | && obj1->last == obj2->last | |
671 | && obj1->step == obj2->step) | |
672 | Py_RETURN_TRUE; | |
673 | else | |
674 | Py_RETURN_FALSE; | |
675 | ||
676 | case Py_NE: | |
677 | if (!ptid_equal (obj1->ptid, obj2->ptid) | |
678 | || obj1->element_type != obj2->element_type | |
679 | || obj1->first != obj2->first | |
680 | || obj1->last != obj2->last | |
681 | || obj1->step != obj2->step) | |
682 | Py_RETURN_TRUE; | |
683 | else | |
684 | Py_RETURN_FALSE; | |
685 | ||
686 | default: | |
687 | break; | |
688 | } | |
689 | ||
690 | Py_INCREF (Py_NotImplemented); | |
691 | return Py_NotImplemented; | |
692 | } | |
693 | ||
694 | /* Implementation of | |
695 | BtraceRecord.method [str]. */ | |
696 | ||
697 | PyObject * | |
698 | recpy_bt_method (PyObject *self, void *closure) | |
699 | { | |
700 | return PyString_FromString ("btrace"); | |
701 | } | |
702 | ||
703 | /* Implementation of | |
704 | BtraceRecord.format [str]. */ | |
705 | ||
706 | PyObject * | |
707 | recpy_bt_format (PyObject *self, void *closure) | |
708 | { | |
ae20e79a TW |
709 | const recpy_record_object * const record = (recpy_record_object *) self; |
710 | const struct thread_info * const tinfo = find_thread_ptid (record->ptid); | |
75c0bdf4 TW |
711 | const struct btrace_config * config; |
712 | ||
713 | if (tinfo == NULL) | |
714 | Py_RETURN_NONE; | |
715 | ||
716 | config = btrace_conf (&tinfo->btrace); | |
717 | ||
718 | if (config == NULL) | |
719 | Py_RETURN_NONE; | |
720 | ||
721 | return PyString_FromString (btrace_format_short_string (config->format)); | |
722 | } | |
723 | ||
724 | /* Implementation of | |
725 | BtraceRecord.replay_position [BtraceInstruction]. */ | |
726 | ||
727 | PyObject * | |
728 | recpy_bt_replay_position (PyObject *self, void *closure) | |
729 | { | |
ae20e79a TW |
730 | const recpy_record_object * const record = (recpy_record_object *) self; |
731 | const struct thread_info * const tinfo = find_thread_ptid (record->ptid); | |
75c0bdf4 TW |
732 | |
733 | if (tinfo == NULL) | |
734 | Py_RETURN_NONE; | |
735 | ||
736 | if (tinfo->btrace.replay == NULL) | |
737 | Py_RETURN_NONE; | |
738 | ||
913aeadd TW |
739 | return btpy_insn_or_gap_new (tinfo, |
740 | btrace_insn_number (tinfo->btrace.replay)); | |
75c0bdf4 TW |
741 | } |
742 | ||
743 | /* Implementation of | |
744 | BtraceRecord.begin [BtraceInstruction]. */ | |
745 | ||
746 | PyObject * | |
747 | recpy_bt_begin (PyObject *self, void *closure) | |
748 | { | |
ae20e79a TW |
749 | const recpy_record_object * const record = (recpy_record_object *) self; |
750 | struct thread_info * const tinfo = find_thread_ptid (record->ptid); | |
75c0bdf4 TW |
751 | struct btrace_insn_iterator iterator; |
752 | ||
753 | if (tinfo == NULL) | |
754 | Py_RETURN_NONE; | |
755 | ||
756 | btrace_fetch (tinfo); | |
757 | ||
758 | if (btrace_is_empty (tinfo)) | |
759 | Py_RETURN_NONE; | |
760 | ||
761 | btrace_insn_begin (&iterator, &tinfo->btrace); | |
913aeadd | 762 | return btpy_insn_or_gap_new (tinfo, btrace_insn_number (&iterator)); |
75c0bdf4 TW |
763 | } |
764 | ||
765 | /* Implementation of | |
766 | BtraceRecord.end [BtraceInstruction]. */ | |
767 | ||
768 | PyObject * | |
769 | recpy_bt_end (PyObject *self, void *closure) | |
770 | { | |
ae20e79a TW |
771 | const recpy_record_object * const record = (recpy_record_object *) self; |
772 | struct thread_info * const tinfo = find_thread_ptid (record->ptid); | |
75c0bdf4 TW |
773 | struct btrace_insn_iterator iterator; |
774 | ||
775 | if (tinfo == NULL) | |
776 | Py_RETURN_NONE; | |
777 | ||
778 | btrace_fetch (tinfo); | |
779 | ||
780 | if (btrace_is_empty (tinfo)) | |
781 | Py_RETURN_NONE; | |
782 | ||
783 | btrace_insn_end (&iterator, &tinfo->btrace); | |
913aeadd | 784 | return btpy_insn_or_gap_new (tinfo, btrace_insn_number (&iterator)); |
75c0bdf4 TW |
785 | } |
786 | ||
787 | /* Implementation of | |
788 | BtraceRecord.instruction_history [list]. */ | |
789 | ||
790 | PyObject * | |
791 | recpy_bt_instruction_history (PyObject *self, void *closure) | |
792 | { | |
ae20e79a TW |
793 | const recpy_record_object * const record = (recpy_record_object *) self; |
794 | struct thread_info * const tinfo = find_thread_ptid (record->ptid); | |
75c0bdf4 TW |
795 | struct btrace_insn_iterator iterator; |
796 | unsigned long first = 0; | |
797 | unsigned long last = 0; | |
798 | ||
799 | if (tinfo == NULL) | |
800 | Py_RETURN_NONE; | |
801 | ||
802 | btrace_fetch (tinfo); | |
803 | ||
804 | if (btrace_is_empty (tinfo)) | |
805 | Py_RETURN_NONE; | |
806 | ||
807 | btrace_insn_begin (&iterator, &tinfo->btrace); | |
808 | first = btrace_insn_number (&iterator); | |
809 | ||
810 | btrace_insn_end (&iterator, &tinfo->btrace); | |
811 | last = btrace_insn_number (&iterator); | |
812 | ||
0ed5da75 | 813 | return btpy_list_new (record->ptid, first, last, 1, &recpy_insn_type); |
75c0bdf4 TW |
814 | } |
815 | ||
816 | /* Implementation of | |
817 | BtraceRecord.function_call_history [list]. */ | |
818 | ||
819 | PyObject * | |
820 | recpy_bt_function_call_history (PyObject *self, void *closure) | |
821 | { | |
ae20e79a TW |
822 | const recpy_record_object * const record = (recpy_record_object *) self; |
823 | struct thread_info * const tinfo = find_thread_ptid (record->ptid); | |
8d0050ea TW |
824 | struct btrace_call_iterator iterator; |
825 | unsigned long first = 0; | |
826 | unsigned long last = 0; | |
75c0bdf4 | 827 | |
8d0050ea TW |
828 | if (tinfo == NULL) |
829 | Py_RETURN_NONE; | |
75c0bdf4 | 830 | |
8d0050ea | 831 | btrace_fetch (tinfo); |
75c0bdf4 | 832 | |
8d0050ea TW |
833 | if (btrace_is_empty (tinfo)) |
834 | Py_RETURN_NONE; | |
75c0bdf4 | 835 | |
8d0050ea TW |
836 | btrace_call_begin (&iterator, &tinfo->btrace); |
837 | first = btrace_call_number (&iterator); | |
75c0bdf4 | 838 | |
8d0050ea TW |
839 | btrace_call_end (&iterator, &tinfo->btrace); |
840 | last = btrace_call_number (&iterator); | |
75c0bdf4 | 841 | |
ae20e79a | 842 | return btpy_list_new (record->ptid, first, last, 1, &btpy_call_type); |
75c0bdf4 TW |
843 | } |
844 | ||
845 | /* Implementation of BtraceRecord.goto (self, BtraceInstruction) -> None. */ | |
846 | ||
847 | PyObject * | |
848 | recpy_bt_goto (PyObject *self, PyObject *args) | |
849 | { | |
ae20e79a TW |
850 | const recpy_record_object * const record = (recpy_record_object *) self; |
851 | struct thread_info * const tinfo = find_thread_ptid (record->ptid); | |
0ed5da75 | 852 | const recpy_element_object *obj; |
75c0bdf4 TW |
853 | |
854 | if (tinfo == NULL || btrace_is_empty (tinfo)) | |
855 | return PyErr_Format (gdbpy_gdb_error, _("Empty branch trace.")); | |
856 | ||
857 | if (!PyArg_ParseTuple (args, "O", &obj)) | |
858 | return NULL; | |
859 | ||
0ed5da75 | 860 | if (Py_TYPE (obj) != &recpy_insn_type) |
75c0bdf4 TW |
861 | return PyErr_Format (PyExc_TypeError, _("Argument must be instruction.")); |
862 | ||
863 | TRY | |
864 | { | |
865 | struct btrace_insn_iterator iter; | |
866 | ||
867 | btrace_insn_end (&iter, &tinfo->btrace); | |
868 | ||
869 | if (btrace_insn_number (&iter) == obj->number) | |
870 | target_goto_record_end (); | |
871 | else | |
872 | target_goto_record (obj->number); | |
873 | } | |
874 | CATCH (except, RETURN_MASK_ALL) | |
875 | { | |
876 | GDB_PY_HANDLE_EXCEPTION (except); | |
877 | } | |
878 | END_CATCH | |
879 | ||
880 | Py_RETURN_NONE; | |
881 | } | |
882 | ||
75c0bdf4 TW |
883 | /* BtraceFunctionCall members. */ |
884 | ||
0d1f4ceb | 885 | static gdb_PyGetSetDef btpy_call_getset[] = |
75c0bdf4 TW |
886 | { |
887 | { "number", btpy_number, NULL, "function call number", NULL}, | |
888 | { "level", btpy_call_level, NULL, "call stack level", NULL}, | |
889 | { "symbol", btpy_call_symbol, NULL, "associated line and symbol", NULL}, | |
890 | { "instructions", btpy_call_instructions, NULL, "list of instructions in \ | |
891 | this function segment", NULL}, | |
892 | { "up", btpy_call_up, NULL, "caller or returned-to function segment", NULL}, | |
893 | { "prev_sibling", btpy_call_prev_sibling, NULL, "previous segment of this \ | |
894 | function", NULL}, | |
895 | { "next_sibling", btpy_call_next_sibling, NULL, "next segment of this \ | |
896 | function", NULL}, | |
897 | {NULL} | |
898 | }; | |
899 | ||
900 | /* BtraceList methods. */ | |
901 | ||
902 | struct PyMethodDef btpy_list_methods[] = | |
903 | { | |
904 | { "count", btpy_list_count, METH_O, "count number of occurences"}, | |
905 | { "index", btpy_list_index, METH_O, "index of entry"}, | |
906 | {NULL} | |
907 | }; | |
908 | ||
909 | /* BtraceList sequence methods. */ | |
910 | ||
911 | static PySequenceMethods btpy_list_sequence_methods = | |
912 | { | |
913 | NULL | |
914 | }; | |
915 | ||
916 | /* BtraceList mapping methods. Necessary for slicing. */ | |
917 | ||
918 | static PyMappingMethods btpy_list_mapping_methods = | |
919 | { | |
920 | NULL | |
921 | }; | |
922 | ||
923 | /* Sets up the btrace record API. */ | |
924 | ||
925 | int | |
926 | gdbpy_initialize_btrace (void) | |
927 | { | |
75c0bdf4 TW |
928 | btpy_call_type.tp_new = PyType_GenericNew; |
929 | btpy_call_type.tp_flags = Py_TPFLAGS_DEFAULT; | |
0ed5da75 | 930 | btpy_call_type.tp_basicsize = sizeof (recpy_element_object); |
75c0bdf4 TW |
931 | btpy_call_type.tp_name = "gdb.BtraceFunctionCall"; |
932 | btpy_call_type.tp_doc = "GDB btrace call object"; | |
933 | btpy_call_type.tp_getset = btpy_call_getset; | |
934 | btpy_call_type.tp_richcompare = btpy_richcompare; | |
935 | btpy_call_type.tp_hash = btpy_hash; | |
936 | ||
937 | btpy_list_type.tp_new = PyType_GenericNew; | |
938 | btpy_list_type.tp_flags = Py_TPFLAGS_DEFAULT; | |
939 | btpy_list_type.tp_basicsize = sizeof (btpy_list_object); | |
940 | btpy_list_type.tp_name = "gdb.BtraceObjectList"; | |
941 | btpy_list_type.tp_doc = "GDB btrace list object"; | |
942 | btpy_list_type.tp_methods = btpy_list_methods; | |
943 | btpy_list_type.tp_as_sequence = &btpy_list_sequence_methods; | |
944 | btpy_list_type.tp_as_mapping = &btpy_list_mapping_methods; | |
945 | btpy_list_type.tp_richcompare = btpy_list_richcompare; | |
946 | ||
947 | btpy_list_sequence_methods.sq_item = btpy_list_item; | |
948 | btpy_list_sequence_methods.sq_length = btpy_list_length; | |
949 | btpy_list_sequence_methods.sq_contains = btpy_list_contains; | |
950 | ||
951 | btpy_list_mapping_methods.mp_subscript = btpy_list_slice; | |
952 | ||
0ed5da75 | 953 | if (PyType_Ready (&btpy_call_type) < 0 |
75c0bdf4 TW |
954 | || PyType_Ready (&btpy_list_type) < 0) |
955 | return -1; | |
956 | else | |
957 | return 0; | |
958 | } |