1 /* Python interface to record targets. 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 "py-instruction.h" 21 #include "py-record.h" 22 #include "py-record-btrace.h" 23 #include "py-record-full.h" 24 #include "target.h" 25 #include "gdbthread.h" 26 27 /* Python Record type. */ 28 29 static PyTypeObject recpy_record_type = { 30 PyVarObject_HEAD_INIT (NULL, 0) 31 }; 32 33 /* Python RecordInstruction type. */ 34 35 PyTypeObject recpy_insn_type = { 36 PyVarObject_HEAD_INIT (NULL, 0) 37 }; 38 39 /* Python RecordFunctionSegment type. */ 40 41 PyTypeObject recpy_func_type = { 42 PyVarObject_HEAD_INIT (NULL, 0) 43 }; 44 45 /* Python RecordGap type. */ 46 47 static PyTypeObject recpy_gap_type = { 48 PyVarObject_HEAD_INIT (NULL, 0) 49 }; 50 51 /* Python RecordGap object. */ 52 struct recpy_gap_object 53 { 54 PyObject_HEAD 55 56 /* Reason code. */ 57 int reason_code; 58 59 /* Reason message. */ 60 const char *reason_string; 61 62 /* Element number. */ 63 Py_ssize_t number; 64 }; 65 66 /* Implementation of record.method. */ 67 68 static PyObject * 69 recpy_method (PyObject *self, void* closure) 70 { 71 const recpy_record_object * const obj = (recpy_record_object *) self; 72 73 if (obj->method == RECORD_METHOD_FULL) 74 return recpy_full_method (self, closure); 75 76 if (obj->method == RECORD_METHOD_BTRACE) 77 return recpy_bt_method (self, closure); 78 79 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 80 } 81 82 /* Implementation of record.format. */ 83 84 static PyObject * 85 recpy_format (PyObject *self, void* closure) 86 { 87 const recpy_record_object * const obj = (recpy_record_object *) self; 88 89 if (obj->method == RECORD_METHOD_FULL) 90 return recpy_full_format (self, closure); 91 92 if (obj->method == RECORD_METHOD_BTRACE) 93 return recpy_bt_format (self, closure); 94 95 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 96 } 97 98 /* Implementation of record.goto (instruction) -> None. */ 99 100 static PyObject * 101 recpy_goto (PyObject *self, PyObject *value) 102 { 103 const recpy_record_object * const obj = (recpy_record_object *) self; 104 105 if (obj->method == RECORD_METHOD_BTRACE) 106 return recpy_bt_goto (self, value); 107 108 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 109 } 110 111 /* Implementation of record.replay_position [instruction] */ 112 113 static PyObject * 114 recpy_replay_position (PyObject *self, void *closure) 115 { 116 const recpy_record_object * const obj = (recpy_record_object *) self; 117 118 if (obj->method == RECORD_METHOD_BTRACE) 119 return recpy_bt_replay_position (self, closure); 120 121 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 122 } 123 124 /* Implementation of record.instruction_history [list]. */ 125 126 static PyObject * 127 recpy_instruction_history (PyObject *self, void* closure) 128 { 129 const recpy_record_object * const obj = (recpy_record_object *) self; 130 131 if (obj->method == RECORD_METHOD_BTRACE) 132 return recpy_bt_instruction_history (self, closure); 133 134 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 135 } 136 137 /* Implementation of record.function_call_history [list]. */ 138 139 static PyObject * 140 recpy_function_call_history (PyObject *self, void* closure) 141 { 142 const recpy_record_object * const obj = (recpy_record_object *) self; 143 144 if (obj->method == RECORD_METHOD_BTRACE) 145 return recpy_bt_function_call_history (self, closure); 146 147 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 148 } 149 150 /* Implementation of record.begin [instruction]. */ 151 152 static PyObject * 153 recpy_begin (PyObject *self, void* closure) 154 { 155 const recpy_record_object * const obj = (recpy_record_object *) self; 156 157 if (obj->method == RECORD_METHOD_BTRACE) 158 return recpy_bt_begin (self, closure); 159 160 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 161 } 162 163 /* Implementation of record.end [instruction]. */ 164 165 static PyObject * 166 recpy_end (PyObject *self, void* closure) 167 { 168 const recpy_record_object * const obj = (recpy_record_object *) self; 169 170 if (obj->method == RECORD_METHOD_BTRACE) 171 return recpy_bt_end (self, closure); 172 173 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 174 } 175 176 /* Create a new gdb.RecordInstruction object. */ 177 178 PyObject * 179 recpy_insn_new (thread_info *thread, enum record_method method, 180 Py_ssize_t number) 181 { 182 recpy_element_object * const obj = PyObject_New (recpy_element_object, 183 &recpy_insn_type); 184 185 if (obj == NULL) 186 return NULL; 187 188 obj->thread = thread; 189 obj->method = method; 190 obj->number = number; 191 192 return (PyObject *) obj; 193 } 194 195 /* Implementation of RecordInstruction.sal [gdb.Symtab_and_line]. */ 196 197 static PyObject * 198 recpy_insn_sal (PyObject *self, void *closure) 199 { 200 const recpy_element_object * const obj = (recpy_element_object *) self; 201 202 if (obj->method == RECORD_METHOD_BTRACE) 203 return recpy_bt_insn_sal (self, closure); 204 205 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 206 } 207 208 /* Implementation of RecordInstruction.pc [int]. */ 209 210 static PyObject * 211 recpy_insn_pc (PyObject *self, void *closure) 212 { 213 const recpy_element_object * const obj = (recpy_element_object *) self; 214 215 if (obj->method == RECORD_METHOD_BTRACE) 216 return recpy_bt_insn_pc (self, closure); 217 218 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 219 } 220 221 /* Implementation of RecordInstruction.data [buffer]. */ 222 223 static PyObject * 224 recpy_insn_data (PyObject *self, void *closure) 225 { 226 const recpy_element_object * const obj = (recpy_element_object *) self; 227 228 if (obj->method == RECORD_METHOD_BTRACE) 229 return recpy_bt_insn_data (self, closure); 230 231 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 232 } 233 234 /* Implementation of RecordInstruction.decoded [str]. */ 235 236 static PyObject * 237 recpy_insn_decoded (PyObject *self, void *closure) 238 { 239 const recpy_element_object * const obj = (recpy_element_object *) self; 240 241 if (obj->method == RECORD_METHOD_BTRACE) 242 return recpy_bt_insn_decoded (self, closure); 243 244 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 245 } 246 247 /* Implementation of RecordInstruction.size [int]. */ 248 249 static PyObject * 250 recpy_insn_size (PyObject *self, void *closure) 251 { 252 const recpy_element_object * const obj = (recpy_element_object *) self; 253 254 if (obj->method == RECORD_METHOD_BTRACE) 255 return recpy_bt_insn_size (self, closure); 256 257 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 258 } 259 260 /* Implementation of RecordInstruction.is_speculative [bool]. */ 261 262 static PyObject * 263 recpy_insn_is_speculative (PyObject *self, void *closure) 264 { 265 const recpy_element_object * const obj = (recpy_element_object *) self; 266 267 if (obj->method == RECORD_METHOD_BTRACE) 268 return recpy_bt_insn_is_speculative (self, closure); 269 270 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 271 } 272 273 /* Create a new gdb.RecordFunctionSegment object. */ 274 275 PyObject * 276 recpy_func_new (thread_info *thread, enum record_method method, 277 Py_ssize_t number) 278 { 279 recpy_element_object * const obj = PyObject_New (recpy_element_object, 280 &recpy_func_type); 281 282 if (obj == NULL) 283 return NULL; 284 285 obj->thread = thread; 286 obj->method = method; 287 obj->number = number; 288 289 return (PyObject *) obj; 290 } 291 292 /* Implementation of RecordFunctionSegment.level [int]. */ 293 294 static PyObject * 295 recpy_func_level (PyObject *self, void *closure) 296 { 297 const recpy_element_object * const obj = (recpy_element_object *) self; 298 299 if (obj->method == RECORD_METHOD_BTRACE) 300 return recpy_bt_func_level (self, closure); 301 302 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 303 } 304 305 /* Implementation of RecordFunctionSegment.symbol [gdb.Symbol]. */ 306 307 static PyObject * 308 recpy_func_symbol (PyObject *self, void *closure) 309 { 310 const recpy_element_object * const obj = (recpy_element_object *) self; 311 312 if (obj->method == RECORD_METHOD_BTRACE) 313 return recpy_bt_func_symbol (self, closure); 314 315 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 316 } 317 318 /* Implementation of RecordFunctionSegment.instructions [list]. */ 319 320 static PyObject * 321 recpy_func_instructions (PyObject *self, void *closure) 322 { 323 const recpy_element_object * const obj = (recpy_element_object *) self; 324 325 if (obj->method == RECORD_METHOD_BTRACE) 326 return recpy_bt_func_instructions (self, closure); 327 328 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 329 } 330 331 /* Implementation of RecordFunctionSegment.up [RecordFunctionSegment]. */ 332 333 static PyObject * 334 recpy_func_up (PyObject *self, void *closure) 335 { 336 const recpy_element_object * const obj = (recpy_element_object *) self; 337 338 if (obj->method == RECORD_METHOD_BTRACE) 339 return recpy_bt_func_up (self, closure); 340 341 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 342 } 343 344 /* Implementation of RecordFunctionSegment.prev [RecordFunctionSegment]. */ 345 346 static PyObject * 347 recpy_func_prev (PyObject *self, void *closure) 348 { 349 const recpy_element_object * const obj = (recpy_element_object *) self; 350 351 if (obj->method == RECORD_METHOD_BTRACE) 352 return recpy_bt_func_prev (self, closure); 353 354 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 355 } 356 357 /* Implementation of RecordFunctionSegment.next [RecordFunctionSegment]. */ 358 359 static PyObject * 360 recpy_func_next (PyObject *self, void *closure) 361 { 362 const recpy_element_object * const obj = (recpy_element_object *) self; 363 364 if (obj->method == RECORD_METHOD_BTRACE) 365 return recpy_bt_func_next (self, closure); 366 367 return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); 368 } 369 370 /* Implementation of RecordInstruction.number [int] and 371 RecordFunctionSegment.number [int]. */ 372 373 static PyObject * 374 recpy_element_number (PyObject *self, void* closure) 375 { 376 const recpy_element_object * const obj = (recpy_element_object *) self; 377 378 return gdb_py_object_from_longest (obj->number).release (); 379 } 380 381 /* Implementation of RecordInstruction.__hash__ [int] and 382 RecordFunctionSegment.__hash__ [int]. */ 383 384 static Py_hash_t 385 recpy_element_hash (PyObject *self) 386 { 387 const recpy_element_object * const obj = (recpy_element_object *) self; 388 389 return obj->number; 390 } 391 392 /* Implementation of operator == and != of RecordInstruction and 393 RecordFunctionSegment. */ 394 395 static PyObject * 396 recpy_element_richcompare (PyObject *self, PyObject *other, int op) 397 { 398 const recpy_element_object * const obj1 = (recpy_element_object *) self; 399 const recpy_element_object * const obj2 = (recpy_element_object *) other; 400 401 if (Py_TYPE (self) != Py_TYPE (other)) 402 { 403 Py_INCREF (Py_NotImplemented); 404 return Py_NotImplemented; 405 } 406 407 switch (op) 408 { 409 case Py_EQ: 410 if (obj1->thread == obj2->thread 411 && obj1->method == obj2->method 412 && obj1->number == obj2->number) 413 Py_RETURN_TRUE; 414 else 415 Py_RETURN_FALSE; 416 417 case Py_NE: 418 if (obj1->thread != obj2->thread 419 || obj1->method != obj2->method 420 || obj1->number != obj2->number) 421 Py_RETURN_TRUE; 422 else 423 Py_RETURN_FALSE; 424 425 default: 426 break; 427 } 428 429 Py_INCREF (Py_NotImplemented); 430 return Py_NotImplemented; 431 } 432 433 /* Create a new gdb.RecordGap object. */ 434 435 PyObject * 436 recpy_gap_new (int reason_code, const char *reason_string, Py_ssize_t number) 437 { 438 recpy_gap_object * const obj = PyObject_New (recpy_gap_object, 439 &recpy_gap_type); 440 441 if (obj == NULL) 442 return NULL; 443 444 obj->reason_code = reason_code; 445 obj->reason_string = reason_string; 446 obj->number = number; 447 448 return (PyObject *) obj; 449 } 450 451 /* Implementation of RecordGap.number [int]. */ 452 453 static PyObject * 454 recpy_gap_number (PyObject *self, void *closure) 455 { 456 const recpy_gap_object * const obj = (const recpy_gap_object *) self; 457 458 return gdb_py_object_from_longest (obj->number).release (); 459 } 460 461 /* Implementation of RecordGap.error_code [int]. */ 462 463 static PyObject * 464 recpy_gap_reason_code (PyObject *self, void *closure) 465 { 466 const recpy_gap_object * const obj = (const recpy_gap_object *) self; 467 468 return gdb_py_object_from_longest (obj->reason_code).release (); 469 } 470 471 /* Implementation of RecordGap.error_string [str]. */ 472 473 static PyObject * 474 recpy_gap_reason_string (PyObject *self, void *closure) 475 { 476 const recpy_gap_object * const obj = (const recpy_gap_object *) self; 477 478 return PyUnicode_FromString (obj->reason_string); 479 } 480 481 /* Record method list. */ 482 483 static PyMethodDef recpy_record_methods[] = { 484 { "goto", recpy_goto, METH_VARARGS, 485 "goto (instruction|function_call) -> None.\n\ 486 Rewind to given location."}, 487 { NULL } 488 }; 489 490 /* Record member list. */ 491 492 static gdb_PyGetSetDef recpy_record_getset[] = { 493 { "method", recpy_method, NULL, "Current recording method.", NULL }, 494 { "format", recpy_format, NULL, "Current recording format.", NULL }, 495 { "replay_position", recpy_replay_position, NULL, "Current replay position.", 496 NULL }, 497 { "instruction_history", recpy_instruction_history, NULL, 498 "List of instructions in current recording.", NULL }, 499 { "function_call_history", recpy_function_call_history, NULL, 500 "List of function calls in current recording.", NULL }, 501 { "begin", recpy_begin, NULL, 502 "First instruction in current recording.", NULL }, 503 { "end", recpy_end, NULL, 504 "One past the last instruction in current recording. This is typically \ 505 the current instruction and is used for e.g. record.goto (record.end).", NULL }, 506 { NULL } 507 }; 508 509 /* RecordInstruction member list. */ 510 511 static gdb_PyGetSetDef recpy_insn_getset[] = { 512 { "number", recpy_element_number, NULL, "instruction number", NULL}, 513 { "sal", recpy_insn_sal, NULL, "associated symbol and line", NULL}, 514 { "pc", recpy_insn_pc, NULL, "instruction address", NULL}, 515 { "data", recpy_insn_data, NULL, "raw instruction data", NULL}, 516 { "decoded", recpy_insn_decoded, NULL, "decoded instruction", NULL}, 517 { "size", recpy_insn_size, NULL, "instruction size in byte", NULL}, 518 { "is_speculative", recpy_insn_is_speculative, NULL, "if the instruction was \ 519 executed speculatively", NULL}, 520 { NULL } 521 }; 522 523 /* RecordFunctionSegment member list. */ 524 525 static gdb_PyGetSetDef recpy_func_getset[] = { 526 { "number", recpy_element_number, NULL, "function segment number", NULL}, 527 { "level", recpy_func_level, NULL, "call stack level", NULL}, 528 { "symbol", recpy_func_symbol, NULL, "associated line and symbol", NULL}, 529 { "instructions", recpy_func_instructions, NULL, "list of instructions in \ 530 this function segment", NULL}, 531 { "up", recpy_func_up, NULL, "caller or returned-to function segment", NULL}, 532 { "prev", recpy_func_prev, NULL, "previous segment of this function", NULL}, 533 { "next", recpy_func_next, NULL, "next segment of this function", NULL}, 534 { NULL } 535 }; 536 537 /* RecordGap member list. */ 538 539 static gdb_PyGetSetDef recpy_gap_getset[] = { 540 { "number", recpy_gap_number, NULL, "element number", NULL}, 541 { "reason_code", recpy_gap_reason_code, NULL, "reason code", NULL}, 542 { "reason_string", recpy_gap_reason_string, NULL, "reason string", NULL}, 543 { NULL } 544 }; 545 546 /* Sets up the record API in the gdb module. */ 547 548 static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION 549 gdbpy_initialize_record (void) 550 { 551 recpy_record_type.tp_new = PyType_GenericNew; 552 recpy_record_type.tp_flags = Py_TPFLAGS_DEFAULT; 553 recpy_record_type.tp_basicsize = sizeof (recpy_record_object); 554 recpy_record_type.tp_name = "gdb.Record"; 555 recpy_record_type.tp_doc = "GDB record object"; 556 recpy_record_type.tp_methods = recpy_record_methods; 557 recpy_record_type.tp_getset = recpy_record_getset; 558 559 recpy_insn_type.tp_new = PyType_GenericNew; 560 recpy_insn_type.tp_flags = Py_TPFLAGS_DEFAULT; 561 recpy_insn_type.tp_basicsize = sizeof (recpy_element_object); 562 recpy_insn_type.tp_name = "gdb.RecordInstruction"; 563 recpy_insn_type.tp_doc = "GDB recorded instruction object"; 564 recpy_insn_type.tp_getset = recpy_insn_getset; 565 recpy_insn_type.tp_richcompare = recpy_element_richcompare; 566 recpy_insn_type.tp_hash = recpy_element_hash; 567 recpy_insn_type.tp_base = py_insn_get_insn_type (); 568 569 recpy_func_type.tp_new = PyType_GenericNew; 570 recpy_func_type.tp_flags = Py_TPFLAGS_DEFAULT; 571 recpy_func_type.tp_basicsize = sizeof (recpy_element_object); 572 recpy_func_type.tp_name = "gdb.RecordFunctionSegment"; 573 recpy_func_type.tp_doc = "GDB record function segment object"; 574 recpy_func_type.tp_getset = recpy_func_getset; 575 recpy_func_type.tp_richcompare = recpy_element_richcompare; 576 recpy_func_type.tp_hash = recpy_element_hash; 577 578 recpy_gap_type.tp_new = PyType_GenericNew; 579 recpy_gap_type.tp_flags = Py_TPFLAGS_DEFAULT; 580 recpy_gap_type.tp_basicsize = sizeof (recpy_gap_object); 581 recpy_gap_type.tp_name = "gdb.RecordGap"; 582 recpy_gap_type.tp_doc = "GDB recorded gap object"; 583 recpy_gap_type.tp_getset = recpy_gap_getset; 584 585 if (PyType_Ready (&recpy_record_type) < 0 586 || PyType_Ready (&recpy_insn_type) < 0 587 || PyType_Ready (&recpy_func_type) < 0 588 || PyType_Ready (&recpy_gap_type) < 0) 589 return -1; 590 else 591 return 0; 592 } 593 594 /* Implementation of gdb.start_recording (method) -> gdb.Record. */ 595 596 PyObject * 597 gdbpy_start_recording (PyObject *self, PyObject *args) 598 { 599 const char *method = NULL; 600 const char *format = NULL; 601 PyObject *ret = NULL; 602 603 if (!PyArg_ParseTuple (args, "|ss", &method, &format)) 604 return NULL; 605 606 try 607 { 608 record_start (method, format, 0); 609 ret = gdbpy_current_recording (self, args); 610 } 611 catch (const gdb_exception &except) 612 { 613 gdbpy_convert_exception (except); 614 } 615 616 return ret; 617 } 618 619 /* Implementation of gdb.current_recording (self) -> gdb.Record. */ 620 621 PyObject * 622 gdbpy_current_recording (PyObject *self, PyObject *args) 623 { 624 recpy_record_object *ret = NULL; 625 626 if (find_record_target () == NULL) 627 Py_RETURN_NONE; 628 629 ret = PyObject_New (recpy_record_object, &recpy_record_type); 630 ret->thread = inferior_thread (); 631 ret->method = target_record_method (ret->thread->ptid); 632 633 return (PyObject *) ret; 634 } 635 636 /* Implementation of gdb.stop_recording (self) -> None. */ 637 638 PyObject * 639 gdbpy_stop_recording (PyObject *self, PyObject *args) 640 { 641 try 642 { 643 record_stop (0); 644 } 645 catch (const gdb_exception &except) 646 { 647 GDB_PY_HANDLE_EXCEPTION (except); 648 } 649 650 Py_RETURN_NONE; 651 } 652 653 GDBPY_INITIALIZE_FILE (gdbpy_initialize_record); 654