From 0b4fe76f956293778f109764911a0b14dc944f5d Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Mon, 20 Jun 2022 11:30:04 -0600 Subject: [PATCH] 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. --- gdb/doc/python.texi | 11 ++++ gdb/python/py-all-events.def | 1 + gdb/python/py-event-types.def | 5 ++ gdb/python/py-event.h | 1 + gdb/python/py-inferior.c | 15 +++++ gdb/python/py-newobjfileevent.c | 36 ++++++++++++ gdb/testsuite/gdb.python/py-event-load.c | 42 ++++++++++++++ gdb/testsuite/gdb.python/py-event-load.exp | 67 ++++++++++++++++++++++ gdb/testsuite/gdb.python/py-event-load.py | 30 ++++++++++ 9 files changed, 208 insertions(+) create mode 100644 gdb/testsuite/gdb.python/py-event-load.c create mode 100644 gdb/testsuite/gdb.python/py-event-load.exp create mode 100644 gdb/testsuite/gdb.python/py-event-load.py diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 5dd907fac42..eeb847aeaa8 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -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. @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 Emits @code{gdb.ClearObjFilesEvent} which indicates that the list of object files for a program space has been reset. diff --git a/gdb/python/py-all-events.def b/gdb/python/py-all-events.def index 7db8efa1390..e8ae9066875 100644 --- a/gdb/python/py-all-events.def +++ b/gdb/python/py-all-events.def @@ -27,6 +27,7 @@ GDB_PY_DEFINE_EVENT(stop) GDB_PY_DEFINE_EVENT(cont) GDB_PY_DEFINE_EVENT(exited) GDB_PY_DEFINE_EVENT(new_objfile) +GDB_PY_DEFINE_EVENT(free_objfile) GDB_PY_DEFINE_EVENT(clear_objfiles) GDB_PY_DEFINE_EVENT(new_inferior) GDB_PY_DEFINE_EVENT(inferior_deleted) diff --git a/gdb/python/py-event-types.def b/gdb/python/py-event-types.def index 596e68a852a..e613e6949d3 100644 --- a/gdb/python/py-event-types.def +++ b/gdb/python/py-event-types.def @@ -86,6 +86,11 @@ GDB_PY_DEFINE_EVENT_TYPE (new_objfile, "GDB new object file event object", 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, "ClearObjFilesEvent", "GDB clear object files event object", diff --git a/gdb/python/py-event.h b/gdb/python/py-event.h index 831dd10fc85..220af1f7b12 100644 --- a/gdb/python/py-event.h +++ b/gdb/python/py-event.h @@ -74,6 +74,7 @@ extern gdbpy_ref<> create_thread_event_object (PyTypeObject *py_type, PyObject *thread); 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 void evpy_dealloc (PyObject *self); diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c index ebcd5b0a70f..61ed342d1c1 100644 --- a/gdb/python/py-inferior.c +++ b/gdb/python/py-inferior.c @@ -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 representing INFERIOR. If the object has already been created, return it and increment the reference count, otherwise, create it. @@ -853,6 +867,7 @@ gdbpy_initialize_inferior (void) gdb::observers::new_objfile.attach (python_new_objfile, "py-inferior", { &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_removed.attach (python_inferior_deleted, "py-inferior"); diff --git a/gdb/python/py-newobjfileevent.c b/gdb/python/py-newobjfileevent.c index dcbd0faf3a8..929d0eaac73 100644 --- a/gdb/python/py-newobjfileevent.c +++ b/gdb/python/py-newobjfileevent.c @@ -53,6 +53,42 @@ emit_new_objfile_event (struct objfile *objfile) 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. */ diff --git a/gdb/testsuite/gdb.python/py-event-load.c b/gdb/testsuite/gdb.python/py-event-load.c new file mode 100644 index 00000000000..de89c5eff5a --- /dev/null +++ b/gdb/testsuite/gdb.python/py-event-load.c @@ -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 . */ + +#include + +#ifdef __WIN32__ +#include +#define dlopen(name, mode) LoadLibrary (TEXT (name)) +#define dlclose(handle) FreeLibrary (handle) +#else +#include +#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; +} diff --git a/gdb/testsuite/gdb.python/py-event-load.exp b/gdb/testsuite/gdb.python/py-event-load.exp new file mode 100644 index 00000000000..f7aeb5f33fb --- /dev/null +++ b/gdb/testsuite/gdb.python/py-event-load.exp @@ -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 . +# + +# 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" diff --git a/gdb/testsuite/gdb.python/py-event-load.py b/gdb/testsuite/gdb.python/py-event-load.py new file mode 100644 index 00000000000..9cf56115205 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-event-load.py @@ -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 . + +# 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) -- 2.30.2