This is the mail archive of the
gdb-patches@sources.redhat.com
mailing list for the GDB project.
Re: RFA (threads testsuite): More thread tests
- From: Michael Snyder <msnyder at redhat dot com>
- To: Daniel Jacobowitz <drow at mvista dot com>
- Cc: gdb-patches at sources dot redhat dot com
- Date: Thu, 22 Aug 2002 16:34:51 -0700
- Subject: Re: RFA (threads testsuite): More thread tests
- Organization: Red Hat, Inc.
- References: <20020709154033.GA7204@nevyn.them.org>
Daniel Jacobowitz wrote:
>
> Here's two tests I had lying around from when I developed the gdbserver
> threads support. Gdbserver passes them with flying colors (if you use my
> other patch which lets gdbserver run tests properly). GDB shows a couple of
> problems, unpredictably (not always repeatable).
>
> OK to add these?
Hi Joel,
I understand the point of schedlock.exp, but what's the point of
print-threads.exp? What is it that you're testing?
Michael
>
> --
> Daniel Jacobowitz Carnegie Mellon University
> MontaVista Software Debian GNU/Linux Developer
>
> 2002-07-09 Daniel Jacobowitz <drow@mvista.com>
>
> * gdb.threads/print-threads.c: New file.
> * gdb.threads/print-threads.exp: New file.
> * gdb.threads/schedlock.c: New file.
> * gdb.threads/schedlock.exp: New file.
>
> --- /dev/null Wed Dec 31 19:00:00 1969
> +++ gdb.threads/print-threads.c Tue Jun 11 14:56:44 2002
> @@ -0,0 +1,58 @@
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <pthread.h>
> +
> +void *thread_function(void *arg); /* Pointer to function executed by each thread */
> +
> +int slow = 0;
> +
> +#define NUM 5
> +
> +int main() {
> + int res;
> + pthread_t threads[NUM];
> + void *thread_result;
> + int args[NUM];
> + int i;
> +
> + for (i = 0; i < NUM; i++)
> + {
> + args[i] = i;
> + res = pthread_create(&threads[i], NULL, thread_function, (void *)&args[i]);
> + }
> +
> + for (i = 0; i < NUM; i++)
> + res = pthread_join(threads[i], &thread_result);
> +
> + printf ("Done\n");
> +
> + if (slow)
> + sleep (4);
> +
> + exit(EXIT_SUCCESS);
> +}
> +
> +void *thread_function(void *arg) {
> + int my_number = *(int *)arg;
> + int rand_num;
> +
> + printf ("Print 1, thread %d\n", my_number);
> + sleep (1);
> +
> + if (slow)
> + {
> + printf ("Print 2, thread %d\n", my_number);
> + sleep (1);
> + printf ("Print 3, thread %d\n", my_number);
> + sleep (1);
> + printf ("Print 4, thread %d\n", my_number);
> + sleep (1);
> + printf ("Print 5, thread %d\n", my_number);
> + sleep (1);
> + }
> +
> + printf("Bye from %d\n", my_number);
> + pthread_exit(NULL);
> +}
> +
> --- /dev/null Wed Dec 31 19:00:00 1969
> +++ gdb.threads/print-threads.exp Tue Jun 11 14:56:44 2002
> @@ -0,0 +1,158 @@
> +# Copyright (C) 1996, 1997, 2002 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 2 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, write to the Free Software
> +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> +
> +# Please email any bugs, comments, and/or additions to this file to:
> +# bug-gdb@prep.ai.mit.edu
> +
> +# This file was written by Daniel Jacobowitz <drow@mvista.com>
> +# (parts based on pthreads.exp by Fred Fish (fnf@cygnus.com).
> +
> +if $tracelevel then {
> + strace $tracelevel
> +}
> +
> +set prms_id 0
> +set bug_id 0
> +
> +# This only works with native configurations
> +if ![isnative] then {
> + return
> +}
> +
> +set testfile "print-threads"
> +set srcfile ${testfile}.c
> +set binfile ${objdir}/${subdir}/${testfile}
> +
> +# regexp for "horizontal" text (i.e. doesn't include newline or
> +# carriage return)
> +set horiz "\[^\n\r\]*"
> +
> +set built_binfile 0
> +
> +# Default to the usual (only?) -lpthread on GNU/Linux to quiet noise
> +if [istarget "*-*-linux*"] then {
> + set possible_libs "-lpthread -lpthreads -lthread"
> +} else {
> + set possible_libs "-lpthreads -lpthread -lthread"
> +}
> +
> +set why_msg "unrecognized error"
> +foreach lib $possible_libs {
> + set options "debug"
> + lappend options "incdir=${objdir}/${subdir}"
> + lappend options "libs=$lib"
> + set ccout [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable $options]
> + switch -regexp -- $ccout {
> + ".*no posix threads support.*" {
> + set why_msg "missing threads include file"
> + break
> + }
> + ".*cannot open -lpthread.*" {
> + set why_msg "missing runtime threads library"
> + }
> + ".*Can't find library for -lpthread.*" {
> + set why_msg "missing runtime threads library"
> + }
> + {^$} {
> + pass "successfully compiled posix threads test case"
> + set built_binfile 1
> + break
> + }
> + }
> +}
> +if {$built_binfile == "0"} {
> + unsupported "Couldn't compile ${srcfile}, ${why_msg}"
> + return -1
> +}
> +
> +# Now we can proceed with the real testing.
> +
> +# Start with a fresh gdb.
> +
> +gdb_exit
> +gdb_start
> +gdb_reinitialize_dir $srcdir/$subdir
> +gdb_load ${binfile}
> +
> +gdb_test "set print sevenbit-strings" ""
> +#gdb_test "set print address off" ""
> +gdb_test "set width 0" ""
> +
> +# We'll need this when we send_gdb a ^C to GDB. Need to do it before we
> +# run the program and gdb starts saving and restoring tty states.
> +# On Ultrix, we don't need it and it is really slow (because shell_escape
> +# doesn't use vfork).
> +if ![istarget "*-*-ultrix*"] then {
> + gdb_test "shell stty intr '^C'" ""
> +}
> +
> +proc test_all_threads { name kill } {
> + global gdb_prompt
> +
> + set i 0
> + set j 0
> + send_gdb "continue\n"
> + gdb_expect {
> + -re "Breakpoint \[0-9\]+, thread_function \\(arg=.*\\) at .*print-threads.c:\[0-9\]+.*$gdb_prompt" {
> + set i [expr $i + 1]
> + pass "Hit thread_function breakpoint, $i ($name)"
> + send_gdb "continue\n"
> + exp_continue
> + }
> + -re "Breakpoint \[0-9\]+, .* kill \\(.*\\) .*$gdb_prompt" {
> + set j [expr $j + 1]
> + if { $kill == 1 } {
> + pass "Hit kill breakpoint, $j ($name)"
> + } else {
> + fail "Hit kill breakpoint, $j ($name) (unexpected)"
> + }
> + send_gdb "continue\n"
> + exp_continue
> + }
> + -re "Program exited normally\\.\[\r\n\]+$gdb_prompt" {
> + pass "program exited normally"
> + if {$i == 5} {
> + pass "all threads ran once ($name)"
> + } else {
> + fail "all threads ran once ($name) (total $i threads ran)"
> + }
> + }
> + -re "$gdb_prompt" {
> + fail "Running threads ($name) (unknown output)"
> + }
> + timeout {
> + fail "Running threads ($name) (timeout)"
> + }
> + }
> +}
> +
> +runto_main
> +gdb_test "break thread_function" "Breakpoint \[0-9\]+ at 0x\[0-9a-f\]+: file .*print-threads.c, line \[0-9\]*\\."
> +gdb_test "set var slow = 0" ""
> +test_all_threads "fast" 0
> +
> +runto_main
> +gdb_test "break thread_function" "Breakpoint \[0-9\]+ at 0x\[0-9a-f\]+: file .*print-threads.c, line \[0-9\]*\\." "break thread_function (2)"
> +gdb_test "set var slow = 1" ""
> +test_all_threads "slow" 0
> +
> +runto_main
> +gdb_test "break thread_function" "Breakpoint \[0-9\]+ at 0x\[0-9a-f\]+: file .*print-threads.c, line \[0-9\]*\\." "break thread_function (3)"
> +gdb_test "set var slow = 1" "" "set var slow = 1 (2)"
> +gdb_test "break kill" "Breakpoint \[0-9\]+ at 0x\[0-9a-f\]+"
> +test_all_threads "slow with kill breakpoint" 1
> +
> +return 0
> --- /dev/null Wed Dec 31 19:00:00 1969
> +++ gdb.threads/schedlock.c Tue Jun 11 14:56:44 2002
> @@ -0,0 +1,44 @@
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <pthread.h>
> +
> +void *thread_function(void *arg); /* Pointer to function executed by each thread */
> +
> +#define NUM 5
> +
> +int args[NUM+1];
> +
> +int main() {
> + int res;
> + pthread_t threads[NUM];
> + void *thread_result;
> + int i;
> +
> + for (i = 0; i < NUM; i++)
> + {
> + args[i] = 1;
> + res = pthread_create(&threads[i], NULL, thread_function, (void *)i);
> + }
> +
> + /* schedlock.exp: last thread start. */
> + args[i] = 1;
> + thread_function ((void *) i);
> +
> + exit(EXIT_SUCCESS);
> +}
> +
> +void *thread_function(void *arg) {
> + int my_number = (int) arg;
> + int *myp = &args[my_number];
> +
> + /* Don't run forever. Run just short of it :) */
> + while (*myp > 0)
> + {
> + /* schedlock.exp: main loop. */
> + (*myp) ++;
> + }
> +
> + pthread_exit(NULL);
> +}
> +
> --- /dev/null Wed Dec 31 19:00:00 1969
> +++ gdb.threads/schedlock.exp Tue Jun 11 14:56:44 2002
> @@ -0,0 +1,374 @@
> +# Copyright (C) 1996, 1997, 2002 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 2 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, write to the Free Software
> +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> +
> +# Please email any bugs, comments, and/or additions to this file to:
> +# bug-gdb@prep.ai.mit.edu
> +
> +# This file was written by Daniel Jacobowitz <drow@mvista.com>
> +# (parts based on pthreads.exp by Fred Fish (fnf@cygnus.com).
> +
> +if $tracelevel then {
> + strace $tracelevel
> +}
> +
> +set prms_id 0
> +set bug_id 0
> +
> +# This only works with native configurations
> +if ![isnative] then {
> + return
> +}
> +
> +set testfile "schedlock"
> +set srcfile ${testfile}.c
> +set binfile ${objdir}/${subdir}/${testfile}
> +
> +set built_binfile 0
> +
> +# Default to the usual (only?) -lpthread on GNU/Linux to quiet noise
> +if [istarget "*-*-linux*"] then {
> + set possible_libs "-lpthread -lpthreads -lthread"
> +} else {
> + set possible_libs "-lpthreads -lpthread -lthread"
> +}
> +
> +set why_msg "unrecognized error"
> +foreach lib $possible_libs {
> + set options "debug"
> + lappend options "incdir=${objdir}/${subdir}"
> + lappend options "libs=$lib"
> + set ccout [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable $options]
> + switch -regexp -- $ccout {
> + ".*no posix threads support.*" {
> + set why_msg "missing threads include file"
> + break
> + }
> + ".*cannot open -lpthread.*" {
> + set why_msg "missing runtime threads library"
> + }
> + ".*Can't find library for -lpthread.*" {
> + set why_msg "missing runtime threads library"
> + }
> + {^$} {
> + pass "successfully compiled posix threads test case"
> + set built_binfile 1
> + break
> + }
> + }
> +}
> +if {$built_binfile == "0"} {
> + unsupported "Couldn't compile ${srcfile}, ${why_msg}"
> + return -1
> +}
> +
> +# Now we can proceed with the real testing.
> +
> +proc get_args { } {
> + global list_count
> + global gdb_prompt
> +
> + send_gdb "print args\n"
> + gdb_expect {
> + -re "\\\$\[0-9\]+ = {(\[0-9\]+), (\[0-9\]+), (\[0-9\]+), (\[0-9\]+), (\[0-9\]+), (\[0-9\]+)}.*$gdb_prompt"
> + {
> + set list_count [expr $list_count + 1]
> + pass "listed args ($list_count)"
> + return [list $expect_out(1,string) $expect_out(2,string) $expect_out(3,string) $expect_out(4,string) $expect_out(5,string) $expect_out(6,string)]
> + }
> + -re "$gdb_prompt"
> + {
> + fail "listed args ($list_count) (unknown output)"
> + }
> + timeout
> + {
> + fail "listed args ($list_count) (timeout)"
> + }
> + }
> +}
> +
> +proc stop_process { description } {
> + global gdb_prompt
> +
> + # For this to work we must be sure to consume the "Continuing."
> + # message first, or GDB's signal handler may not be in place.
> + after 1000 {send_gdb "\003"}
> + gdb_expect {
> + -re "Program received signal SIGINT.*$gdb_prompt $"
> + {
> + pass $description
> + }
> + timeout
> + {
> + fail "$description (timeout)"
> + }
> + }
> +}
> +
> +proc get_current_thread { description } {
> + global gdb_prompt
> +
> + send_gdb "bt\n"
> + gdb_expect {
> + -re "thread_function \\(arg=0x(\[0-9\])\\).*$gdb_prompt $"
> + {
> + pass $description
> + return $expect_out(1,string)
> + }
> + -re "$gdb_prompt $"
> + {
> + fail "$description (unknown output)"
> + }
> + timeout
> + {
> + fail "$description (timeout)"
> + }
> + }
> +
> + return
> + send_gdb "info threads\n"
> + gdb_expect {
> + -re "\\* (\[0-9\]+) .*$gdb_prompt $"
> + {
> + pass $description
> + return $expect_out(1,string)
> + }
> + -re "$gdb_prompt $"
> + {
> + fail "$description (unknown output)"
> + }
> + timeout
> + {
> + fail "$description (timeout)"
> + }
> + }
> +}
> +
> +proc my_continue { msg } {
> + send_gdb "continue\n"
> + gdb_expect {
> + -re "Continuing"
> + { pass "continue ($msg)" }
> + timeout
> + { fail "continue ($msg) (timeout)" }
> + }
> +
> + stop_process "stop all threads ($msg)"
> +
> + # Make sure we're in one of the looping threads.
> + gdb_breakpoint [gdb_get_line_number "schedlock.exp: main loop"]
> + gdb_continue_to_breakpoint "return to loop ($msg)"
> + delete_breakpoints
> +}
> +
> +proc step_ten_loops { msg } {
> + global gdb_prompt
> +
> + for {set i 0} {[expr $i < 10]} {set i [expr $i + 1]} {
> + send_gdb "step\n"
> + set other_step 0
> + gdb_expect {
> + -re ".*myp\\) \\+\\+;\[\r\n\]+$gdb_prompt $" {
> + pass "step to increment ($msg $i)"
> + }
> + -re "$gdb_prompt $" {
> + if {$other_step == 0} {
> + set other_step 1
> + send_gdb "step\n"
> + exp_continue
> + } else {
> + fail "step to increment ($msg $i)"
> + # FIXME cascade?
> + }
> + }
> + timeout {
> + fail "step to increment ($msg $i) (timeout)"
> + }
> + }
> + }
> +}
> +
> +# Start with a fresh gdb.
> +
> +gdb_exit
> +gdb_start
> +gdb_reinitialize_dir $srcdir/$subdir
> +
> +# We'll need this when we send_gdb a ^C to GDB. Need to do it before we
> +# run the program and gdb starts saving and restoring tty states.
> +# On Ultrix, we don't need it and it is really slow (because shell_escape
> +# doesn't use vfork).
> +if ![istarget "*-*-ultrix*"] then {
> + gdb_test "shell stty intr '^C'" ""
> +}
> +
> +gdb_load ${binfile}
> +
> +gdb_test "set print sevenbit-strings" ""
> +gdb_test "set width 0" ""
> +
> +runto_main
> +
> +# See if scheduler locking is available on this target.
> +send_gdb "set scheduler-locking off\n"
> +global gdb_prompt
> +gdb_expect {
> + -re "Target .* cannot support this command"
> + {
> + unsupported "target does not support scheduler locking"
> + return
> + }
> + -re "$gdb_prompt $"
> + {
> + pass "scheduler locking set to none"
> + }
> + timeout
> + {
> + unsupported "target does not support scheduler locking (timeout)"
> + return
> + }
> +}
> +
> +gdb_breakpoint [gdb_get_line_number "schedlock.exp: last thread start"]
> +gdb_continue_to_breakpoint "all threads started"
> +
> +global list_count
> +set list_count 0
> +
> +set start_args [get_args]
> +
> +# First make sure that all threads are alive.
> +my_continue "initial"
> +
> +set cont_args [get_args]
> +
> +for {set i 0} {[expr $i < 6]} {set i [expr $i + 1]} {
> + if {[lindex $start_args $i] == [lindex $cont_args $i]} {
> + fail "thread $i ran (didn't run)"
> + } else {
> + pass "thread $i ran"
> + }
> +}
> +
> +# We can't change threads, unfortunately, in current GDB. Use
> +# whichever we stopped in.
> +set curthread [get_current_thread "find current thread (1)"]
> +
> +
> +
> +
> +# Test stepping without scheduler locking.
> +gdb_test "set scheduler-locking off" ""
> +
> +step_ten_loops "unlocked"
> +
> +# Make sure we're still in the same thread.
> +set newthread [get_current_thread "find current thread (2)"]
> +if {$curthread == $newthread} {
> + pass "step without lock does not change thread"
> +} else {
> + fail "step without lock does not change thread (switched to thread $newthread)"
> +}
> +
> +set start_args $cont_args
> +set cont_args [get_args]
> +
> +for {set i 0} {[expr $i < 6]} {set i [expr $i + 1]} {
> + if {[lindex $start_args $i] == [lindex $cont_args $i]} {
> + if {$i == $curthread} {
> + fail "current thread stepped (didn't run)"
> + } else {
> + fail "other thread $i ran (didn't run) (1)"
> + }
> + } else {
> + if {$i == $curthread} {
> + if {[lindex $start_args $i] == [expr [lindex $cont_args $i] - 10]} {
> + pass "current thread stepped"
> + } else {
> + fail "current thread stepped (wrong amount)"
> + }
> + } else {
> + pass "other thread $i ran (1)"
> + }
> + }
> +}
> +
> +# Test continue with scheduler locking
> +gdb_test "set scheduler-locking on" ""
> +
> +my_continue "with lock"
> +
> +# Make sure we're still in the same thread.
> +set newthread [get_current_thread "find current thread (3)"]
> +if {$curthread == $newthread} {
> + pass "continue with lock does not change thread"
> +} else {
> + fail "continue with lock does not change thread (switched to thread $newthread)"
> +}
> +
> +set start_args $cont_args
> +set cont_args [get_args]
> +
> +for {set i 0} {[expr $i < 6]} {set i [expr $i + 1]} {
> + if {[lindex $start_args $i] == [lindex $cont_args $i]} {
> + if {$i == $curthread} {
> + fail "current thread ran (didn't run)"
> + } else {
> + pass "other thread $i didn't run"
> + }
> + } else {
> + if {$i == $curthread} {
> + pass "current thread ran"
> + } else {
> + fail "other thread $i didn't run (ran)"
> + }
> + }
> +}
> +
> +# Test stepping with scheduler locking
> +step_ten_loops "locked"
> +
> +# Make sure we're still in the same thread.
> +set newthread [get_current_thread "find current thread (2)"]
> +if {$curthread == $newthread} {
> + pass "step with lock does not change thread"
> +} else {
> + fail "step with lock does not change thread (switched to thread $newthread)"
> +}
> +
> +set start_args $cont_args
> +set cont_args [get_args]
> +
> +for {set i 0} {[expr $i < 6]} {set i [expr $i + 1]} {
> + if {[lindex $start_args $i] == [lindex $cont_args $i]} {
> + if {$i == $curthread} {
> + fail "current thread stepped locked (didn't run)"
> + } else {
> + pass "other thread $i didn't run (stepping)"
> + }
> + } else {
> + if {$i == $curthread} {
> + if {[lindex $start_args $i] == [expr [lindex $cont_args $i] - 10]} {
> + pass "current thread stepped locked"
> + } else {
> + fail "current thread stepped locked (wrong amount)"
> + }
> + } else {
> + fail "other thread $i didn't run (stepping) (ran)"
> + }
> + }
> +}
> +
> +return 0