mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2026-08-27 00:26:02 -04:00
While working on the commit:
commit 9959019545d8d6d71d927f20f088efba944b1e9c
Date: Sun Sep 28 16:16:53 2025 +0100
gdb: fix for 'set suppress-cli-notifications on' missed case
I spotted this message in the gdb.mi/user-selected-context-sync.exp
test script:
# Idea for the future: selecting a thread in a different inferior. For now,
# GDB doesn't show an inferior switch, but if it did, it would be a nice
# place to test it.
What this message is talking about is this behaviour:
(gdb) info threads
Id Target Id Frame
1.1 Thread 0xf7dbc700 (LWP 818430) "thr" 0xf7eb2888 in clone () from /lib/libc.so.6
1.2 Thread 0xf7dbbb40 (LWP 818433) "thr" 0xf7fd0579 in __kernel_vsyscall ()
1.3 Thread 0xf73ffb40 (LWP 818434) "thr" breakpt () at thr.c:19
2.1 Thread 0xf7dbc700 (LWP 818456) "thr" 0xf7eb2888 in clone () from /lib/libc.so.6
2.2 Thread 0xf7dbbb40 (LWP 818457) "thr" breakpt () at thr.c:19
* 2.3 Thread 0xf73ffb40 (LWP 818458) "thr" breakpt () at thr.c:19
(gdb) inferior 1
[Switching to inferior 1 [process 818430] (/home/andrew/tmp/thr)]
[Switching to thread 1.1 (Thread 0xf7dbc700 (LWP 818430))]
#0 0xf7eb2888 in clone () from /lib/libc.so.6
(gdb) thread 2.2
[Switching to thread 2.2 (Thread 0xf7dbbb40 (LWP 818457))]
#0 breakpt () at thr.c:19
19 while (stop)
(gdb)
Notice that when we switch from thread 2.3 to 1.1 using the 'inferior
1' command, GDB tells us that the inferior has changed, and that the
thread has changed (and also that the frame has changed).
But, when we switch from 1.1 to 2.2 using the 'thread 2.2' command, we
are only told about the thread change.
The 'Switching to inferior ...' line includes some useful information,
the process PID and the executable name, and I think it is a shame
that these are not presented when using the 'thread' command to switch
inferior.
So, this commit addresses this issue.
A question that came up during review, and which I'm clarifying here:
this change only affects the output of GDB when the thread command is
also used to switch inferiors. I am (in effect) arguing that the
command 'thread 2.2' should be treated as a shorthand for 'inferior 2;
thread 2', and should display all of the associated output. If the
user is only switching threads within a single inferior then it is not
necessary to re-display the inferior information.
I acknowledge that this does mean the output of the 'thread' command
will now be different depending on whether the user changes inferior
or not. However, I think this is better than the alternative, having
the 'thread' command always re-print the inferior information. I
think this would introduce excess noise that is not useful.
There are changes in basically two areas. The easy part is in
thread_command (thread.c). Here we spot when the inferior has changed
as a result of the 'thread' command, and include
USER_SELECTED_INFERIOR in the set of state passed to the
notify_user_selected_context_changed function.
The change in mi/mi-main.c is a little more involved. In the
mi_cmd_execute function we use an instance of user_selected_context to
spot if any inferior state (frame, thread, or inferior) changes after
an MI command, this is then used to decide if there should be a call
to interps_notify_user_selected_context_changed.
First, by calling interps_notify_user_selected_context_changed
directly, instead of notify_user_selected_context_changed, we fail to
trigger the Python selected_context event, which feels like a
mistake. If the context is changed via an MI command, I think we
should still trigger the Python event. So the first thing I did was
change the interps_notify_user_selected_context_changed call into a
call to notify_user_selected_context_changed. I updated the
gdb.python/py-selected-context.exp test to cover this case.
After that, in mi_cmd_execute, notify_user_selected_context_changed is
always passed 'USER_SELECTED_THREAD | USER_SELECTED_FRAME'. This
makes sense, the MI doesn't allow "switching inferiors" as a command,
instead, an MI frontend must switch threads, and the inferior is
switched as a consequence. But this does mean that if a user has a
CLI and MI interpreter running, and the MI switches threads, the CLI
will only receive the thread switch style notifications, that is,
there will be no "Switching to inferior ..." line.
What I've done is rename user_selected_context::has_changed to
user_selected_context::what_changed, this function is now responsible
for returning the set of USER_SELECTED_* flags that indicate what
changed.
If anything has changed then we always return USER_SELECTED_THREAD |
USER_SELECTED_FRAME as a minimum. This retains the existing
behaviour, but is possibly more aggressive than we need to be; the
-stack-select-frame command can only change the frame, so maybe in
this case we should only return USER_SELECTED_FRAME? I've left that
for the future though.
However, the important change is that in ::what_changed, I now spot
when the inferior has changed and include USER_SELECTED_INFERIOR in
the set of flags that are returned.
In mi_cmd_execute we now call the new what_changed function, and use
the set of flags returned when calling
notify_user_selected_context_changed. This means that the CLI will
now receive inferior changed notifications when appropriate.
The gdb.mi/user-selected-context-sync.exp script has been updated,
replacing the comment I quoted above with an actual test that the
inferior change is announced correctly.
Reviewed-By: Guinevere Larsen <guinevere@redhat.com>
Reviewed-By: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
150 lines
5.3 KiB
Text
150 lines
5.3 KiB
Text
# Copyright (C) 2026 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/>.
|
|
|
|
# Check the Python gdb.selected_context event handling.
|
|
|
|
require allow_python_tests
|
|
|
|
load_lib gdb-python.exp
|
|
|
|
standard_testfile
|
|
|
|
if { [build_executable "build exec" $testfile $srcfile {debug pthreads}] } {
|
|
return
|
|
}
|
|
|
|
clean_restart
|
|
|
|
# Source the Python script.
|
|
set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
|
|
gdb_test "source ${pyfile}" "^DONE" "load python file"
|
|
gdb_test "test-selected-context-event" \
|
|
"^GDB selected-context event registered\\."
|
|
|
|
# Return a regexp for when the selected context event triggers, and
|
|
# runs without error.
|
|
proc event_regexp { inferior {thread "None"} {frame "None"}} {
|
|
return [multi_line \
|
|
" Inferior: ${inferior}" \
|
|
" Thread: [string_to_regexp $thread]" \
|
|
" Frame: [string_to_regexp $frame]"]
|
|
}
|
|
|
|
# Use 'info inferiors' to check that INF is the currently selected
|
|
# inferior. INF should be an inferior number, e.g. '1', '2', etc.
|
|
proc check_inferior { inf testname } {
|
|
gdb_test "info inferiors" \
|
|
"\r\n\\*\\s+[string_to_regexp $inf]\\s+\[^\r\n\]*(?=\r\n)" \
|
|
$testname
|
|
}
|
|
|
|
# Use 'info threads' to check that THR is the currently selected
|
|
# thread. THR should be the thread-id (e.g. '1.1', '2.1') as appears
|
|
# in the 'info threads' output.
|
|
proc check_thread { thr testname } {
|
|
gdb_test "info threads" \
|
|
"\r\n\\*\\s+[string_to_regexp $thr]\\s+\[^\r\n\]+(?=\r\n).*" \
|
|
$testname
|
|
}
|
|
|
|
# Create a second inferior.
|
|
gdb_test "add-inferior" "Added inferior 2\[^\r\n\]*"
|
|
|
|
# Switch between inferiors before either inferior is started. The
|
|
# event will include a valid gdb.Inferior, but the thread and frame
|
|
# will both be None.
|
|
gdb_test "inferior 2" [event_regexp 2] \
|
|
"switch to inferior 2, inferior is not started"
|
|
gdb_test "inferior 1" [event_regexp 1] \
|
|
"switch to inferior 1, inferior is not started"
|
|
|
|
# Arrange for the event handler to raise an error. Switch inferior,
|
|
# check the error is printed, then check that the inferior switch was
|
|
# still successful.
|
|
gdb_test_no_output "python event_throws_error = True"
|
|
gdb_test "inferior 2" \
|
|
[multi_line \
|
|
"\\\[Switching to inferior 2\[^\r\n\]*\\\]" \
|
|
"\[^\r\n\]+: error from gdb_selected_context_handler"] \
|
|
"switch to inferior 2, event raises an error"
|
|
check_inferior 2 "check inferior 2 was selected"
|
|
|
|
# Switch back to inferior 1.
|
|
gdb_test "inferior 1" ".*" \
|
|
"return to inferior 1"
|
|
|
|
# Load the executable and start the inferior.
|
|
gdb_load $binfile
|
|
if {![runto_main]} {
|
|
return
|
|
}
|
|
|
|
# Setup breakpoints and continue until the first is reached.
|
|
gdb_breakpoint [gdb_get_line_number "First breakpoint"]
|
|
gdb_breakpoint [gdb_get_line_number "Second breakpoint"]
|
|
gdb_continue_to_breakpoint "first bp"
|
|
|
|
# Ensure the expected thread is currently selected.
|
|
check_thread 1.2 "confirm expected thread selected"
|
|
|
|
# Switch thread. The event handler is still configured to raise an
|
|
# error, but the thread switch should still happen.
|
|
gdb_test "thread 1" \
|
|
[multi_line \
|
|
"\\\[Switching to thread 1\\.1\[^\r\n\]*\\\]" \
|
|
"#0\\s+\[^\r\n\]+(" \
|
|
"(warning: )?$decimal\t\[^\r\n\]+)?" \
|
|
"\[^\r\n\]+: error from gdb_selected_context_handler"] \
|
|
"switch thread, handler raises an error"
|
|
check_thread 1.1 "thread switched despite handler error"
|
|
|
|
# Switch frame, ensure event handler raises an error.
|
|
gdb_test "up" \
|
|
"#1\\s+.*: error from gdb_selected_context_handler" \
|
|
"error from event handler when switching frames"
|
|
|
|
# Disable handler errors.
|
|
gdb_test_no_output "python event_throws_error = False"
|
|
|
|
# Switch thread, ensure event handler triggers.
|
|
gdb_test "thread 2" [event_regexp 1 1.2 #0] \
|
|
"switch to thread 2, event handler triggers"
|
|
|
|
# Now switch frames, ensure the event handler triggers.
|
|
gdb_test "up" [event_regexp 1 1.2 #1] \
|
|
"move up a frame, event handler triggers"
|
|
gdb_test "down" [event_regexp 1 1.2 #0] \
|
|
"move down a frame, event handler triggers"
|
|
gdb_test "frame 1" [event_regexp 1 1.2 #1] \
|
|
"select a frame, event handler triggers"
|
|
|
|
# Check that switching threads via MI also triggers the Python
|
|
# selected_context event. Grab the regexp we expect for the CLI, pull
|
|
# it apart, and wrap each line with the usual ~"...\n" wrapper we see
|
|
# with MI. Then add the MI '^done,....' line, and make this into
|
|
# the expected output regexp.
|
|
set cli_pattern [event_regexp 1 1.1 #0]
|
|
set cli_pattern [string map {\r\n \n} $cli_pattern]
|
|
set cli_pattern [split $cli_pattern \n]
|
|
set mi_pattern {}
|
|
foreach line $cli_pattern {
|
|
lappend mi_pattern "~\"$line\\\\n\""
|
|
}
|
|
lappend mi_pattern "\\^done,new-thread-id=\"1\",\[^\r\n\]+"
|
|
set pattern [multi_line {*}$mi_pattern]
|
|
|
|
# Switch threads using the MI. Python event should trigger.
|
|
gdb_test "interpreter-exec mi \"-thread-select 1\"" $pattern \
|
|
"mi thread switch triggers Python event"
|