From 8bc2021fdcd6e4a91a4ae0cfe81c518112fc5c0f Mon Sep 17 00:00:00 2001 From: Elena Zannoni Date: Tue, 22 Jul 2003 15:49:45 +0000 Subject: [PATCH] 2003-07-22 Elena Zannoni * gdb.threads/tls.c : New file. * gdb.threads/tls.exp : New file. * gdb.threads/tls-main.c : New file. * gdb.threads/tls-shared.c : New file. * gdb.threads/tls-shared.exp : New file. --- gdb/testsuite/ChangeLog | 16 ++ gdb/testsuite/gdb.threads/tls-main.c | 9 + gdb/testsuite/gdb.threads/tls-shared.c | 6 + gdb/testsuite/gdb.threads/tls-shared.exp | 115 +++++++++ gdb/testsuite/gdb.threads/tls.c | 222 ++++++++++++++++++ gdb/testsuite/gdb.threads/tls.exp | 283 +++++++++++++++++++++++ 6 files changed, 651 insertions(+) create mode 100644 gdb/testsuite/gdb.threads/tls-main.c create mode 100644 gdb/testsuite/gdb.threads/tls-shared.c create mode 100644 gdb/testsuite/gdb.threads/tls-shared.exp create mode 100644 gdb/testsuite/gdb.threads/tls.c create mode 100644 gdb/testsuite/gdb.threads/tls.exp diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 7c0655b3843..835176c81dd 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2003-07-22 Elena Zannoni + + * gdb.threads/tls.c : New file. + * gdb.threads/tls.exp : New file. + * gdb.threads/tls-main.c : New file. + * gdb.threads/tls-shared.c : New file. + * gdb.threads/tls-shared.exp : New file. + 2003-07-22 Andreas Schwab * gdb.asm/powerpc.inc (gdbasm_startup): Force alignment. @@ -21,6 +29,14 @@ * gdb.c++/templates.exp (test_ptype_of_templates): Recognize expansion of size_t to 'unsigned long', not only to 'unsigned'. +2003-07-09 Elena Zannoni + + * gdb.threads/tls.c: New file. + * gdb.threads/tls.exp: New file. + * gdb.threads/tls-main.c: New file. + * gdb.threads/tls-shared.c: New file. + * gdb.threads/tls-shared.exp: New file. + 2003-07-09 Elena Zannoni * gdb.base/annota1.exp: Make sure that we properly escape the diff --git a/gdb/testsuite/gdb.threads/tls-main.c b/gdb/testsuite/gdb.threads/tls-main.c new file mode 100644 index 00000000000..eec5d50bcf4 --- /dev/null +++ b/gdb/testsuite/gdb.threads/tls-main.c @@ -0,0 +1,9 @@ +__thread int i_tls = 2; +int main () +{ + int result; + result = foo (); /* Call to foo should return 2, not 1. */ + result ++; + return 0; /* break here to check result */ +} + diff --git a/gdb/testsuite/gdb.threads/tls-shared.c b/gdb/testsuite/gdb.threads/tls-shared.c new file mode 100644 index 00000000000..d4f8e5cc6e0 --- /dev/null +++ b/gdb/testsuite/gdb.threads/tls-shared.c @@ -0,0 +1,6 @@ +__thread int i_tls = 1; +int foo () +{ + return i_tls; +} + diff --git a/gdb/testsuite/gdb.threads/tls-shared.exp b/gdb/testsuite/gdb.threads/tls-shared.exp new file mode 100644 index 00000000000..e8c536b3bd4 --- /dev/null +++ b/gdb/testsuite/gdb.threads/tls-shared.exp @@ -0,0 +1,115 @@ +# Copyright 2003 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 + +# tls-shared.exp -- Expect script to test thread local storage in gdb, with +# shared libraries. + +if $tracelevel then { + strace $tracelevel +} + +set testfile tls-main +set libfile tls-shared +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} + +remote_exec build "rm -f ${binfile}" + +# get the value of gcc_compiled +if [get_compiler_info ${binfile}] { + return -1 +} + +if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}.o" object {debug}] != "" } { + return -1 +} + +# Build the shared libraries this test case needs. +# + +if {$gcc_compiled == 0} { + if [istarget "hppa*-hp-hpux*"] then { + set additional_flags "additional_flags=+z" + } elseif { [istarget "mips-sgi-irix*"] } { + # Disable SGI compiler's implicit -Dsgi + set additional_flags "additional_flags=-Usgi" + } else { + # don't know what the compiler is... + set additional_flags "" + } +} else { + if { ([istarget "powerpc*-*-aix*"] + || [istarget "rs6000*-*-aix*"]) } { + set additional_flags "" + } else { + set additional_flags "additional_flags=-fpic" + } +} + +set additional_flags "$additional_flags -shared" +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${libfile}.c" "${objdir}/${subdir}/${libfile}.so" executable [list debug $additional_flags "incdir=${objdir}"]] != ""} { + return -1 +} + +if { ($gcc_compiled +&& ([istarget "powerpc*-*-aix*"] +|| [istarget "rs6000*-*-aix*"] )) } { + set additional_flags "additional_flags=-L${objdir}/${subdir}" +} elseif { [istarget "mips-sgi-irix*"] } { + set additional_flags "additional_flags=-rpath ${objdir}/${subdir}" +} else { + set additional_flags "" +} + +if {[gdb_compile_pthreads "${objdir}/${subdir}/${testfile}.o ${objdir}/${subdir}/${libfile}.so" "${binfile}" executable [list debug $additional_flags]] != ""} { + gdb_suppress_entire_file "Testcase compile failed, so all tests in this file will automatically fail." +} + + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +if ![runto_main] then { + fail "Can't run to main" + return 0 +} + +gdb_test "print i_tls" "2" "print thread local storage variable" + +gdb_test "ptype i_tls" "int" "ptype of thread local storage variable" + +gdb_test "info address i_tls" \ + "Symbol \\\"i_tls\\\" is a thread-local variable at offset 0 in the thread-local storage for .*tls-main.." \ + "print storage info for thread local storage variable" + +set line_number [gdb_get_line_number "break here to check result"] + +gdb_test "break $line_number" \ + "Breakpoint.*at.*file.*tls-main.c.*line ${line_number}." \ + "break at and of main" +gdb_test "continue" \ + "main .* at .*:.*return 0.*break here to check result.*" \ + "continue to break" +# This is more of a gcc/glibc test, really. +# +gdb_test "print result" "3" "print result" + + diff --git a/gdb/testsuite/gdb.threads/tls.c b/gdb/testsuite/gdb.threads/tls.c new file mode 100644 index 00000000000..c4f6a30432c --- /dev/null +++ b/gdb/testsuite/gdb.threads/tls.c @@ -0,0 +1,222 @@ +/* BeginSourceFile tls.c + + This file creates and deletes threads. It uses thread local storage + variables too. */ + +#include +#include +#include +#include +#include +#include +#include + +#define N_THREADS 3 + +/* Uncomment to turn on debugging output */ +/*#define START_DEBUG*/ + +/* Thread-local storage. */ +__thread int a_thread_local; +__thread int another_thread_local; + +/* Global variable just for info addr in gdb. */ +int a_global; + +/* Print the results of thread-local storage. */ +int thread_local_val[ N_THREADS ]; +int another_thread_local_val[ N_THREADS ]; + +/* Semaphores to make sure the threads are alive when we print the TLS + variables from gdb. */ +sem_t tell_main, tell_thread; + + +void print_error () +{ + switch (errno) + { + case EAGAIN: + fprintf (stderr, "EAGAIN\n"); + break; + case EINTR: + fprintf (stderr, "EINTR\n"); + break; + case EINVAL: + fprintf (stderr, "EINVAL\n"); + break; + case ENOSYS: + fprintf (stderr, "ENOSYS\n"); + break; + case ENOENT: + fprintf (stderr, "ENOENT\n"); + break; + case EDEADLK: + fprintf (stderr, "EDEADLK\n"); + break; + default: + fprintf (stderr, "Unknown error\n"); + break; + } +} + +/* Routine for each thread to run, does nothing. */ +void *spin( vp ) + void * vp; +{ + int me = (long) vp; + int i; + + /* Use a_global. */ + a_global++; + + a_thread_local = 0; + another_thread_local = me; + for( i = 0; i <= me; i++ ) { + a_thread_local += i; + } + + another_thread_local_val[me] = another_thread_local; + thread_local_val[ me ] = a_thread_local; /* here we know tls value */ + + if (sem_post (&tell_main) == -1) + { + fprintf (stderr, "th %d post on sem tell_main failed\n", me); + print_error (); + return; + } +#ifdef START_DEBUG + fprintf (stderr, "th %d post on tell main\n", me); +#endif + + do + { + errno = 0; +#ifdef START_DEBUG + fprintf (stderr, "th %d start wait on tell_thread\n", me); +#endif + if (sem_wait (&tell_thread) == -1) + { + if (errno != EINTR) + { + fprintf (stderr, "th %d wait on sem tell_thread failed\n", me); + print_error (); + return; + } +#ifdef START_DEBUG + fprintf (stderr, "th %d wait tell_thread got EINTR, rewaiting\n", me); +#endif + } + } + while (errno == EINTR); + +#ifdef START_DEBUG + fprintf (stderr, "th %d Wait on tell_thread\n", me); +#endif + +} + +void +do_pass() +{ + int i; + pthread_t t[ N_THREADS ]; + int err; + + for( i = 0; i < N_THREADS; i++) + { + thread_local_val[i] = 0; + another_thread_local_val[i] = 0; + } + + if (sem_init (&tell_main, 0, 0) == -1) + { + fprintf (stderr, "tell_main semaphore init failed\n"); + return; + } + + if (sem_init (&tell_thread, 0, 0) == -1) + { + fprintf (stderr, "tell_thread semaphore init failed\n"); + return; + } + + /* Start N_THREADS threads, then join them so that they are terminated. */ + for( i = 0; i < N_THREADS; i++ ) + { + err = pthread_create( &t[i], NULL, spin, (void *) (long) i ); + if( err != 0 ) { + fprintf(stderr, "Error in thread %d create\n", i ); + } + } + + for( i = 0; i < N_THREADS; i++ ) + { + do + { + errno = 0; + +#ifdef START_DEBUG + fprintf (stderr, "main %d start wait on tell_main\n", i); +#endif + if (sem_wait (&tell_main) == -1) + { + if (errno != EINTR) + { + fprintf (stderr, "main %d wait on sem tell_main failed\n", i); + print_error (); + return; + } +#ifdef START_DEBUG + fprintf (stderr, "main %d wait tell_main got EINTR, rewaiting\n", i); +#endif + } + } + while (errno == EINTR); + +#ifdef START_DEBUG + fprintf (stderr, "main %d wait on tell_main\n",i); +#endif + } + +#ifdef START_DEBUG + fprintf (stderr, "main done waiting on tell_main\n"); +#endif + + i = 10; /* Here all threads should be still alive. */ + + for( i = 0; i < N_THREADS; i++ ) + { + if (sem_post (&tell_thread) == -1) + { + fprintf (stderr, "main %d post on sem tell_thread failed\n", i); + print_error (); + return; + } +#ifdef START_DEBUG + fprintf (stderr, "main %d post on tell_thread\n", i); +#endif + } + + for( i = 0; i < N_THREADS; i++ ) + { + err = pthread_join(t[i], NULL ); + if( err != 0 ) + { + fprintf (stderr, "error in thread %d join\n", i ); + } + } + + i = 10; /* Null line for setting bpts on. */ + +} + +int +main() +{ + do_pass (); + + return 0; /* Set breakpoint here before exit. */ +} + +/* EndSourceFile */ diff --git a/gdb/testsuite/gdb.threads/tls.exp b/gdb/testsuite/gdb.threads/tls.exp new file mode 100644 index 00000000000..2fb518a1e28 --- /dev/null +++ b/gdb/testsuite/gdb.threads/tls.exp @@ -0,0 +1,283 @@ +# tls.exp -- Expect script to test thread-local storage +# Copyright (C) 1992, 2003 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 + +set testfile tls +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} + +if [istarget "*-*-linux"] then { + set target_cflags "-D_MIT_POSIX_THREADS" +} else { + set target_cflags "" +} + +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list debug "incdir=${objdir}"]] != "" } { + return -1 +} + +### Compute the value of the a_thread_local variable. +proc compute_expected_value {value} { + set expected_value 0 + set i 0 + while { $i <= $value} { + incr expected_value $i + incr i + } + return $expected_value +} + +### Get the value of the variable 'me' for the current thread. +proc get_me_variable {tnum} { + global expect_out + global gdb_prompt + global decimal + + set value_of_me -1 + send_gdb "print me\n" + gdb_expect { + -re ".*= ($decimal).*\r\n$gdb_prompt $" { + set value_of_me $expect_out(1,string) + pass "$tnum thread print me" + } + -re "$gdb_prompt $" { + fail "$tnum thread print me" + } + timeout { + fail "$tnum thread print me (timeout)" + } + } + return ${value_of_me} +} + +### Check the values of the thread local variables in the thread. +### Also check that info address print the right things. +proc check_thread_local {number} { + set me_variable [get_me_variable $number] + set expected_value [compute_expected_value ${me_variable}] + + gdb_test "p a_thread_local" \ + "= $expected_value" \ + "${number} thread local storage" + + gdb_test "p another_thread_local" \ + "= $me_variable" \ + "${number} another thread local storage" + + gdb_test "info address a_thread_local" \ + ".*a_thread_local.*a thread-local variable at offset.*" \ + "${number} info address a_thread_local" + + gdb_test "info address another_thread_local" \ + ".*another_thread_local.*a thread-local variable at offset.*" \ + "${number} info address another_thread_local" +} + +### Select a particular thread. +proc select_thread {thread} { + global gdb_prompt + + send_gdb "thread $thread\n" + gdb_expect { + -re "\\\[Switching to thread .*\\\].*\r\n$gdb_prompt $" { + pass "selected thread: $thread" + } + -re "$gdb_prompt $" { + fail "selected thread: $thread" + } + timeout { + fail "selected thread: $thread (timeout)" + } + } +} + +### Do a backtrace for the current thread, and check that the 'spin' routine +### is in it. This means we have one of the threads we created, rather +### than the main thread. Record the thread in the spin_threads +### array. Also remember the level of the 'spin' routine in the backtrace, for +### later use. +proc check_thread_stack {number spin_threads spin_threads_level} { + global gdb_prompt + global expect_out + global decimal + global hex + upvar $spin_threads tarr + upvar $spin_threads_level tarrl + + select_thread $number + send_gdb "where\n" + gdb_expect { + -re ".*(\[0-9\]+)\[ \t\]+$hex in spin \\(vp=(0x\[0-9a-f\]+).*\r\n$gdb_prompt $" { + if {[info exists tarr($number)]} { + fail "backtrace of thread number $number in spin" + } else { + pass "backtrace of thread number $number in spin" + set level $expect_out(1,string) + set tarrl($number) $level + set tarr($number) 1 + } + } + -re ".*$gdb_prompt $" { + set tarr($number) 0 + set tarrl($number) 0 + pass "backtrace of thread number $number not relevant" + } + timeout { + fail "backtrace of thread number $number (timeout)" + } + } +} + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir + +gdb_load ${binfile} +if ![runto_main] then { + fail "Can't run to main" + return 0 +} + +# Set a breakpoint at the "spin" routine to +# test the thread local's value. +# +gdb_test "b [gdb_get_line_number "here we know tls value"]" \ + ".*Breakpoint 2.*tls.*" "set breakpoint at all threads" + +# Set a bp at a point where we know all threads are alive. +# +gdb_test "b [gdb_get_line_number "still alive"]" \ + ".*Breakpoint 3.*tls.*" "set breakpoint at synch point" + +# Set a bp at the end to see if all threads are finished. +# +gdb_test "b [gdb_get_line_number "before exit"]" \ + ".*Breakpoint 4.*tls.*" "set breakpoint at exit" + +send_gdb "continue\n" +gdb_expect { + -re ".*Program exited normally.*$gdb_prompt $" { + fail "continue to first thread: program runaway" + } + -re ".*Pass 0 done.*Pass 1 done.*$gdb_prompt $" { + fail "continue to first thread: program runaway 2" + } + -re ".*Breakpoint 2.*tls value.*$gdb_prompt $" { + pass "continue to first thread: get to thread" + } + -re ".*$gdb_prompt $" { + fail "continue to first thread: no progress?" + } + timeout { fail "continue to first thread (timeout)" } +} + +gdb_test "info thread" ".*Thread.*spin.*" \ + "at least one th in spin while stopped at first th" + +check_thread_local "first" + +gdb_test "continue" ".*Breakpoint 2.*tls value.*" "continue to second thread" +gdb_test "info thread" "Thread.*spin.*" \ + "at least one th in spin while stopped at second th" + +check_thread_local "second" + +gdb_test "continue" ".*Breakpoint 2.*tls value.*" "continue to third thread" +gdb_test "info thread" ".*Thread.*spin.*" \ + "at least one th in spin while stopped at third th" + +check_thread_local "third" + +gdb_test "continue" ".*Breakpoint 3.*still alive.*" "continue to synch point" + +send_gdb "info thread\n" +gdb_expect { + -re "^info thread\[ \t\r\n\]+(\[0-9\]+) Thread.*$gdb_prompt $" { + set no_of_threads $expect_out(1,string) + pass "get number of threads" + } + -re "$gdb_prompt $" { + fail "get number of threads" + } + timeout { + fail "get number of threads (timeout)" + } +} + +array set spin_threads {} +unset spin_threads +array set spin_threads_level {} +unset spin_threads_level + +# For each thread check its backtrace to see if it is stopped at the +# spin routine. +for {set i 1} {$i <= $no_of_threads} {incr i} { + check_thread_stack $i spin_threads spin_threads_level +} + +### Loop through the threads and check the values of the tls variables. +### keep track of how many threads we find in the spin routine. +set thrs_in_spin 0 +foreach i [array names spin_threads] { + if {$spin_threads($i) == 1} { + incr thrs_in_spin + select_thread $i + set level $spin_threads_level($i) + gdb_test "up $level" ".*spin.*sem_wait.*" "thread $i up" + check_thread_local $i + } +} + +if {$thrs_in_spin == 0} { + fail "No thread backtrace reported spin (vsyscall kernel problem?)" +} + +gdb_test "continue" ".*Breakpoint 4.*before exit.*" "threads exited" + +send_gdb "info thread\n" +gdb_expect { + -re ".* 1 Thread.*2 Thread.*$gdb_prompt $" { + fail "Too many threads left at end" + } + -re ".*\\\* 1 Thread.*main.*$gdb_prompt $" { + pass "Expect only base thread at end" + } + -re ".*No stack.*$gdb_prompt $" { + fail "runaway at end" + } + -re ".*$gdb_prompt $" { + fail "mess at end" + } + timeout { fail "at end (timeout)" } +} + +# Start over and do some "info address" stuff +# +runto spin + +gdb_test "info address a_global" \ + ".*a_global.*static storage at address.*" "info address a_global" + +gdb_test "info address me" ".*me.*is a variable at offset.*" "info address me" + +# Done! +# +gdb_exit + +return 0 -- 2.30.2