xref: /netbsd-src/external/gpl3/gdb/dist/gdb/python/py-stopevent.c (revision 7b75fb82a29e85f63942bcd36fb838f6ffe46195)
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 (&current_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