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 | ||
913aeadd TW |
32 | /* Python RecordGap type. */ |
33 | ||
34 | PyTypeObject recpy_gap_type = { | |
35 | PyVarObject_HEAD_INIT (NULL, 0) | |
36 | }; | |
37 | ||
38 | /* Python RecordGap object. */ | |
39 | typedef struct | |
40 | { | |
41 | PyObject_HEAD | |
42 | ||
43 | /* Reason code. */ | |
44 | int reason_code; | |
45 | ||
46 | /* Reason message. */ | |
47 | const char *reason_string; | |
48 | ||
49 | /* Element number. */ | |
50 | Py_ssize_t number; | |
51 | } recpy_gap_object; | |
52 | ||
4726b2d8 TW |
53 | /* Implementation of record.method. */ |
54 | ||
55 | static PyObject * | |
56 | recpy_method (PyObject *self, void* closure) | |
57 | { | |
75c0bdf4 TW |
58 | const recpy_record_object * const obj = (recpy_record_object *) self; |
59 | ||
60 | if (obj->method == RECORD_METHOD_FULL) | |
61 | return recpy_full_method (self, closure); | |
62 | ||
63 | if (obj->method == RECORD_METHOD_BTRACE) | |
64 | return recpy_bt_method (self, closure); | |
65 | ||
4726b2d8 TW |
66 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); |
67 | } | |
68 | ||
69 | /* Implementation of record.format. */ | |
70 | ||
71 | static PyObject * | |
72 | recpy_format (PyObject *self, void* closure) | |
73 | { | |
75c0bdf4 TW |
74 | const recpy_record_object * const obj = (recpy_record_object *) self; |
75 | ||
76 | if (obj->method == RECORD_METHOD_FULL) | |
77 | return recpy_full_format (self, closure); | |
78 | ||
79 | if (obj->method == RECORD_METHOD_BTRACE) | |
80 | return recpy_bt_format (self, closure); | |
81 | ||
4726b2d8 TW |
82 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); |
83 | } | |
84 | ||
85 | /* Implementation of record.goto (instruction) -> None. */ | |
86 | ||
87 | static PyObject * | |
88 | recpy_goto (PyObject *self, PyObject *value) | |
89 | { | |
75c0bdf4 TW |
90 | const recpy_record_object * const obj = (recpy_record_object *) self; |
91 | ||
92 | if (obj->method == RECORD_METHOD_BTRACE) | |
93 | return recpy_bt_goto (self, value); | |
94 | ||
4726b2d8 TW |
95 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); |
96 | } | |
97 | ||
98 | /* Implementation of record.replay_position [instruction] */ | |
99 | ||
100 | static PyObject * | |
101 | recpy_replay_position (PyObject *self, void *closure) | |
102 | { | |
75c0bdf4 TW |
103 | const recpy_record_object * const obj = (recpy_record_object *) self; |
104 | ||
105 | if (obj->method == RECORD_METHOD_BTRACE) | |
106 | return recpy_bt_replay_position (self, closure); | |
107 | ||
4726b2d8 TW |
108 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); |
109 | } | |
110 | ||
111 | /* Implementation of record.instruction_history [list]. */ | |
112 | ||
113 | static PyObject * | |
114 | recpy_instruction_history (PyObject *self, void* closure) | |
115 | { | |
75c0bdf4 TW |
116 | const recpy_record_object * const obj = (recpy_record_object *) self; |
117 | ||
118 | if (obj->method == RECORD_METHOD_BTRACE) | |
119 | return recpy_bt_instruction_history (self, closure); | |
120 | ||
4726b2d8 TW |
121 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); |
122 | } | |
123 | ||
124 | /* Implementation of record.function_call_history [list]. */ | |
125 | ||
126 | static PyObject * | |
127 | recpy_function_call_history (PyObject *self, void* closure) | |
128 | { | |
75c0bdf4 TW |
129 | const recpy_record_object * const obj = (recpy_record_object *) self; |
130 | ||
131 | if (obj->method == RECORD_METHOD_BTRACE) | |
132 | return recpy_bt_function_call_history (self, closure); | |
133 | ||
4726b2d8 TW |
134 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); |
135 | } | |
136 | ||
137 | /* Implementation of record.begin [instruction]. */ | |
138 | ||
139 | static PyObject * | |
140 | recpy_begin (PyObject *self, void* closure) | |
141 | { | |
75c0bdf4 TW |
142 | const recpy_record_object * const obj = (recpy_record_object *) self; |
143 | ||
144 | if (obj->method == RECORD_METHOD_BTRACE) | |
145 | return recpy_bt_begin (self, closure); | |
146 | ||
4726b2d8 TW |
147 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); |
148 | } | |
149 | ||
150 | /* Implementation of record.end [instruction]. */ | |
151 | ||
152 | static PyObject * | |
153 | recpy_end (PyObject *self, void* closure) | |
154 | { | |
75c0bdf4 TW |
155 | const recpy_record_object * const obj = (recpy_record_object *) self; |
156 | ||
157 | if (obj->method == RECORD_METHOD_BTRACE) | |
158 | return recpy_bt_end (self, closure); | |
159 | ||
4726b2d8 TW |
160 | return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); |
161 | } | |
162 | ||
913aeadd TW |
163 | /* Create a new gdb.RecordGap object. */ |
164 | ||
165 | PyObject * | |
166 | recpy_gap_new (int reason_code, const char *reason_string, Py_ssize_t number) | |
167 | { | |
168 | recpy_gap_object * const obj = PyObject_New (recpy_gap_object, | |
169 | &recpy_gap_type); | |
170 | ||
171 | if (obj == NULL) | |
172 | return NULL; | |
173 | ||
174 | obj->reason_code = reason_code; | |
175 | obj->reason_string = reason_string; | |
176 | obj->number = number; | |
177 | ||
178 | return (PyObject *) obj; | |
179 | } | |
180 | ||
181 | /* Implementation of RecordGap.number [int]. */ | |
182 | ||
183 | static PyObject * | |
184 | recpy_gap_number (PyObject *self, void *closure) | |
185 | { | |
186 | const recpy_gap_object * const obj = (const recpy_gap_object *) self; | |
187 | ||
188 | return PyInt_FromSsize_t (obj->number); | |
189 | } | |
190 | ||
191 | /* Implementation of RecordGap.error_code [int]. */ | |
192 | ||
193 | static PyObject * | |
194 | recpy_gap_reason_code (PyObject *self, void *closure) | |
195 | { | |
196 | const recpy_gap_object * const obj = (const recpy_gap_object *) self; | |
197 | ||
198 | return PyInt_FromLong (obj->reason_code); | |
199 | } | |
200 | ||
201 | /* Implementation of RecordGap.error_string [str]. */ | |
202 | ||
203 | static PyObject * | |
204 | recpy_gap_reason_string (PyObject *self, void *closure) | |
205 | { | |
206 | const recpy_gap_object * const obj = (const recpy_gap_object *) self; | |
207 | ||
208 | return PyString_FromString (obj->reason_string); | |
209 | } | |
210 | ||
4726b2d8 TW |
211 | /* Record method list. */ |
212 | ||
213 | static PyMethodDef recpy_record_methods[] = { | |
214 | { "goto", recpy_goto, METH_VARARGS, | |
215 | "goto (instruction|function_call) -> None.\n\ | |
216 | Rewind to given location."}, | |
217 | { NULL } | |
218 | }; | |
219 | ||
220 | /* Record member list. */ | |
221 | ||
0d1f4ceb | 222 | static gdb_PyGetSetDef recpy_record_getset[] = { |
4726b2d8 TW |
223 | { "method", recpy_method, NULL, "Current recording method.", NULL }, |
224 | { "format", recpy_format, NULL, "Current recording format.", NULL }, | |
225 | { "replay_position", recpy_replay_position, NULL, "Current replay position.", | |
226 | NULL }, | |
227 | { "instruction_history", recpy_instruction_history, NULL, | |
228 | "List of instructions in current recording.", NULL }, | |
229 | { "function_call_history", recpy_function_call_history, NULL, | |
230 | "List of function calls in current recording.", NULL }, | |
231 | { "begin", recpy_begin, NULL, | |
232 | "First instruction in current recording.", NULL }, | |
233 | { "end", recpy_end, NULL, | |
234 | "One past the last instruction in current recording. This is typically \ | |
235 | the current instruction and is used for e.g. record.goto (record.end).", NULL }, | |
236 | { NULL } | |
237 | }; | |
238 | ||
913aeadd TW |
239 | /* RecordGap member list. */ |
240 | ||
241 | static gdb_PyGetSetDef recpy_gap_getset[] = { | |
242 | { "number", recpy_gap_number, NULL, "element number", NULL}, | |
243 | { "reason_code", recpy_gap_reason_code, NULL, "reason code", NULL}, | |
244 | { "reason_string", recpy_gap_reason_string, NULL, "reason string", NULL}, | |
245 | { NULL } | |
246 | }; | |
247 | ||
4726b2d8 TW |
248 | /* Sets up the record API in the gdb module. */ |
249 | ||
250 | int | |
251 | gdbpy_initialize_record (void) | |
252 | { | |
253 | recpy_record_type.tp_new = PyType_GenericNew; | |
254 | recpy_record_type.tp_flags = Py_TPFLAGS_DEFAULT; | |
255 | recpy_record_type.tp_basicsize = sizeof (recpy_record_object); | |
256 | recpy_record_type.tp_name = "gdb.Record"; | |
257 | recpy_record_type.tp_doc = "GDB record object"; | |
258 | recpy_record_type.tp_methods = recpy_record_methods; | |
259 | recpy_record_type.tp_getset = recpy_record_getset; | |
260 | ||
913aeadd TW |
261 | recpy_gap_type.tp_new = PyType_GenericNew; |
262 | recpy_gap_type.tp_flags = Py_TPFLAGS_DEFAULT; | |
263 | recpy_gap_type.tp_basicsize = sizeof (recpy_gap_object); | |
264 | recpy_gap_type.tp_name = "gdb.RecordGap"; | |
265 | recpy_gap_type.tp_doc = "GDB recorded gap object"; | |
266 | recpy_gap_type.tp_getset = recpy_gap_getset; | |
267 | ||
268 | if (PyType_Ready (&recpy_record_type) < 0 | |
269 | || PyType_Ready (&recpy_gap_type) < 0) | |
270 | return -1; | |
271 | else | |
272 | return 0; | |
4726b2d8 TW |
273 | } |
274 | ||
275 | /* Implementation of gdb.start_recording (method) -> gdb.Record. */ | |
276 | ||
277 | PyObject * | |
278 | gdbpy_start_recording (PyObject *self, PyObject *args) | |
279 | { | |
280 | const char *method = NULL; | |
281 | const char *format = NULL; | |
282 | PyObject *ret = NULL; | |
283 | ||
284 | if (!PyArg_ParseTuple (args, "|ss", &method, &format)) | |
285 | return NULL; | |
286 | ||
287 | TRY | |
288 | { | |
289 | record_start (method, format, 0); | |
290 | ret = gdbpy_current_recording (self, args); | |
291 | } | |
292 | CATCH (except, RETURN_MASK_ALL) | |
293 | { | |
294 | gdbpy_convert_exception (except); | |
295 | } | |
296 | END_CATCH | |
297 | ||
298 | return ret; | |
299 | } | |
300 | ||
301 | /* Implementation of gdb.current_recording (self) -> gdb.Record. */ | |
302 | ||
303 | PyObject * | |
304 | gdbpy_current_recording (PyObject *self, PyObject *args) | |
305 | { | |
306 | recpy_record_object *ret = NULL; | |
307 | ||
308 | if (find_record_target () == NULL) | |
309 | Py_RETURN_NONE; | |
310 | ||
311 | ret = PyObject_New (recpy_record_object, &recpy_record_type); | |
312 | ret->ptid = inferior_ptid; | |
313 | ret->method = target_record_method (inferior_ptid); | |
314 | ||
315 | return (PyObject *) ret; | |
316 | } | |
317 | ||
318 | /* Implementation of gdb.stop_recording (self) -> None. */ | |
319 | ||
320 | PyObject * | |
321 | gdbpy_stop_recording (PyObject *self, PyObject *args) | |
322 | { | |
323 | PyObject *ret = NULL; | |
324 | ||
325 | TRY | |
326 | { | |
327 | record_stop (0); | |
328 | ret = Py_None; | |
329 | Py_INCREF (Py_None); | |
330 | } | |
331 | CATCH (except, RETURN_MASK_ALL) | |
332 | { | |
333 | gdbpy_convert_exception (except); | |
334 | } | |
335 | END_CATCH | |
336 | ||
337 | return ret; | |
338 | } |