| # 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" |