Add gdb.free_objfile event registry

Currently, Python code can use event registries to detect when gdb
loads a new objfile, and when gdb clears the objfile list.  However,
there's no way to detect the removal of an objfile, say when the
inferior calls dlclose.

This patch adds a gdb.free_objfile event registry and arranges for an
event to be emitted in this case.
This commit is contained in:
Tom Tromey
2022-06-20 11:30:04 -06:00
parent 23948f5602
commit 0b4fe76f95
9 changed files with 208 additions and 0 deletions

View File

@ -3494,6 +3494,17 @@ A reference to the object file (@code{gdb.Objfile}) which has been loaded.
@xref{Objfiles In Python}, for details of the @code{gdb.Objfile} object. @xref{Objfiles In Python}, for details of the @code{gdb.Objfile} object.
@end defvar @end defvar
@item events.free_objfile
Emits @code{gdb.FreeObjFileEvent} which indicates that an object file
is about to be removed from @value{GDBN}. One reason this can happen
is when the inferior calls @code{dlclose}.
@code{gdb.FreeObjFileEvent} has one attribute:
@defvar NewObjFileEvent.objfile
A reference to the object file (@code{gdb.Objfile}) which will be unloaded.
@xref{Objfiles In Python}, for details of the @code{gdb.Objfile} object.
@end defvar
@item events.clear_objfiles @item events.clear_objfiles
Emits @code{gdb.ClearObjFilesEvent} which indicates that the list of object Emits @code{gdb.ClearObjFilesEvent} which indicates that the list of object
files for a program space has been reset. files for a program space has been reset.

View File

@ -27,6 +27,7 @@ GDB_PY_DEFINE_EVENT(stop)
GDB_PY_DEFINE_EVENT(cont) GDB_PY_DEFINE_EVENT(cont)
GDB_PY_DEFINE_EVENT(exited) GDB_PY_DEFINE_EVENT(exited)
GDB_PY_DEFINE_EVENT(new_objfile) GDB_PY_DEFINE_EVENT(new_objfile)
GDB_PY_DEFINE_EVENT(free_objfile)
GDB_PY_DEFINE_EVENT(clear_objfiles) GDB_PY_DEFINE_EVENT(clear_objfiles)
GDB_PY_DEFINE_EVENT(new_inferior) GDB_PY_DEFINE_EVENT(new_inferior)
GDB_PY_DEFINE_EVENT(inferior_deleted) GDB_PY_DEFINE_EVENT(inferior_deleted)

View File

@ -86,6 +86,11 @@ GDB_PY_DEFINE_EVENT_TYPE (new_objfile,
"GDB new object file event object", "GDB new object file event object",
event_object_type); event_object_type);
GDB_PY_DEFINE_EVENT_TYPE (free_objfile,
"FreeObjFileEvent",
"GDB free object file event object",
event_object_type);
GDB_PY_DEFINE_EVENT_TYPE (clear_objfiles, GDB_PY_DEFINE_EVENT_TYPE (clear_objfiles,
"ClearObjFilesEvent", "ClearObjFilesEvent",
"GDB clear object files event object", "GDB clear object files event object",

View File

@ -74,6 +74,7 @@ extern gdbpy_ref<> create_thread_event_object (PyTypeObject *py_type,
PyObject *thread); PyObject *thread);
extern int emit_new_objfile_event (struct objfile *objfile); extern int emit_new_objfile_event (struct objfile *objfile);
extern int emit_free_objfile_event (struct objfile *objfile);
extern int emit_clear_objfiles_event (void); extern int emit_clear_objfiles_event (void);
extern void evpy_dealloc (PyObject *self); extern void evpy_dealloc (PyObject *self);

View File

@ -197,6 +197,20 @@ python_new_objfile (struct objfile *objfile)
} }
} }
/* Emit a Python event when an objfile is about to be removed. */
static void
python_free_objfile (struct objfile *objfile)
{
if (!gdb_python_initialized)
return;
gdbpy_enter enter_py (objfile->arch ());
if (emit_free_objfile_event (objfile) < 0)
gdbpy_print_stack ();
}
/* Return a reference to the Python object of type Inferior /* Return a reference to the Python object of type Inferior
representing INFERIOR. If the object has already been created, representing INFERIOR. If the object has already been created,
return it and increment the reference count, otherwise, create it. return it and increment the reference count, otherwise, create it.
@ -853,6 +867,7 @@ gdbpy_initialize_inferior (void)
gdb::observers::new_objfile.attach gdb::observers::new_objfile.attach
(python_new_objfile, "py-inferior", (python_new_objfile, "py-inferior",
{ &auto_load_new_objfile_observer_token }); { &auto_load_new_objfile_observer_token });
gdb::observers::free_objfile.attach (python_free_objfile, "py-inferior");
gdb::observers::inferior_added.attach (python_new_inferior, "py-inferior"); gdb::observers::inferior_added.attach (python_new_inferior, "py-inferior");
gdb::observers::inferior_removed.attach (python_inferior_deleted, gdb::observers::inferior_removed.attach (python_inferior_deleted,
"py-inferior"); "py-inferior");

View File

@ -53,6 +53,42 @@ emit_new_objfile_event (struct objfile *objfile)
return -1; return -1;
} }
/* Create an event object representing a to-be-freed objfile. Return
nullptr, with the Python exception set, on error. */
static gdbpy_ref<>
create_free_objfile_event_object (struct objfile *objfile)
{
gdbpy_ref<> objfile_event
= create_event_object (&free_objfile_event_object_type);
if (objfile_event == nullptr)
return nullptr;
gdbpy_ref<> py_objfile = objfile_to_objfile_object (objfile);
if (py_objfile == nullptr
|| evpy_add_attribute (objfile_event.get (), "objfile",
py_objfile.get ()) < 0)
return nullptr;
return objfile_event;
}
/* Callback function which notifies observers when a free objfile
event occurs. This function will create a new Python event object.
Return -1 if emit fails. */
int
emit_free_objfile_event (struct objfile *objfile)
{
if (evregpy_no_listeners_p (gdb_py_events.free_objfile))
return 0;
gdbpy_ref<> event = create_free_objfile_event_object (objfile);
if (event == nullptr)
return -1;
return evpy_emit_event (event.get (), gdb_py_events.free_objfile);
}
/* Subroutine of emit_clear_objfiles_event to simplify it. */ /* Subroutine of emit_clear_objfiles_event to simplify it. */

View File

@ -0,0 +1,42 @@
/* This testcase is part of GDB, the GNU debugger.
Copyright 2022 Free Software Foundation, Inc.
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 <unistd.h>
#ifdef __WIN32__
#include <windows.h>
#define dlopen(name, mode) LoadLibrary (TEXT (name))
#define dlclose(handle) FreeLibrary (handle)
#else
#include <dlfcn.h>
#endif
/* This is updated by the .exp file. */
char *libname = "py-events-shlib.so";
int
main ()
{
void *h;
h = dlopen (libname, RTLD_LAZY);
dlclose (h);
h = NULL; /* final breakpoint here */
return 0;
}

View File

@ -0,0 +1,67 @@
# Copyright 2022 Free Software Foundation, Inc.
#
# 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/>.
#
# Test the Python free_objfile event.
load_lib gdb-python.exp
if {[skip_shlib_tests]} {
untested "skipping shared library tests"
return -1
}
if {[get_compiler_info]} {
warning "Could not get compiler info"
untested "no compiler info"
return -1
}
standard_testfile .c
if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
executable {debug shlib_load}] != ""} {
untested "failed to compile"
return -1
}
set testfile2 py-events-shlib
set srcfile2 ${testfile2}.c
set binfile2 [standard_output_file ${testfile2}.so]
set binfile2_dlopen [shlib_target_file ${testfile2}.so]
if {[gdb_compile_shlib "${srcdir}/${subdir}/${srcfile2}" \
${binfile2} {debug}] != ""} {
untested "failed to compile shared library"
return -1
}
clean_restart $testfile
if {![runto_main]} {
return
}
if { [skip_python_tests] } { return }
gdb_test_no_output "set var libname = \"$binfile2_dlopen\""
set pyfile [gdb_remote_download host ${srcdir}/${subdir}/py-event-load.py]
gdb_test_no_output "source ${pyfile}" "load python file"
gdb_breakpoint [gdb_get_line_number "final breakpoint here"]
gdb_continue_to_breakpoint "run to final breakpoint"
gdb_test "python print(freed_objfile)" [string_to_regexp $binfile2_dlopen] \
"print name of unloaded objfile"

View File

@ -0,0 +1,30 @@
# Copyright (C) 2022 Free Software Foundation, Inc.
# 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/>.
# Test Python free_objfile event.
import gdb
freed_objfile = None
def free_objfile_handler(event):
assert isinstance(event, gdb.FreeObjFileEvent)
global freed_objfile
freed_objfile = event.objfile.username
gdb.events.free_objfile.connect(free_objfile_handler)