blob: be26bc17f859060d37bd6c3affcb34ff528dea36 [file] [log] [blame]
/* Python interface to inferior stop events.
Copyright (C) 2009-2024 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "py-stopevent.h"
#include "py-uiout.h"
#include "thread-fsm.h"
gdbpy_ref<>
create_stop_event_object (PyTypeObject *py_type, const gdbpy_ref<> &dict)
{
gdbpy_ref<> thread = py_get_event_thread (inferior_ptid);
if (thread == nullptr)
return nullptr;
gdbpy_ref<> result = create_thread_event_object (py_type, thread.get ());
if (result == nullptr)
return nullptr;
if (evpy_add_attribute (result.get (), "details", dict.get ()) < 0)
return nullptr;
return result;
}
/* Print BPSTAT to a new Python dictionary. Returns the dictionary,
or null if a Python exception occurred. */
static gdbpy_ref<>
py_print_bpstat (bpstat *bs, enum gdb_signal stop_signal)
{
py_ui_out uiout;
struct value *return_value = nullptr;
try
{
scoped_restore save_uiout = make_scoped_restore (&current_uiout, &uiout);
thread_info *tp = inferior_thread ();
if (tp->thread_fsm () != nullptr && tp->thread_fsm ()->finished_p ())
{
async_reply_reason reason = tp->thread_fsm ()->async_reply_reason ();
uiout.field_string ("reason", async_reason_lookup (reason));
return_value_info *rvinfo = tp->thread_fsm ()->return_value ();
if (rvinfo != nullptr && rvinfo->value != nullptr)
return_value = rvinfo->value;
}
if (stop_signal != GDB_SIGNAL_0 && stop_signal != GDB_SIGNAL_TRAP)
print_signal_received_reason (&uiout, stop_signal);
else
{
struct target_waitstatus last;
get_last_target_status (nullptr, nullptr, &last);
bpstat_print (bs, last.kind ());
}
}
catch (const gdb_exception &except)
{
gdbpy_convert_exception (except);
return nullptr;
}
gdbpy_ref<> dict = uiout.result ();
if (dict == nullptr)
return nullptr;
/* This has to be done separately to avoid error issues, and because
there's no API to add generic Python objects to a py_ui_out. */
if (return_value != nullptr)
{
gdbpy_ref<> val (value_to_value_object (return_value));
if (val == nullptr)
return nullptr;
if (PyDict_SetItemString (dict.get (), "finish-value", val.get ()) < 0)
return nullptr;
}
return dict;
}
/* Callback observers when a stop event occurs. This function will create a
new Python stop event object. If only a specific thread is stopped the
thread object of the event will be set to that thread. Otherwise, if all
threads are stopped thread object will be set to None.
return 0 if the event was created and emitted successfully otherwise
returns -1. */
int
emit_stop_event (struct bpstat *bs, enum gdb_signal stop_signal)
{
gdbpy_ref<> stop_event_obj;
gdbpy_ref<> list;
PyObject *first_bp = NULL;
struct bpstat *current_bs;
if (evregpy_no_listeners_p (gdb_py_events.stop))
return 0;
gdbpy_ref<> dict = py_print_bpstat (bs, stop_signal);
if (dict == nullptr)
return -1;
/* Add any breakpoint set at this location to the list. */
for (current_bs = bs; current_bs != NULL; current_bs = current_bs->next)
{
if (current_bs->breakpoint_at
&& current_bs->breakpoint_at->py_bp_object)
{
PyObject *current_py_bp =
(PyObject *) current_bs->breakpoint_at->py_bp_object;
if (list == NULL)
{
list.reset (PyList_New (0));
if (list == NULL)
return -1;
}
if (PyList_Append (list.get (), current_py_bp))
return -1;
if (first_bp == NULL)
first_bp = current_py_bp;
}
}
if (list != NULL)
{
stop_event_obj = create_breakpoint_event_object (dict,
list.get (),
first_bp);
if (stop_event_obj == NULL)
return -1;
}
/* Check if the signal is "Signal 0" or "Trace/breakpoint trap". */
if (stop_signal != GDB_SIGNAL_0
&& stop_signal != GDB_SIGNAL_TRAP)
{
stop_event_obj = create_signal_event_object (dict, stop_signal);
if (stop_event_obj == NULL)
return -1;
}
/* If all fails emit an unknown stop event. All event types should
be known and this should eventually be unused. */
if (stop_event_obj == NULL)
{
stop_event_obj = create_stop_event_object (&stop_event_object_type,
dict);
if (stop_event_obj == NULL)
return -1;
}
return evpy_emit_event (stop_event_obj.get (), gdb_py_events.stop);
}