Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

# Copyright (C) 2016-2020 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/>.

# Regression test for PR gdb/18360.  Test that "interrupt -a" while
# some thread is stepping over a breakpoint behaves as expected.

standard_testfile

if {[prepare_for_testing "failed to prepare" $testfile $srcfile \
	 {debug pthreads}] == -1} {
    return -1
}

if {![runto_main]} {
    fail "can't run to main"
    return -1
}

# Read the number of threads out of the inferior.
set NUM_THREADS [get_integer_valueof "num_threads" -1]

# Account for the main thread.
incr NUM_THREADS

# Run command and wait for the prompt, without end anchor.

proc gdb_test_no_anchor {cmd} {
    global gdb_prompt

    gdb_test_multiple $cmd $cmd {
	-re "$gdb_prompt " {
	    pass $cmd
	}
	-re "infrun:" {
	    exp_continue
	}
    }
}

# Enable/disable debugging.

proc enable_debug {enable} {

    # Comment out to debug problems with the test.
    return

    gdb_test_no_anchor "set debug infrun $enable"
    gdb_test_no_anchor "set debug displaced $enable"
}

# If RESULT is not zero, make the caller return RESULT.

proc return_if_nonzero { result } {
    if {$result != 0} {
	return -code return $result
    }
}

# Do one iteration of "c -a& -> interrupt -a".  Return zero on sucess,
# and non-zero if some test fails.

proc test_one_iteration {} {
    global gdb_prompt
    global NUM_THREADS
    global decimal

    set saw_continuing 0
    set test "continue -a &"
    return_if_nonzero [gdb_test_multiple $test $test {
	-re "Continuing.\r\n" {
	    set saw_continuing 1
	    exp_continue
	}
	-re "$gdb_prompt " {
	    if ![gdb_assert $saw_continuing $test] {
		return 1
	    }
	}
	-re "infrun:" {
	    exp_continue
	}
    }]

    set running_count 0
    set test "all threads are running"
    return_if_nonzero [gdb_test_multiple "info threads" $test {
	-re "Thread \[^\r\n\]* \\(running\\)" {
	    incr running_count
	    exp_continue
	}
	-re "$gdb_prompt " {
	    if ![gdb_assert {$running_count == $NUM_THREADS} $test] {
		return 1
	    }
	}
	-re "infrun:" {
	    exp_continue
	}
    }]

    set test "interrupt -a"
    return_if_nonzero [gdb_test_multiple $test $test {
	-re "$gdb_prompt " {
	    pass $test
	}
	-re "infrun:" {
	    exp_continue
	}
    }]

    set stopped_count 0
    set test "wait for stops"
    # Don't return on failure here, in order to let "info threads" put
    # useful info in gdb.log.
    gdb_test_multiple "" $test {
	-re "Thread $decimal \[^\r\n\]*stopped" {
	    incr stopped_count
	    if {$stopped_count != $NUM_THREADS} {
		exp_continue
	    }
	}
	-re "$gdb_prompt " {
	    gdb_assert {$stopped_count == $NUM_THREADS} $test
	}
	-re "infrun:" {
	    exp_continue
	}
    }

    # Check if all threads are seen as stopped with "info
    # threads".  It's a bit redundant with the test above, but
    # it's useful to have this in the gdb.log if the above ever
    # happens to fail.
    set running_count 0
    set test "all threads are stopped"
    return_if_nonzero [gdb_test_multiple "info threads" $test {
	-re "Thread \[^\r\n\]* \\(running\\)" {
	    incr running_count
	    exp_continue
	}
	-re "$gdb_prompt " {
	    if ![gdb_assert {$running_count == 0} $test] {
		return 1
	    }
	}
    }]

    return 0
}

# The test driver proper.  If DISPLACED is "on", turn on displaced
# stepping.  If "off", turn it off.

proc testdriver {displaced} {
    global binfile
    global GDBFLAGS

    save_vars { GDBFLAGS } {
	append GDBFLAGS " -ex \"set non-stop on\""
	clean_restart $binfile
    }

    gdb_test_no_output "set displaced-stepping $displaced"

    if ![runto all_started] {
	fail "couldn't run to all_started"
	return
    }
    set break_line [gdb_get_line_number "set breakpoint here"]

    gdb_test "break $break_line if always_zero" "Breakpoint .*" "set breakpoint"

    enable_debug 1

    for {set iter 0} {$iter < 20} {incr iter} {
	with_test_prefix "iter=$iter" {
	    # Return early if some test fails, to avoid cascading
	    # timeouts if something goes wrong.
	    if {[test_one_iteration] != 0} {
		return
	    }
	}
    }
}

foreach_with_prefix displaced-stepping {"on" "off"} {
    if { ${displaced-stepping} != "off" && ![support_displaced_stepping] } {
	continue
    }

    testdriver ${displaced-stepping}
}