From 7c6cb899c759fb00dc1a95a2ff904220fca1a9dc Mon Sep 17 00:00:00 2001 From: Simon Marchi Date: Fri, 5 Feb 2021 16:42:32 -0500 Subject: [PATCH] Testcases for stepping over thread exit syscall (PR gdb/27338) Add new gdb.threads/step-over-thread-exit.exp and gdb.threads/step-over-thread-exit-while-stop-all-threads.exp testcases, exercising stepping over thread exit syscall. These make use of lib/my-syscalls.S to define the exit syscall. Co-authored-by: Pedro Alves Reviewed-By: Andrew Burgess Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=27338 Change-Id: Ie8b2c5747db99b7023463a897a8390d9e814a9c9 --- ...-over-thread-exit-while-stop-all-threads.c | 77 +++++++++++ ...ver-thread-exit-while-stop-all-threads.exp | 69 ++++++++++ .../gdb.threads/step-over-thread-exit.c | 52 ++++++++ .../gdb.threads/step-over-thread-exit.exp | 126 ++++++++++++++++++ gdb/testsuite/lib/my-syscalls.S | 4 + gdb/testsuite/lib/my-syscalls.h | 5 + 6 files changed, 333 insertions(+) create mode 100644 gdb/testsuite/gdb.threads/step-over-thread-exit-while-stop-all-threads.c create mode 100644 gdb/testsuite/gdb.threads/step-over-thread-exit-while-stop-all-threads.exp create mode 100644 gdb/testsuite/gdb.threads/step-over-thread-exit.c create mode 100644 gdb/testsuite/gdb.threads/step-over-thread-exit.exp diff --git a/gdb/testsuite/gdb.threads/step-over-thread-exit-while-stop-all-threads.c b/gdb/testsuite/gdb.threads/step-over-thread-exit-while-stop-all-threads.c new file mode 100644 index 00000000000..2699ad5d714 --- /dev/null +++ b/gdb/testsuite/gdb.threads/step-over-thread-exit-while-stop-all-threads.c @@ -0,0 +1,77 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2021-2022 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 . */ + +#include +#include +#include +#include "../lib/my-syscalls.h" + +#define NUM_THREADS 32 + +static void * +stepper_over_exit_thread (void *v) +{ + my_exit (0); + + /* my_exit above should exit the thread, we don't expect to reach + here. */ + abort (); +} + +static void * +spawner_thread (void *v) +{ + for (;;) + { + pthread_t threads[NUM_THREADS]; + int i; + + for (i = 0; i < NUM_THREADS; i++) + pthread_create (&threads[i], NULL, stepper_over_exit_thread, NULL); + + for (i = 0; i < NUM_THREADS; i++) + pthread_join (threads[i], NULL); + } +} + +static void +break_here (void) +{ +} + +static void * +breakpoint_hitter_thread (void *v) +{ + for (;;) + break_here (); +} + +int +main () +{ + pthread_t breakpoint_hitter; + pthread_t spawner; + + alarm (60); + + pthread_create (&spawner, NULL, spawner_thread, NULL); + pthread_create (&breakpoint_hitter, NULL, breakpoint_hitter_thread, NULL); + + pthread_join (spawner, NULL); + + return 0; +} diff --git a/gdb/testsuite/gdb.threads/step-over-thread-exit-while-stop-all-threads.exp b/gdb/testsuite/gdb.threads/step-over-thread-exit-while-stop-all-threads.exp new file mode 100644 index 00000000000..6a46aff700e --- /dev/null +++ b/gdb/testsuite/gdb.threads/step-over-thread-exit-while-stop-all-threads.exp @@ -0,0 +1,69 @@ +# Copyright 2021-2022 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 . + +# Test stepping over a breakpoint installed on an instruction that +# exits the thread, while another thread is repeatedly hitting a +# breakpoint, causing GDB to stop all threads. + +standard_testfile .c + +set syscalls_src $srcdir/lib/my-syscalls.S + +if { [build_executable "failed to prepare" $testfile \ + [list $srcfile $syscalls_src] {debug pthreads}] == -1 } { + return +} + +proc test {displaced-stepping target-non-stop} { + save_vars ::GDBFLAGS { + append ::GDBFLAGS " -ex \"maintenance set target-non-stop ${target-non-stop}\"" + clean_restart $::binfile + } + + gdb_test_no_output "set displaced-stepping ${displaced-stepping}" + + if { ![runto_main] } { + return + } + + # The "stepper over exit" threads will step over an instruction + # that causes them to exit. + gdb_test "break my_exit_syscall if 0" + + # The "breakpoint hitter" thread will repeatedly hit this + # breakpoint, causing GDB to stop all threads. + gdb_test "break break_here" + + # To avoid flooding the log with thread created/exited messages. + gdb_test_no_output "set print thread-events off" + + # Make sure the target reports the breakpoint stops. + gdb_test_no_output "set breakpoint condition-evaluation host" + + for { set i 0 } { $i < 30 } { incr i } { + with_test_prefix "iter $i" { + if { [gdb_test "continue" "hit Breakpoint $::decimal, break_here .*"] != 0 } { + # Exit if there's a failure to avoid lengthy timeouts. + break + } + } + } +} + +foreach_with_prefix displaced-stepping {off auto} { + foreach_with_prefix target-non-stop {off on} { + test ${displaced-stepping} ${target-non-stop} + } +} diff --git a/gdb/testsuite/gdb.threads/step-over-thread-exit.c b/gdb/testsuite/gdb.threads/step-over-thread-exit.c new file mode 100644 index 00000000000..878e5924c5c --- /dev/null +++ b/gdb/testsuite/gdb.threads/step-over-thread-exit.c @@ -0,0 +1,52 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2021-2022 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 . */ + +#include +#include +#include +#include "../lib/my-syscalls.h" + +static void * +thread_func (void *arg) +{ + my_exit (0); + + /* my_exit above should exit the thread, we don't expect to reach + here. */ + abort (); +} + +int +main (void) +{ + int i; + + /* Spawn and join a thread, 100 times. */ + for (i = 0; i < 100; i++) + { + pthread_t thread; + int ret; + + ret = pthread_create (&thread, NULL, thread_func, NULL); + assert (ret == 0); + + ret = pthread_join (thread, NULL); + assert (ret == 0); + } + + return 0; +} diff --git a/gdb/testsuite/gdb.threads/step-over-thread-exit.exp b/gdb/testsuite/gdb.threads/step-over-thread-exit.exp new file mode 100644 index 00000000000..ed8534cf518 --- /dev/null +++ b/gdb/testsuite/gdb.threads/step-over-thread-exit.exp @@ -0,0 +1,126 @@ +# Copyright 2021-2022 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 . + +# Test stepping over a breakpoint installed on an instruction that +# exits the thread. + +standard_testfile .c + +set syscalls_src $srcdir/lib/my-syscalls.S + +if { [build_executable "failed to prepare" $testfile \ + [list $srcfile $syscalls_src] {debug pthreads}] == -1 } { + return +} + +# Each argument is a different testing axis, most of them obvious. +# NS_STOP_ALL is only used if testing "set non-stop on", and indicates +# whether to have GDB explicitly stop all threads before continuing to +# thread exit. +proc test {displaced-stepping non-stop target-non-stop schedlock ns_stop_all} { + if {${non-stop} == "off" && $ns_stop_all} { + error "invalid arguments" + } + + save_vars ::GDBFLAGS { + append ::GDBFLAGS " -ex \"maintenance set target-non-stop ${target-non-stop}\"" + append ::GDBFLAGS " -ex \"set non-stop ${non-stop}\"" + clean_restart $::binfile + } + + gdb_test_no_output "set displaced-stepping ${displaced-stepping}" + + if { ![runto_main] } { + return + } + + gdb_breakpoint "my_exit_syscall" + + if {$schedlock + || (${non-stop} == "on" && $ns_stop_all)} { + gdb_test "continue" \ + "Thread 2 .*hit Breakpoint $::decimal.* my_exit_syscall .*" \ + "continue until syscall" + + if {${non-stop} == "on"} { + # The test only spawns one thread at a time, so this just + # stops the main thread. + gdb_test_multiple "interrupt -a" "" { + -re "$::gdb_prompt " { + gdb_test_multiple "" $gdb_test_name { + -re "Thread 1 \[^\r\n\]*stopped." { + pass $gdb_test_name + } + } + } + } + } + + gdb_test "thread 2" "Switching to thread 2 .*" + + gdb_test_no_output "set scheduler-locking ${schedlock}" + + gdb_test "continue" \ + "No unwaited-for children left." \ + "continue stops when thread exits" + } else { + gdb_test_no_output "set scheduler-locking ${schedlock}" + + for { set i 0 } { $i < 100 } { incr i } { + with_test_prefix "iter $i" { + set ok 0 + set thread "" + gdb_test_multiple "continue" "" { + -re -wrap "Thread ($::decimal) .*hit Breakpoint $::decimal.* my_exit_syscall .*" { + set thread $expect_out(1,string) + set ok 1 + } + } + if {!${ok}} { + # Exit if there's a failure to avoid lengthy + # timeouts. + break + } + + if {${non-stop}} { + gdb_test "thread $thread" "Switching to thread .*" \ + "switch to event thread" + } + } + } + } +} + +foreach_with_prefix displaced-stepping {off auto} { + foreach_with_prefix non-stop {off on} { + foreach_with_prefix target-non-stop {off on} { + if {${non-stop} == "on" && ${target-non-stop} == "off"} { + # Invalid combination. + continue + } + + foreach_with_prefix schedlock {off on} { + if {${non-stop} == "on"} { + foreach_with_prefix ns_stop_all {0 1} { + test ${displaced-stepping} ${non-stop} ${target-non-stop} \ + ${schedlock} ${ns_stop_all} + } + } else { + test ${displaced-stepping} ${non-stop} ${target-non-stop} ${schedlock} 0 + } + } + } + } +} diff --git a/gdb/testsuite/lib/my-syscalls.S b/gdb/testsuite/lib/my-syscalls.S index 38299e45284..02196dd9555 100644 --- a/gdb/testsuite/lib/my-syscalls.S +++ b/gdb/testsuite/lib/my-syscalls.S @@ -69,4 +69,8 @@ NAME ## _syscall: ;\ SYSCALL (my_execve, __NR_execve) +/* void my_exit (int code); */ + +SYSCALL (my_exit, __NR_exit) + .section .note.GNU-stack,"",@progbits diff --git a/gdb/testsuite/lib/my-syscalls.h b/gdb/testsuite/lib/my-syscalls.h index cdce05058f9..7f9ae387427 100644 --- a/gdb/testsuite/lib/my-syscalls.h +++ b/gdb/testsuite/lib/my-syscalls.h @@ -22,4 +22,9 @@ int my_execve (const char *file, char *argv[], char *envp[]); +/* `exit` syscall, which makes the thread exit (as opposed to + `exit_group`, which makes the process exit). */ + +void my_exit (int code); + #endif /* MY_SYSCALLS_H */ -- 2.30.2