aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/NEWS5
-rw-r--r--gdb/doc/python.texi33
-rw-r--r--gdb/python/py-all-events.def1
-rw-r--r--gdb/python/py-event-types.def5
-rw-r--r--gdb/python/py-progspace.c54
-rw-r--r--gdb/testsuite/gdb.python/py-exec-file.exp100
6 files changed, 198 insertions, 0 deletions
diff --git a/gdb/NEWS b/gdb/NEWS
index 2d57f52..fd7ab28 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -310,6 +310,11 @@ show tui mouse-events
file name will have been partially resolved to an absolute file
name.
+ ** A new executable_changed event registry is available. This event
+ emits ExecutableChangedEvent objects, which have 'progspace' (a
+ gdb.Progspace) and 'reload' (a Boolean) attributes. This event
+ is emitted when gdb.Progspace.executable_filename changes.
+
*** Changes in GDB 13
* MI version 1 is deprecated, and will be removed in GDB 14.
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 4c8bb12..0471e78 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -3929,6 +3929,39 @@ This is emitted when @value{GDBN} removes a connection
The @code{gdb.TargetConnection} that is being removed.
@end defvar
+@item events.executable_changed
+Emits @code{gdb.ExecutableChangedEvent} which indicates that the
+@code{gdb.Progspace.executable_filename} has changed.
+
+This event is emitted when either the value of
+@code{gdb.Progspace.executable_filename } has changed to name a
+different file, or the executable file named by
+@code{gdb.Progspace.executable_filename} has changed on disk, and
+@value{GDBN} has therefore reloaded it.
+
+@defvar ExecutableChangedEvent.progspace
+The @code{gdb.Progspace} in which the current executable has changed.
+The file name of the updated executable will be visible in
+@code{gdb.Progspace.executable_filename} (@pxref{Progspaces In Python}).
+@end defvar
+@defvar ExecutableChangedEvent.reload
+This attribute will be @code{True} if the value of
+@code{gdb.Progspace.executable_filename} didn't change, but the file
+it names changed on disk instead, and @value{GDBN} reloaded it.
+
+When this attribute is @code{False}, the value in
+@code{gdb.Progspace.executable_filename} was changed to name a
+different file.
+@end defvar
+
+Remember that @value{GDBN} tracks the executable file and the symbol
+file separately, these are visible as
+@code{gdb.Progspace.executable_filename} and
+@code{gdb.Progspace.filename} respectively. When using the @kbd{file}
+command, @value{GDBN} updates both of these fields, but the executable
+file is updated first, so when this event is emitted, the executable
+filename will have changed, but the symbol filename might still hold
+its previous value.
@end table
@node Threads In Python
diff --git a/gdb/python/py-all-events.def b/gdb/python/py-all-events.def
index aa28f2c..04a12e1 100644
--- a/gdb/python/py-all-events.def
+++ b/gdb/python/py-all-events.def
@@ -42,3 +42,4 @@ GDB_PY_DEFINE_EVENT(breakpoint_modified)
GDB_PY_DEFINE_EVENT(before_prompt)
GDB_PY_DEFINE_EVENT(gdb_exiting)
GDB_PY_DEFINE_EVENT(connection_removed)
+GDB_PY_DEFINE_EVENT(executable_changed)
diff --git a/gdb/python/py-event-types.def b/gdb/python/py-event-types.def
index 395d6c0..b862094 100644
--- a/gdb/python/py-event-types.def
+++ b/gdb/python/py-event-types.def
@@ -125,3 +125,8 @@ GDB_PY_DEFINE_EVENT_TYPE (connection,
"ConnectionEvent",
"GDB connection added or removed object",
event_object_type);
+
+GDB_PY_DEFINE_EVENT_TYPE (executable_changed,
+ "ExecutableChangedEvent",
+ "GDB executable changed event",
+ event_object_type);
diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c
index 1319978..082509b 100644
--- a/gdb/python/py-progspace.c
+++ b/gdb/python/py-progspace.c
@@ -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;
diff --git a/gdb/testsuite/gdb.python/py-exec-file.exp b/gdb/testsuite/gdb.python/py-exec-file.exp
index 14e5088..5ad3cd7 100644
--- a/gdb/testsuite/gdb.python/py-exec-file.exp
+++ b/gdb/testsuite/gdb.python/py-exec-file.exp
@@ -35,11 +35,59 @@ if {[build_executable "failed to prepare second executable" \
set binfile1 [gdb_remote_download host $binfile1]
set binfile2 [gdb_remote_download host $binfile2]
+# Setup a Python function to listen for the executable changed event.
+proc setup_exec_change_handler {} {
+ gdb_py_test_silent_cmd \
+ [multi_line \
+ "python" \
+ "def reset_state():" \
+ " global exec_changed_state" \
+ " exec_changed_state = \[0, None, None\]" \
+ "end" ] \
+ "build reset_state function" 0
+
+ gdb_py_test_silent_cmd \
+ [multi_line \
+ "python" \
+ "def executable_changed(event):" \
+ " global exec_changed_state" \
+ " exec_changed_state\[0\] += 1" \
+ " exec_changed_state\[1\] = event.progspace.executable_filename" \
+ " exec_changed_state\[2\] = event.reload" \
+ "end" ] \
+ "build executable_changed function" 0
+
+ gdb_test_no_output -nopass "python reset_state()"
+ gdb_test_no_output "python gdb.events.executable_changed.connect(executable_changed)"
+}
+
+# Check the global Python state that is updated when the
+# executable_changed event occurs, and then reset the global state.
+# FILENAME is a string, the name of the new executable file. RELOAD
+# is a string, which should be 'True' or 'False', and represents if
+# the executable file was reloaded, or changed.
+proc check_exec_change { filename_re reload testname } {
+ if { $filename_re ne "None" } {
+ set filename_re "'$filename_re'"
+ }
+ if { $filename_re eq "None" && $reload eq "None" } {
+ set count 0
+ } else {
+ set count 1
+ }
+ gdb_test "python print(exec_changed_state)" \
+ "\\\[$count, $filename_re, $reload\\\]" \
+ $testname
+ gdb_test_no_output -nopass "python reset_state()"
+}
+
# Check that the executable_filename is set correctly after using the
# 'file' command.
with_test_prefix "using 'file' command" {
clean_restart
+ setup_exec_change_handler
+
gdb_test "python print(gdb.current_progspace().executable_filename)" \
"None" \
"check executable_filename when no file is loaded"
@@ -51,6 +99,9 @@ with_test_prefix "using 'file' command" {
"[string_to_regexp $binfile1]" \
"check executable_filename when first executable is loaded"
+ check_exec_change [string_to_regexp $binfile1] False \
+ "check executable_changed state after first executable was loaded"
+
gdb_test "file $binfile2" \
"Reading symbols from [string_to_regexp $binfile2]\\.\\.\\..*" \
"load second executable" \
@@ -59,42 +110,91 @@ with_test_prefix "using 'file' command" {
"[string_to_regexp $binfile2]" \
"check executable_filename when second executable is loaded"
+ check_exec_change [string_to_regexp $binfile2] False \
+ "check executable_changed state after second executable was loaded"
+
gdb_unload
gdb_test "python print(gdb.current_progspace().executable_filename)" \
"None" \
"check executable_filename after unloading file"
+
+ check_exec_change None False \
+ "check executable_changed state after unloading the executable"
}
# Check that the executable_filename is correctly set when we only set
# the exec-file.
with_test_prefix "using 'exec-file' command" {
clean_restart
+
+ setup_exec_change_handler
+
gdb_test_no_output "exec-file $binfile1" \
"load first executable"
gdb_test "python print(gdb.current_progspace().executable_filename)" \
"[string_to_regexp $binfile1]" \
"check executable_filename when first executable is loaded"
+ check_exec_change [string_to_regexp $binfile1] False \
+ "check executable_changed state after first executable was loaded"
+
gdb_test_no_output "exec-file $binfile2" \
"load second executable"
gdb_test "python print(gdb.current_progspace().executable_filename)" \
"[string_to_regexp $binfile2]" \
"check executable_filename when second executable is loaded"
+ check_exec_change [string_to_regexp $binfile2] False \
+ "check executable_changed state after second executable was loaded"
+
gdb_test "exec-file" "No executable file now\\."
gdb_test "python print(gdb.current_progspace().executable_filename)" \
"None" \
"check executable_filename after unloading file"
+
+ check_exec_change None False \
+ "check executable_changed state after unloading the executable"
}
# Check that setting the symbol-file doesn't cause the
# executable_filename to be set.
with_test_prefix "using 'symbol-file' command" {
clean_restart
+
+ setup_exec_change_handler
+
gdb_test "symbol-file $binfile1" \
"Reading symbols from [string_to_regexp $binfile1]\\.\\.\\..*" \
"load first executable"
gdb_test "python print(gdb.current_progspace().executable_filename)" \
"None" \
"check executable_filename after setting symbol-file"
+
+ check_exec_change None None \
+ "check executable_changed state after setting symbol-file"
+}
+
+# Check the executable_changed event when the executable changes on disk.
+with_test_prefix "exec changes on disk" {
+ clean_restart $binfile1
+
+ setup_exec_change_handler
+
+ runto_main
+
+ gdb_test_no_output "shell sleep 1" \
+ "ensure executable is at least 1 second old"
+
+ gdb_test "shell touch ${binfile1}" "" \
+ "update the executable on disk"
+
+ runto_main
+
+ # There is currently an issue where the executable_changed event
+ # will trigger twice during an inferior restart. This should be
+ # fixed in the next commit, at which point this kfail can be
+ # removed.
+ setup_kfail "????" *-*-*
+ check_exec_change [string_to_regexp $binfile1] True \
+ "check executable_changed state after exec changed on disk"
}