From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-io1-xd29.google.com (mail-io1-xd29.google.com [IPv6:2607:f8b0:4864:20::d29]) by sourceware.org (Postfix) with ESMTPS id 512BA3858C2C for ; Mon, 20 Jun 2022 17:36:26 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 512BA3858C2C Received: by mail-io1-xd29.google.com with SMTP id n11so11830784iod.4 for ; Mon, 20 Jun 2022 10:36:26 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=x/5+hKzOrpHYQ8ZVI79HDm5fuu7dmNwgAcq5kWd+PKs=; b=hBFGb0VZw2BeqOSdy3CQAVOar4ioQOldmbvUPngxMXbRfkRm5CnVDIK4vQmJoN5c61 MyBt0wI1MEfNBnyB875VbZYf7H2JzrQvsKbvk/bXabopME7Fk/4pul/gj/ADrnt5FAvd XJjJkKFQFhIL9IKaUnKI8pUVDXjzpZI1UvgY4d216vS+h06SANHoQy/lUPNHiIwEJYOA XV/hxbTV01PX1CCCcDj2GZvhD4TURJOvM827yRQoJD1VZK8H3kp6LUP+TG2tjqLo+XIV 6D5QA6sbm+YUNryKTXX0nJHuMIYXff9bqLaEF2+5iNWO1M3oN1Oqg8Dnl5ENbbRXvIla yQzQ== X-Gm-Message-State: AJIora+fYqaBUIuaO5N0Nr3Y0JJpjLjm69W7H6pknOzC+PKW/t6TwkCD 1WUsOYiB6yuJV6wkJ6Px94HdVFtPo7lRfg== X-Google-Smtp-Source: AGRyM1stDsT90w4J1kRVSxrX2vABupPrHAuKYTEJ60DeuUBm9b/BGFTKlD9GtRrqZh4gcHmhrpSFPA== X-Received: by 2002:a02:8609:0:b0:330:e92c:2cc4 with SMTP id e9-20020a028609000000b00330e92c2cc4mr14263937jai.95.1655746585499; Mon, 20 Jun 2022 10:36:25 -0700 (PDT) Received: from murgatroyd.Home (71-211-187-180.hlrn.qwest.net. [71.211.187.180]) by smtp.gmail.com with ESMTPSA id x5-20020a056638010500b00339c2c0d123sm427095jao.116.2022.06.20.10.36.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 Jun 2022 10:36:24 -0700 (PDT) From: Tom Tromey To: gdb-patches@sourceware.org Cc: Tom Tromey Subject: [PATCH] Add gdb.free_objfile event registry Date: Mon, 20 Jun 2022 11:36:22 -0600 Message-Id: <20220620173622.2711270-1-tromey@adacore.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-11.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 20 Jun 2022 17:36:28 -0000 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 9e2e97b1e74..7d2c6e323a1 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -3470,6 +3470,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..515b4b0be08 --- /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 2010-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 = "catch-load-so.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.34.1