1 /* Python interface to inferior stop events. 2 3 Copyright (C) 2009-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-stopevent.h" 21 #include "py-uiout.h" 22 #include "thread-fsm.h" 23 24 gdbpy_ref<> 25 create_stop_event_object (PyTypeObject *py_type, const gdbpy_ref<> &dict) 26 { 27 gdbpy_ref<> thread = py_get_event_thread (inferior_ptid); 28 if (thread == nullptr) 29 return nullptr; 30 31 gdbpy_ref<> result = create_thread_event_object (py_type, thread.get ()); 32 if (result == nullptr) 33 return nullptr; 34 35 if (evpy_add_attribute (result.get (), "details", dict.get ()) < 0) 36 return nullptr; 37 38 return result; 39 } 40 41 /* Print BPSTAT to a new Python dictionary. Returns the dictionary, 42 or null if a Python exception occurred. */ 43 44 static gdbpy_ref<> 45 py_print_bpstat (bpstat *bs, enum gdb_signal stop_signal) 46 { 47 py_ui_out uiout; 48 struct value *return_value = nullptr; 49 50 try 51 { 52 scoped_restore save_uiout = make_scoped_restore (¤t_uiout, &uiout); 53 54 thread_info *tp = inferior_thread (); 55 if (tp->thread_fsm () != nullptr && tp->thread_fsm ()->finished_p ()) 56 { 57 async_reply_reason reason = tp->thread_fsm ()->async_reply_reason (); 58 uiout.field_string ("reason", async_reason_lookup (reason)); 59 60 return_value_info *rvinfo = tp->thread_fsm ()->return_value (); 61 if (rvinfo != nullptr && rvinfo->value != nullptr) 62 return_value = rvinfo->value; 63 } 64 65 if (stop_signal != GDB_SIGNAL_0 && stop_signal != GDB_SIGNAL_TRAP) 66 print_signal_received_reason (&uiout, stop_signal); 67 else 68 { 69 struct target_waitstatus last; 70 get_last_target_status (nullptr, nullptr, &last); 71 72 bpstat_print (bs, last.kind ()); 73 } 74 } 75 catch (const gdb_exception &except) 76 { 77 gdbpy_convert_exception (except); 78 return nullptr; 79 } 80 81 gdbpy_ref<> dict = uiout.result (); 82 if (dict == nullptr) 83 return nullptr; 84 85 /* This has to be done separately to avoid error issues, and because 86 there's no API to add generic Python objects to a py_ui_out. */ 87 if (return_value != nullptr) 88 { 89 gdbpy_ref<> val (value_to_value_object (return_value)); 90 if (val == nullptr) 91 return nullptr; 92 if (PyDict_SetItemString (dict.get (), "finish-value", val.get ()) < 0) 93 return nullptr; 94 } 95 96 return dict; 97 } 98 99 /* Callback observers when a stop event occurs. This function will create a 100 new Python stop event object. If only a specific thread is stopped the 101 thread object of the event will be set to that thread. Otherwise, if all 102 threads are stopped thread object will be set to None. 103 return 0 if the event was created and emitted successfully otherwise 104 returns -1. */ 105 106 int 107 emit_stop_event (struct bpstat *bs, enum gdb_signal stop_signal) 108 { 109 gdbpy_ref<> stop_event_obj; 110 gdbpy_ref<> list; 111 PyObject *first_bp = NULL; 112 struct bpstat *current_bs; 113 114 if (evregpy_no_listeners_p (gdb_py_events.stop)) 115 return 0; 116 117 gdbpy_ref<> dict = py_print_bpstat (bs, stop_signal); 118 if (dict == nullptr) 119 return -1; 120 121 /* Add any breakpoint set at this location to the list. */ 122 for (current_bs = bs; current_bs != NULL; current_bs = current_bs->next) 123 { 124 if (current_bs->breakpoint_at 125 && current_bs->breakpoint_at->py_bp_object) 126 { 127 PyObject *current_py_bp = 128 (PyObject *) current_bs->breakpoint_at->py_bp_object; 129 130 if (list == NULL) 131 { 132 list.reset (PyList_New (0)); 133 if (list == NULL) 134 return -1; 135 } 136 137 if (PyList_Append (list.get (), current_py_bp)) 138 return -1; 139 140 if (first_bp == NULL) 141 first_bp = current_py_bp; 142 } 143 } 144 145 if (list != NULL) 146 { 147 stop_event_obj = create_breakpoint_event_object (dict, 148 list.get (), 149 first_bp); 150 if (stop_event_obj == NULL) 151 return -1; 152 } 153 154 /* Check if the signal is "Signal 0" or "Trace/breakpoint trap". */ 155 if (stop_signal != GDB_SIGNAL_0 156 && stop_signal != GDB_SIGNAL_TRAP) 157 { 158 stop_event_obj = create_signal_event_object (dict, stop_signal); 159 if (stop_event_obj == NULL) 160 return -1; 161 } 162 163 /* If all fails emit an unknown stop event. All event types should 164 be known and this should eventually be unused. */ 165 if (stop_event_obj == NULL) 166 { 167 stop_event_obj = create_stop_event_object (&stop_event_object_type, 168 dict); 169 if (stop_event_obj == NULL) 170 return -1; 171 } 172 173 return evpy_emit_event (stop_event_obj.get (), gdb_py_events.stop); 174 } 175