Home | History | Annotate | Line # | Download | only in gdb.threads
      1  1.1  christos # Copyright 2021-2024 Free Software Foundation, Inc.
      2  1.1  christos 
      3  1.1  christos # This program is free software; you can redistribute it and/or modify
      4  1.1  christos # it under the terms of the GNU General Public License as published by
      5  1.1  christos # the Free Software Foundation; either version 3 of the License, or
      6  1.1  christos # (at your option) any later version.
      7  1.1  christos #
      8  1.1  christos # This program is distributed in the hope that it will be useful,
      9  1.1  christos # but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  1.1  christos # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11  1.1  christos # GNU General Public License for more details.
     12  1.1  christos #
     13  1.1  christos # You should have received a copy of the GNU General Public License
     14  1.1  christos # along with this program.  If not, see <http://www.gnu.org/licenses/>.
     15  1.1  christos 
     16  1.1  christos # Test stepping over a breakpoint installed on an instruction that
     17  1.1  christos # exits the thread.
     18  1.1  christos 
     19  1.1  christos standard_testfile .c
     20  1.1  christos 
     21  1.1  christos set syscalls_src $srcdir/lib/my-syscalls.S
     22  1.1  christos 
     23  1.1  christos if { [build_executable "failed to prepare" $testfile \
     24  1.1  christos 	  [list $srcfile $syscalls_src] {debug pthreads}] == -1 } {
     25  1.1  christos     return
     26  1.1  christos }
     27  1.1  christos 
     28  1.1  christos # Test stepping/continuing at an exit syscall instruction.
     29  1.1  christos #
     30  1.1  christos # Each argument is a different testing axis.
     31  1.1  christos #
     32  1.1  christos # STEP_OVER_MODE can be one of:
     33  1.1  christos #
     34  1.1  christos #   - none: don't put a breakpoint on the exit syscall instruction.
     35  1.1  christos #
     36  1.1  christos #   - inline: put a breakpoint on the exit syscall instruction, and
     37  1.1  christos #     use in-line stepping to step over it (disable
     38  1.1  christos #     displaced-stepping).
     39  1.1  christos #
     40  1.1  christos #   - displaced: same, but use displaced stepping.
     41  1.1  christos #
     42  1.1  christos # SCHEDLOCK can be "on" or "off".
     43  1.1  christos #
     44  1.1  christos # CMD is the GDB command to run when at the exit syscall instruction.
     45  1.1  christos #
     46  1.1  christos # NS_STOP_ALL is only used if testing "set non-stop on", and indicates
     47  1.1  christos # whether to have GDB explicitly stop all threads before continuing to
     48  1.1  christos # thread exit.
     49  1.1  christos #
     50  1.1  christos proc test {step_over_mode non-stop target-non-stop schedlock cmd ns_stop_all} {
     51  1.1  christos     if {${non-stop} == "off" && $ns_stop_all} {
     52  1.1  christos 	error "invalid arguments"
     53  1.1  christos     }
     54  1.1  christos 
     55  1.1  christos     save_vars ::GDBFLAGS {
     56  1.1  christos 	append ::GDBFLAGS " -ex \"maintenance set target-non-stop ${target-non-stop}\""
     57  1.1  christos 	append ::GDBFLAGS " -ex \"set non-stop ${non-stop}\""
     58  1.1  christos 	clean_restart $::binfile
     59  1.1  christos     }
     60  1.1  christos 
     61  1.1  christos     if { $step_over_mode == "none" } {
     62  1.1  christos 	# Nothing to do.
     63  1.1  christos     } elseif { $step_over_mode == "inline" } {
     64  1.1  christos 	gdb_test_no_output "set displaced-stepping off"
     65  1.1  christos     } elseif { $step_over_mode == "displaced" } {
     66  1.1  christos 	gdb_test_no_output "set displaced-stepping on"
     67  1.1  christos     } else {
     68  1.1  christos 	error "Invalid step_over_mode value: $step_over_mode"
     69  1.1  christos     }
     70  1.1  christos 
     71  1.1  christos     if {$schedlock
     72  1.1  christos 	|| (${non-stop} == "on" && $ns_stop_all)} {
     73  1.1  christos 
     74  1.1  christos 	gdb_test_no_output "set args 1"
     75  1.1  christos 
     76  1.1  christos 	if { ![runto my_exit_syscall] } {
     77  1.1  christos 	    return
     78  1.1  christos 	}
     79  1.1  christos 
     80  1.1  christos 	if {${non-stop} == "on"} {
     81  1.1  christos 	    # The test only spawns one thread at a time, so this just
     82  1.1  christos 	    # stops the main thread.  IOW, we only need to wait for
     83  1.1  christos 	    # one stop.
     84  1.1  christos 	    gdb_test_multiple "interrupt -a" "" {
     85  1.1  christos 		-re "$::gdb_prompt " {
     86  1.1  christos 		    gdb_test_multiple "" $gdb_test_name {
     87  1.1  christos 			-re "Thread 1 \[^\r\n\]*stopped." {
     88  1.1  christos 			    pass $gdb_test_name
     89  1.1  christos 			}
     90  1.1  christos 		    }
     91  1.1  christos 		}
     92  1.1  christos 	    }
     93  1.1  christos 
     94  1.1  christos 	    gdb_test "thread 2" "Switching to thread 2 .*"
     95  1.1  christos 	}
     96  1.1  christos 
     97  1.1  christos 	gdb_test_no_output "set scheduler-locking ${schedlock}"
     98  1.1  christos 
     99  1.1  christos 	# If testing a step-over is requested, leave the breakpoint at
    100  1.1  christos 	# the current instruction to force a step-over; otherwise,
    101  1.1  christos 	# remove it.
    102  1.1  christos 	if { $step_over_mode == "none" } {
    103  1.1  christos 	    delete_breakpoints
    104  1.1  christos 	}
    105  1.1  christos 
    106  1.1  christos 	if {$cmd == "continue"} {
    107  1.1  christos 	    gdb_test "continue" \
    108  1.1  christos 		"No unwaited-for children left." \
    109  1.1  christos 		"continue stops when thread exits"
    110  1.1  christos 	} else {
    111  1.1  christos 	    gdb_test_multiple $cmd "command aborts when thread exits" {
    112  1.1  christos 		-re "Command aborted, thread exited\\.\r\n$::gdb_prompt " {
    113  1.1  christos 		    pass $gdb_test_name
    114  1.1  christos 		}
    115  1.1  christos 	    }
    116  1.1  christos 	}
    117  1.1  christos     } else {
    118  1.1  christos 	# Schedlock is off here.
    119  1.1  christos 	#
    120  1.1  christos 	# With "continue" and no scheduler-locking, GDB doesn't stop
    121  1.1  christos 	# with "Command aborted, thread exited." when the thread
    122  1.1  christos 	# exits, it just lets the inferior continue running freely.
    123  1.1  christos 	# So we test that we can move past the thread exit, and that
    124  1.1  christos 	# other threads can be freely scheduled.  We do that by
    125  1.1  christos 	# spawning another thread as soon as the first exit.  We test
    126  1.1  christos 	# that a number of times.  This should also exercise GDB's
    127  1.1  christos 	# handling of inline or displaced step-overs, that GDB handles
    128  1.1  christos 	# the related resource accounting correctly when the stepping
    129  1.1  christos 	# thread exits, etc.
    130  1.1  christos 	#
    131  1.1  christos 	# With "continue" and $step_over_mode == "none" however, after
    132  1.1  christos 	# the first my_exit_syscall breakpoint hit, we will remove the
    133  1.1  christos 	# breakpoint, so no other thread would ever hit it again.  So
    134  1.1  christos 	# might as well just test one thread.
    135  1.1  christos 	#
    136  1.1  christos 	# With step/next, GDB aborts the execution command with
    137  1.1  christos 	# "Command aborted, thread exited." when the stepping thread
    138  1.1  christos 	# exits.  If we let the main spawn another thread as soon as
    139  1.1  christos 	# the first exits, it would be possible for that new thread to
    140  1.1  christos 	# hit the exit syscall insn breakpoint quickly enough that it
    141  1.1  christos 	# would be reported to be user before the first thread exit
    142  1.1  christos 	# would be, which would confuse testing.  To avoid that, we
    143  1.1  christos 	# only spawn one thread, too.
    144  1.1  christos 	#
    145  1.1  christos 	if {$cmd != "continue" || $step_over_mode == "none"} {
    146  1.1  christos 	    set n_threads 1
    147  1.1  christos 	} else {
    148  1.1  christos 	    set n_threads 100
    149  1.1  christos 	}
    150  1.1  christos 
    151  1.1  christos 	gdb_test_no_output "set args $n_threads"
    152  1.1  christos 
    153  1.1  christos 	if { ![runto_main] } {
    154  1.1  christos 	    return
    155  1.1  christos 	}
    156  1.1  christos 
    157  1.1  christos 	gdb_breakpoint "my_exit_syscall"
    158  1.1  christos 
    159  1.1  christos 	gdb_test_no_output "set scheduler-locking ${schedlock}"
    160  1.1  christos 
    161  1.1  christos 	if {$cmd != "continue" || $step_over_mode == "none"} {
    162  1.1  christos 	    set thread "<unknown>"
    163  1.1  christos 	    gdb_test_multiple "continue" "" {
    164  1.1  christos 		-re -wrap "Thread ($::decimal) .*hit Breakpoint $::decimal.* my_exit_syscall .*" {
    165  1.1  christos 		    set thread $expect_out(1,string)
    166  1.1  christos 		}
    167  1.1  christos 	    }
    168  1.1  christos 	    if {${non-stop}} {
    169  1.1  christos 		gdb_test -nopass "thread $thread" "Switching to thread .*" \
    170  1.1  christos 		    "switch to event thread"
    171  1.1  christos 	    }
    172  1.1  christos 
    173  1.1  christos 	    # If testing a step-over is requested, leave the breakpoint at
    174  1.1  christos 	    # the current instruction to force a step-over; otherwise,
    175  1.1  christos 	    # remove it.
    176  1.1  christos 	    if { $step_over_mode == "none" } {
    177  1.1  christos 		delete_breakpoints
    178  1.1  christos 	    }
    179  1.1  christos 
    180  1.1  christos 	    if {$cmd == "continue"} {
    181  1.1  christos 		gdb_continue_to_end "continue to end" "continue" 1
    182  1.1  christos 	    } else {
    183  1.1  christos 		gdb_test_multiple $cmd "command aborts when thread exits" {
    184  1.1  christos 		    -re "Command aborted, thread exited\\.\r\n$::gdb_prompt " {
    185  1.1  christos 			pass $gdb_test_name
    186  1.1  christos 		    }
    187  1.1  christos 		}
    188  1.1  christos 		gdb_test "p \$_thread == $thread" "= 1" \
    189  1.1  christos 		    "selected thread didn't change"
    190  1.1  christos 	    }
    191  1.1  christos 	} else {
    192  1.1  christos 	    for { set i 0 } { $i < 100 } { incr i } {
    193  1.1  christos 		with_test_prefix "iter $i" {
    194  1.1  christos 		    set ok 0
    195  1.1  christos 		    set thread "<unknown>"
    196  1.1  christos 		    gdb_test_multiple "continue" "" {
    197  1.1  christos 			-re -wrap "Thread ($::decimal) .*hit Breakpoint $::decimal.* my_exit_syscall .*" {
    198  1.1  christos 			    set thread $expect_out(1,string)
    199  1.1  christos 			    set ok 1
    200  1.1  christos 			}
    201  1.1  christos 		    }
    202  1.1  christos 		    if {!${ok}} {
    203  1.1  christos 			# Exit if there's a failure to avoid lengthy
    204  1.1  christos 			# timeouts.
    205  1.1  christos 			break
    206  1.1  christos 		    }
    207  1.1  christos 
    208  1.1  christos 		    if {${non-stop}} {
    209  1.1  christos 			gdb_test -nopass "thread $thread" "Switching to thread .*" \
    210  1.1  christos 			    "switch to event thread"
    211  1.1  christos 		    }
    212  1.1  christos 		}
    213  1.1  christos 	    }
    214  1.1  christos 	}
    215  1.1  christos     }
    216  1.1  christos }
    217  1.1  christos 
    218  1.1  christos foreach_with_prefix step_over_mode {none inline displaced} {
    219  1.1  christos     foreach_with_prefix non-stop {off on} {
    220  1.1  christos 	foreach_with_prefix target-non-stop {off on} {
    221  1.1  christos 	    if {${non-stop} == "on" && ${target-non-stop} == "off"} {
    222  1.1  christos 		# Invalid combination.
    223  1.1  christos 		continue
    224  1.1  christos 	    }
    225  1.1  christos 
    226  1.1  christos 	    foreach_with_prefix schedlock {off on} {
    227  1.1  christos 		foreach_with_prefix cmd {"next" "continue"} {
    228  1.1  christos 		    if {${non-stop} == "on"} {
    229  1.1  christos 			foreach_with_prefix ns_stop_all {0 1} {
    230  1.1  christos 			    test ${step_over_mode} ${non-stop} ${target-non-stop} \
    231  1.1  christos 				${schedlock} ${cmd} ${ns_stop_all}
    232  1.1  christos 			}
    233  1.1  christos 		    } else {
    234  1.1  christos 			test ${step_over_mode} ${non-stop} ${target-non-stop} ${schedlock} ${cmd} 0
    235  1.1  christos 		    }
    236  1.1  christos 		}
    237  1.1  christos 	    }
    238  1.1  christos 	}
    239  1.1  christos     }
    240  1.1  christos }
    241