gdb/python: make the executable_changed event available from Python

This commit makes the executable_changed observable available through
the Python API as an event.  There's nothing particularly interesting
going on here, it just follows the same pattern as many of the other
Python events we support.

The new event registry is called events.executable_changed, and this
emits an ExecutableChangedEvent object which has two attributes, a
gdb.Progspace called 'progspace', this is the program space in which
the executable changed, and a Boolean called 'reload', which is True
if the same executable changed on disk and has been reloaded, or is
False when a new executable has been loaded.

One interesting thing did come up during testing though, you'll notice
the test contains a setup_kfail call.  During testing I observed that
the executable_changed event would trigger twice when GDB restarted an
inferior.  However, the ExecutableChangedEvent object is identical for
both calls, so the wrong information is never sent out, we just see
one too many events.

I tracked this down to how the reload_symbols function (symfile.c)
takes care to also reload the executable, however, I've split fixing
this into a separate commit, so see the next commit for details.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Approved-By: Tom Tromey <tom@tromey.com>
This commit is contained in:
Andrew Burgess
2023-08-28 16:22:36 +01:00
parent 063453b199
commit 42f297ad36
6 changed files with 198 additions and 0 deletions

View File

@ -26,6 +26,8 @@
#include "arch-utils.h"
#include "solib.h"
#include "block.h"
#include "py-event.h"
#include "observable.h"
struct pspace_object
{
@ -592,9 +594,61 @@ gdbpy_is_progspace (PyObject *obj)
return PyObject_TypeCheck (obj, &pspace_object_type);
}
/* Emit an ExecutableChangedEvent event to REGISTRY. Return 0 on success,
or a negative value on error. PSPACE is the program_space in which the
current executable has changed, and RELOAD_P is true if the executable
path stayed the same, but the file on disk changed, or false if the
executable path actually changed. */
static int
emit_executable_changed_event (eventregistry_object *registry,
struct program_space *pspace, bool reload_p)
{
gdbpy_ref<> event_obj
= create_event_object (&executable_changed_event_object_type);
if (event_obj == nullptr)
return -1;
gdbpy_ref<> py_pspace = pspace_to_pspace_object (pspace);
if (py_pspace == nullptr
|| evpy_add_attribute (event_obj.get (), "progspace",
py_pspace.get ()) < 0)
return -1;
gdbpy_ref<> py_reload_p (PyBool_FromLong (reload_p ? 1 : 0));
if (py_reload_p == nullptr
|| evpy_add_attribute (event_obj.get (), "reload",
py_reload_p.get ()) < 0)
return -1;
return evpy_emit_event (event_obj.get (), registry);
}
/* Listener for the executable_changed observable, this is called when the
current executable within PSPACE changes. RELOAD_P is true if the
executable path stayed the same but the file changed on disk. RELOAD_P
is false if the executable path was changed. */
static void
gdbpy_executable_changed (struct program_space *pspace, bool reload_p)
{
if (!gdb_python_initialized)
return;
gdbpy_enter enter_py;
if (!evregpy_no_listeners_p (gdb_py_events.executable_changed))
if (emit_executable_changed_event (gdb_py_events.executable_changed,
pspace, reload_p) < 0)
gdbpy_print_stack ();
}
static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
gdbpy_initialize_pspace (void)
{
gdb::observers::executable_changed.attach (gdbpy_executable_changed,
"py-progspace");
if (PyType_Ready (&pspace_object_type) < 0)
return -1;