Commit | Line | Data |
---|---|---|
4726b2d8 TW |
1 | /* Python interface to record targets. |
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" | |
ae20e79a | 21 | #include "py-record.h" |
75c0bdf4 TW |
22 | #include "py-record-btrace.h" |
23 | #include "py-record-full.h" | |
4726b2d8 TW |
24 | #include "target.h" |
25 | ||
4726b2d8 TW |
26 | /* Python Record type. */ |
27 | ||
28 | static PyTypeObject recpy_record_type = { | |
29 | PyVarObject_HEAD_INIT (NULL, 0) | |
30 | }; | |
31 | ||
0ed5da75 TW |
32 | /* Python RecordInstruction type. */ |
33 | ||
34 | PyTypeObject recpy_insn_type = { | |
35 | PyVarObject_HEAD_INIT (NULL, 0) | |
36 | }; | |
37 | ||
913aeadd TW |
38 | /* Python RecordGap type. */ |
39 | ||
40 | PyTypeObject recpy_gap_type = { | |
41 | PyVarObject_HEAD_INIT (NULL, 0) | |
42 | }; | |
43 | ||
44 | /* Python RecordGap object. */ | |
45 | typedef struct | |
46 | { | |
47 | PyObject_HEAD | |
48 | ||
49 | /* Reason code. */ | |
50 | int reason_code; | |
51 | ||
52 | /* Reason message. */ | |
53 | const char *reason_string; | |
54 | ||
55 | /* Element number. */ | |
56 | Py_ssize_t number; | |
57 | } recpy_gap_object; | |
58 | ||
4726b2d8 TW |
59 | /* Implementation of record.method. */ |
60 | ||
61 | static PyObject * | |
62 | recpy_method (PyObject *self, void* closure) | |
63 | { | |
75c0bdf4 TW |
64 | const recpy_record_object * const obj = (recpy_record_object *) self; |
65 | ||
66 | if (obj->method == RECORD_METHOD_FULL) | |
67 | return recpy_full_method (self, closure); | |
68 | ||
69 | if (obj->method == RECORD_METHOD_BTRACE) | |
70 | return recpy_bt_method (self, closure); | |
71 | ||
4726b2d8 TW |
72 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); |
73 | } | |
74 | ||
75 | /* Implementation of record.format. */ | |
76 | ||
77 | static PyObject * | |
78 | recpy_format (PyObject *self, void* closure) | |
79 | { | |
75c0bdf4 TW |
80 | const recpy_record_object * const obj = (recpy_record_object *) self; |
81 | ||
82 | if (obj->method == RECORD_METHOD_FULL) | |
83 | return recpy_full_format (self, closure); | |
84 | ||
85 | if (obj->method == RECORD_METHOD_BTRACE) | |
86 | return recpy_bt_format (self, closure); | |
87 | ||
4726b2d8 TW |
88 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); |
89 | } | |
90 | ||
91 | /* Implementation of record.goto (instruction) -> None. */ | |
92 | ||
93 | static PyObject * | |
94 | recpy_goto (PyObject *self, PyObject *value) | |
95 | { | |
75c0bdf4 TW |
96 | const recpy_record_object * const obj = (recpy_record_object *) self; |
97 | ||
98 | if (obj->method == RECORD_METHOD_BTRACE) | |
99 | return recpy_bt_goto (self, value); | |
100 | ||
4726b2d8 TW |
101 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); |
102 | } | |
103 | ||
104 | /* Implementation of record.replay_position [instruction] */ | |
105 | ||
106 | static PyObject * | |
107 | recpy_replay_position (PyObject *self, void *closure) | |
108 | { | |
75c0bdf4 TW |
109 | const recpy_record_object * const obj = (recpy_record_object *) self; |
110 | ||
111 | if (obj->method == RECORD_METHOD_BTRACE) | |
112 | return recpy_bt_replay_position (self, closure); | |
113 | ||
4726b2d8 TW |
114 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); |
115 | } | |
116 | ||
117 | /* Implementation of record.instruction_history [list]. */ | |
118 | ||
119 | static PyObject * | |
120 | recpy_instruction_history (PyObject *self, void* closure) | |
121 | { | |
75c0bdf4 TW |
122 | const recpy_record_object * const obj = (recpy_record_object *) self; |
123 | ||
124 | if (obj->method == RECORD_METHOD_BTRACE) | |
125 | return recpy_bt_instruction_history (self, closure); | |
126 | ||
4726b2d8 TW |
127 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); |
128 | } | |
129 | ||
130 | /* Implementation of record.function_call_history [list]. */ | |
131 | ||
132 | static PyObject * | |
133 | recpy_function_call_history (PyObject *self, void* closure) | |
134 | { | |
75c0bdf4 TW |
135 | const recpy_record_object * const obj = (recpy_record_object *) self; |
136 | ||
137 | if (obj->method == RECORD_METHOD_BTRACE) | |
138 | return recpy_bt_function_call_history (self, closure); | |
139 | ||
4726b2d8 TW |
140 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); |
141 | } | |
142 | ||
143 | /* Implementation of record.begin [instruction]. */ | |
144 | ||
145 | static PyObject * | |
146 | recpy_begin (PyObject *self, void* closure) | |
147 | { | |
75c0bdf4 TW |
148 | const recpy_record_object * const obj = (recpy_record_object *) self; |
149 | ||
150 | if (obj->method == RECORD_METHOD_BTRACE) | |
151 | return recpy_bt_begin (self, closure); | |
152 | ||
4726b2d8 TW |
153 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); |
154 | } | |
155 | ||
156 | /* Implementation of record.end [instruction]. */ | |
157 | ||
158 | static PyObject * | |
159 | recpy_end (PyObject *self, void* closure) | |
160 | { | |
75c0bdf4 TW |
161 | const recpy_record_object * const obj = (recpy_record_object *) self; |
162 | ||
163 | if (obj->method == RECORD_METHOD_BTRACE) | |
164 | return recpy_bt_end (self, closure); | |
165 | ||
4726b2d8 TW |
166 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); |
167 | } | |
168 | ||
0ed5da75 TW |
169 | /* Create a new gdb.RecordInstruction object. */ |
170 | ||
171 | PyObject * | |
172 | recpy_insn_new (ptid_t ptid, enum record_method method, Py_ssize_t number) | |
173 | { | |
174 | recpy_element_object * const obj = PyObject_New (recpy_element_object, | |
175 | &recpy_insn_type); | |
176 | ||
177 | if (obj == NULL) | |
178 | return NULL; | |
179 | ||
180 | obj->ptid = ptid; | |
181 | obj->method = method; | |
182 | obj->number = number; | |
183 | ||
184 | return (PyObject *) obj; | |
185 | } | |
186 | ||
187 | /* Implementation of RecordInstruction.sal [gdb.Symtab_and_line]. */ | |
188 | ||
189 | static PyObject * | |
190 | recpy_insn_sal (PyObject *self, void *closure) | |
191 | { | |
192 | const recpy_element_object * const obj = (recpy_element_object *) self; | |
193 | ||
194 | if (obj->method == RECORD_METHOD_BTRACE) | |
195 | return recpy_bt_insn_sal (self, closure); | |
196 | ||
197 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); | |
198 | } | |
199 | ||
200 | /* Implementation of RecordInstruction.pc [int]. */ | |
201 | ||
202 | static PyObject * | |
203 | recpy_insn_pc (PyObject *self, void *closure) | |
204 | { | |
205 | const recpy_element_object * const obj = (recpy_element_object *) self; | |
206 | ||
207 | if (obj->method == RECORD_METHOD_BTRACE) | |
208 | return recpy_bt_insn_pc (self, closure); | |
209 | ||
210 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); | |
211 | } | |
212 | ||
213 | /* Implementation of RecordInstruction.data [buffer]. */ | |
214 | ||
215 | static PyObject * | |
216 | recpy_insn_data (PyObject *self, void *closure) | |
217 | { | |
218 | const recpy_element_object * const obj = (recpy_element_object *) self; | |
219 | ||
220 | if (obj->method == RECORD_METHOD_BTRACE) | |
221 | return recpy_bt_insn_data (self, closure); | |
222 | ||
223 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); | |
224 | } | |
225 | ||
226 | /* Implementation of RecordInstruction.decoded [str]. */ | |
227 | ||
228 | static PyObject * | |
229 | recpy_insn_decoded (PyObject *self, void *closure) | |
230 | { | |
231 | const recpy_element_object * const obj = (recpy_element_object *) self; | |
232 | ||
233 | if (obj->method == RECORD_METHOD_BTRACE) | |
234 | return recpy_bt_insn_decoded (self, closure); | |
235 | ||
236 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); | |
237 | } | |
238 | ||
239 | /* Implementation of RecordInstruction.size [int]. */ | |
240 | ||
241 | static PyObject * | |
242 | recpy_insn_size (PyObject *self, void *closure) | |
243 | { | |
244 | const recpy_element_object * const obj = (recpy_element_object *) self; | |
245 | ||
246 | if (obj->method == RECORD_METHOD_BTRACE) | |
247 | return recpy_bt_insn_size (self, closure); | |
248 | ||
249 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); | |
250 | } | |
251 | ||
252 | /* Implementation of RecordInstruction.is_speculative [bool]. */ | |
253 | ||
254 | static PyObject * | |
255 | recpy_insn_is_speculative (PyObject *self, void *closure) | |
256 | { | |
257 | const recpy_element_object * const obj = (recpy_element_object *) self; | |
258 | ||
259 | if (obj->method == RECORD_METHOD_BTRACE) | |
260 | return recpy_bt_insn_is_speculative (self, closure); | |
261 | ||
262 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); | |
263 | } | |
264 | ||
265 | /* Implementation of RecordInstruction.number [int]. */ | |
266 | ||
267 | static PyObject * | |
268 | recpy_element_number (PyObject *self, void* closure) | |
269 | { | |
270 | const recpy_element_object * const obj = (recpy_element_object *) self; | |
271 | ||
272 | return PyInt_FromSsize_t (obj->number); | |
273 | } | |
274 | ||
275 | /* Implementation of RecordInstruction.__hash__ [int]. */ | |
276 | static Py_hash_t | |
277 | recpy_element_hash (PyObject *self) | |
278 | { | |
279 | const recpy_element_object * const obj = (recpy_element_object *) self; | |
280 | ||
281 | return obj->number; | |
282 | } | |
283 | ||
284 | /* Implementation of operator == and != of RecordInstruction. */ | |
285 | ||
286 | static PyObject * | |
287 | recpy_element_richcompare (PyObject *self, PyObject *other, int op) | |
288 | { | |
289 | const recpy_element_object * const obj1 = (recpy_element_object *) self; | |
290 | const recpy_element_object * const obj2 = (recpy_element_object *) other; | |
291 | ||
292 | if (Py_TYPE (self) != Py_TYPE (other)) | |
293 | { | |
294 | Py_INCREF (Py_NotImplemented); | |
295 | return Py_NotImplemented; | |
296 | } | |
297 | ||
298 | switch (op) | |
299 | { | |
300 | case Py_EQ: | |
301 | if (ptid_equal (obj1->ptid, obj2->ptid) | |
302 | && obj1->method == obj2->method | |
303 | && obj1->number == obj2->number) | |
304 | Py_RETURN_TRUE; | |
305 | else | |
306 | Py_RETURN_FALSE; | |
307 | ||
308 | case Py_NE: | |
309 | if (!ptid_equal (obj1->ptid, obj2->ptid) | |
310 | || obj1->method != obj2->method | |
311 | || obj1->number != obj2->number) | |
312 | Py_RETURN_TRUE; | |
313 | else | |
314 | Py_RETURN_FALSE; | |
315 | ||
316 | default: | |
317 | break; | |
318 | } | |
319 | ||
320 | Py_INCREF (Py_NotImplemented); | |
321 | return Py_NotImplemented; | |
322 | } | |
323 | ||
913aeadd TW |
324 | /* Create a new gdb.RecordGap object. */ |
325 | ||
326 | PyObject * | |
327 | recpy_gap_new (int reason_code, const char *reason_string, Py_ssize_t number) | |
328 | { | |
329 | recpy_gap_object * const obj = PyObject_New (recpy_gap_object, | |
330 | &recpy_gap_type); | |
331 | ||
332 | if (obj == NULL) | |
333 | return NULL; | |
334 | ||
335 | obj->reason_code = reason_code; | |
336 | obj->reason_string = reason_string; | |
337 | obj->number = number; | |
338 | ||
339 | return (PyObject *) obj; | |
340 | } | |
341 | ||
342 | /* Implementation of RecordGap.number [int]. */ | |
343 | ||
344 | static PyObject * | |
345 | recpy_gap_number (PyObject *self, void *closure) | |
346 | { | |
347 | const recpy_gap_object * const obj = (const recpy_gap_object *) self; | |
348 | ||
349 | return PyInt_FromSsize_t (obj->number); | |
350 | } | |
351 | ||
352 | /* Implementation of RecordGap.error_code [int]. */ | |
353 | ||
354 | static PyObject * | |
355 | recpy_gap_reason_code (PyObject *self, void *closure) | |
356 | { | |
357 | const recpy_gap_object * const obj = (const recpy_gap_object *) self; | |
358 | ||
359 | return PyInt_FromLong (obj->reason_code); | |
360 | } | |
361 | ||
362 | /* Implementation of RecordGap.error_string [str]. */ | |
363 | ||
364 | static PyObject * | |
365 | recpy_gap_reason_string (PyObject *self, void *closure) | |
366 | { | |
367 | const recpy_gap_object * const obj = (const recpy_gap_object *) self; | |
368 | ||
369 | return PyString_FromString (obj->reason_string); | |
370 | } | |
371 | ||
4726b2d8 TW |
372 | /* Record method list. */ |
373 | ||
374 | static PyMethodDef recpy_record_methods[] = { | |
375 | { "goto", recpy_goto, METH_VARARGS, | |
376 | "goto (instruction|function_call) -> None.\n\ | |
377 | Rewind to given location."}, | |
378 | { NULL } | |
379 | }; | |
380 | ||
381 | /* Record member list. */ | |
382 | ||
0d1f4ceb | 383 | static gdb_PyGetSetDef recpy_record_getset[] = { |
4726b2d8 TW |
384 | { "method", recpy_method, NULL, "Current recording method.", NULL }, |
385 | { "format", recpy_format, NULL, "Current recording format.", NULL }, | |
386 | { "replay_position", recpy_replay_position, NULL, "Current replay position.", | |
387 | NULL }, | |
388 | { "instruction_history", recpy_instruction_history, NULL, | |
389 | "List of instructions in current recording.", NULL }, | |
390 | { "function_call_history", recpy_function_call_history, NULL, | |
391 | "List of function calls in current recording.", NULL }, | |
392 | { "begin", recpy_begin, NULL, | |
393 | "First instruction in current recording.", NULL }, | |
394 | { "end", recpy_end, NULL, | |
395 | "One past the last instruction in current recording. This is typically \ | |
396 | the current instruction and is used for e.g. record.goto (record.end).", NULL }, | |
397 | { NULL } | |
398 | }; | |
399 | ||
0ed5da75 TW |
400 | /* RecordInstruction member list. */ |
401 | ||
402 | static gdb_PyGetSetDef recpy_insn_getset[] = { | |
403 | { "number", recpy_element_number, NULL, "instruction number", NULL}, | |
404 | { "sal", recpy_insn_sal, NULL, "associated symbol and line", NULL}, | |
405 | { "pc", recpy_insn_pc, NULL, "instruction address", NULL}, | |
406 | { "data", recpy_insn_data, NULL, "raw instruction data", NULL}, | |
407 | { "decoded", recpy_insn_decoded, NULL, "decoded instruction", NULL}, | |
408 | { "size", recpy_insn_size, NULL, "instruction size in byte", NULL}, | |
409 | { "is_speculative", recpy_insn_is_speculative, NULL, "if the instruction was \ | |
410 | executed speculatively", NULL}, | |
411 | { NULL } | |
412 | }; | |
413 | ||
913aeadd TW |
414 | /* RecordGap member list. */ |
415 | ||
416 | static gdb_PyGetSetDef recpy_gap_getset[] = { | |
417 | { "number", recpy_gap_number, NULL, "element number", NULL}, | |
418 | { "reason_code", recpy_gap_reason_code, NULL, "reason code", NULL}, | |
419 | { "reason_string", recpy_gap_reason_string, NULL, "reason string", NULL}, | |
420 | { NULL } | |
421 | }; | |
422 | ||
4726b2d8 TW |
423 | /* Sets up the record API in the gdb module. */ |
424 | ||
425 | int | |
426 | gdbpy_initialize_record (void) | |
427 | { | |
428 | recpy_record_type.tp_new = PyType_GenericNew; | |
429 | recpy_record_type.tp_flags = Py_TPFLAGS_DEFAULT; | |
430 | recpy_record_type.tp_basicsize = sizeof (recpy_record_object); | |
431 | recpy_record_type.tp_name = "gdb.Record"; | |
432 | recpy_record_type.tp_doc = "GDB record object"; | |
433 | recpy_record_type.tp_methods = recpy_record_methods; | |
434 | recpy_record_type.tp_getset = recpy_record_getset; | |
435 | ||
0ed5da75 TW |
436 | recpy_insn_type.tp_new = PyType_GenericNew; |
437 | recpy_insn_type.tp_flags = Py_TPFLAGS_DEFAULT; | |
438 | recpy_insn_type.tp_basicsize = sizeof (recpy_element_object); | |
439 | recpy_insn_type.tp_name = "gdb.RecordInstruction"; | |
440 | recpy_insn_type.tp_doc = "GDB recorded instruction object"; | |
441 | recpy_insn_type.tp_getset = recpy_insn_getset; | |
442 | recpy_insn_type.tp_richcompare = recpy_element_richcompare; | |
443 | recpy_insn_type.tp_hash = recpy_element_hash; | |
444 | ||
913aeadd TW |
445 | recpy_gap_type.tp_new = PyType_GenericNew; |
446 | recpy_gap_type.tp_flags = Py_TPFLAGS_DEFAULT; | |
447 | recpy_gap_type.tp_basicsize = sizeof (recpy_gap_object); | |
448 | recpy_gap_type.tp_name = "gdb.RecordGap"; | |
449 | recpy_gap_type.tp_doc = "GDB recorded gap object"; | |
450 | recpy_gap_type.tp_getset = recpy_gap_getset; | |
451 | ||
452 | if (PyType_Ready (&recpy_record_type) < 0 | |
0ed5da75 | 453 | || PyType_Ready (&recpy_insn_type) < 0 |
913aeadd TW |
454 | || PyType_Ready (&recpy_gap_type) < 0) |
455 | return -1; | |
456 | else | |
457 | return 0; | |
4726b2d8 TW |
458 | } |
459 | ||
460 | /* Implementation of gdb.start_recording (method) -> gdb.Record. */ | |
461 | ||
462 | PyObject * | |
463 | gdbpy_start_recording (PyObject *self, PyObject *args) | |
464 | { | |
465 | const char *method = NULL; | |
466 | const char *format = NULL; | |
467 | PyObject *ret = NULL; | |
468 | ||
469 | if (!PyArg_ParseTuple (args, "|ss", &method, &format)) | |
470 | return NULL; | |
471 | ||
472 | TRY | |
473 | { | |
474 | record_start (method, format, 0); | |
475 | ret = gdbpy_current_recording (self, args); | |
476 | } | |
477 | CATCH (except, RETURN_MASK_ALL) | |
478 | { | |
479 | gdbpy_convert_exception (except); | |
480 | } | |
481 | END_CATCH | |
482 | ||
483 | return ret; | |
484 | } | |
485 | ||
486 | /* Implementation of gdb.current_recording (self) -> gdb.Record. */ | |
487 | ||
488 | PyObject * | |
489 | gdbpy_current_recording (PyObject *self, PyObject *args) | |
490 | { | |
491 | recpy_record_object *ret = NULL; | |
492 | ||
493 | if (find_record_target () == NULL) | |
494 | Py_RETURN_NONE; | |
495 | ||
496 | ret = PyObject_New (recpy_record_object, &recpy_record_type); | |
497 | ret->ptid = inferior_ptid; | |
498 | ret->method = target_record_method (inferior_ptid); | |
499 | ||
500 | return (PyObject *) ret; | |
501 | } | |
502 | ||
503 | /* Implementation of gdb.stop_recording (self) -> None. */ | |
504 | ||
505 | PyObject * | |
506 | gdbpy_stop_recording (PyObject *self, PyObject *args) | |
507 | { | |
508 | PyObject *ret = NULL; | |
509 | ||
510 | TRY | |
511 | { | |
512 | record_stop (0); | |
513 | ret = Py_None; | |
514 | Py_INCREF (Py_None); | |
515 | } | |
516 | CATCH (except, RETURN_MASK_ALL) | |
517 | { | |
518 | gdbpy_convert_exception (except); | |
519 | } | |
520 | END_CATCH | |
521 | ||
522 | return ret; | |
523 | } |