[RFA/RFC] thread local storage tests

Elena Zannoni
2003-02-08T00:04:23Z

I am posting two tests for thread local storage, they work with the
latest and greatest combination of gcc, binutils, glibc.

On my old system (stock RH 7.2) they produce 2 unsupported results.

I suspect we'll have to go through a few iterations to get all the
possible error messages completely accounted for.

Hopefully somebody will be willing to give them a spin, and see what
happens on their system.

The files tls.exp and tls.c are modified versions of old HP tests that
were in the gdb.hp directory. Turns out they worked fine for us too,
the syntax is the same. I had to clean them up quite a bit, to
simplify and limit the scope of the test to TLS. tls.c was used
also in another script to test other stuff. Maybe it's worth
resurrecting that too (we need more thread tests).

elena

* gdb.threads/tls.exp: New file. Based on gdb.hp/thr-stg.exp.
* gdb.threads/tls.c: New file. Evolved from gdb.hp/start-stop.c.
* gdb.threads/tls-shared.exp: New file.
* gdb.threads/tls-main.c: New file.
* gdb.threads/tls-shared.c: New file.

--- /dev/null Thu Aug 30 16:30:55 2001
+++ tls.exp Fri Feb 7 18:58:27 2003
@@ -0,0 +1,166 @@
+# 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
+}
+
+proc compute_expected_value {value} {
+ set expected_value 0
+ set i 0
+ while { $i < $value} {
+ incr expected_value $i
+ incr i
+ }
+ return $expected_value
+}
+
+proc get_global_variable { } {
+ global expect_out
+ global gdb_prompt
+ global decimal
+
+ set value_of_global -1
+ send_gdb "print a_global\n"
+ gdb_expect {
+ -re ".*= ($decimal).*\r\n$gdb_prompt $" {
+ set value_of_global $expect_out(1,string)
+ pass "print a_global"
+ }
+ -re "$gdb_prompt $" {
+ fail "print a_global"
+ }
+ timeout {
+ fail "print a_global (timeout)"
+ }
+ }
+ return ${value_of_global}
+}
+
+proc check_thread_local {number} {
+ set global_variable [get_global_variable]
+ set expected_value [compute_expected_value ${global_variable}]
+ gdb_test "p a_thread_local" \
+ "= $expected_value" \
+ "${number} thread local storage"
+}
+
+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 bp at all threads"
+
+# Set a bp at the end to see if all threads are finished.
+#
+gdb_test "b [gdb_get_line_number "Dummy line"]" \
+ ".*Breakpoint 3.*tls.*" "set breakpoint at end"
+
+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 thread in spin: at first thread"
+
+check_thread_local "first"
+
+gdb_test "continue" ".*Breakpoint 2.*tls value.*" "continue to second thread"
+gdb_test "info thread" "Thread.*spin.*" \
+ "at least one thread in spin: at second thread"
+
+check_thread_local "second"
+
+gdb_test "continue" ".*Breakpoint 2.*tls value.*" "continue to third thread"
+gdb_test "info thread" ".*Thread.*spin.*" \
+ "at least one thread in spin: at third thread"
+
+check_thread_local "third"
+
+gdb_test "continue" ".*Breakpoint 3.*Dummy line.*" "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.*do_it.*$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.*" "i ad a_global"
+
+gdb_test "info address me" ".*me.*is a variable at offset.*" "i ad me"
+
+gdb_test "info address a_thread_local" \
+ ".*a_thread_local.*a thread-local variable at offset.*" \
+ "i ad a_thread_local"
+
+# Done!
+#
+gdb_exit
+
+return 0

--- /dev/null Thu Aug 30 16:30:55 2001
+++ tls.c Fri Feb 7 18:58:27 2003
@@ -0,0 +1,160 @@
+/* BeginSourceFile tls.c
+
+ This file creates and deletes threads, so that gdb can be tested on
+ thread delete. It uses thread local storage variables too.
+
+ To compile:
+
+ cc -Ae +DA1.0 -g -o start_stop -lpthread tls.c
+
+ To run:
+
+ start_stop --normal run
+ start_stop 1 --waits in each thread to keep it alive. */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <pthread.h>
+
+#define TRUE 1
+#define FALSE 0
+#define OUTER_LOOP_COUNT 3
+#define N_THREADS 3
+#define MAX_LOCAL_VAL 40
+
+/* Uncomment to turn on debugging output */
+/* #define START_DEBUG */
+
+/* True if waiting for attach.
+ */
+int wait_here;
+
+/* Thing to check for debugging purposes.
+*/
+int a_global = 0;
+
+/* Thread-local storage.
+ */
+__thread int a_thread_local;
+
+/* Check the results of thread-local storage.
+ */
+int thread_local_val[ N_THREADS ];
+
+/* Routine for each thread to run, does nothing.
+ */
+void *spin( vp )
+ void * vp;
+{
+ int me = (int) vp;
+ int i;
+
+#ifdef START_DEBUG
+ printf( "== In thread %d\n", me );
+#endif
+
+ a_global++;
+
+ a_thread_local = 0;
+ for( i = 0; i < a_global; i++ ) {
+ a_thread_local += i;
+ }
+
+ thread_local_val[ me ] = a_thread_local; /* here we know tls value */
+
+#ifdef START_DEBUG
+ printf( "== Thread %d, a_thread_local is %d\n",
+ (int) vp, a_thread_local );
+#endif
+
+ if( wait_here ) {
+ /* Extend life of thread to extend life of thread-local var.
+ * This makes life easier for human debugging (though you'd
+ * probably want to make the delay longer).
+ */
+ sleep( 5 );
+ }
+}
+
+void
+do_pass( pass )
+ int pass;
+{
+ int i;
+ pthread_t t[ N_THREADS ];
+ int err;
+
+ for( i = 0; i < N_THREADS; i++) {
+ thread_local_val[i] = 0;
+ }
+
+ /* 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 *)i );
+ if( err != 0 ) {
+ printf( "== Start/stop, error in thread %d create\n", i );
+ }
+ }
+
+ for( i = 0; i < N_THREADS; i++ ) {
+ err = pthread_join(t[i], NULL ); /* Line 105 */
+ if( err != 0 ) { /* Line 106 */
+ printf( "== Start/stop, error in thread %d join\n", i );
+ }
+ }
+
+ i = 10; /* Line 109. Null line for setting bpts on. */
+
+#ifdef START_DEBUG
+ for( i = 0; i < N_THREADS; i++) {
+ printf( " Local in thread %d was %d\n",
+ i, thread_local_val[i]);
+ }
+ printf( "== Pass %d done\n", pass );
+#endif
+
+}
+
+void
+do_it()
+{
+ /* We want to start some threads and then
+ * end them, and then do it again and again
+ */
+ int i;
+ int dummy;
+
+ for( i = 0; i < OUTER_LOOP_COUNT; i++ ) {
+ do_pass( i );
+ dummy = i; /* Dummy line for setting bps on */
+ }
+}
+
+main( argc, argv )
+int argc;
+char **argv;
+{
+ wait_here = FALSE;
+ if((argc > 1) && (0 != argv )) {
+ if( 1 == atoi( argv[1] ) )
+ wait_here = TRUE;
+ }
+
+#ifdef START_DEBUG
+ printf( "== Test starting\n" );
+#endif
+
+ do_it();
+
+#ifdef START_DEBUG
+ printf( "== Test done\n" );
+#endif
+
+ return(0);
+}
+
+/* EndSourceFile */

--- /dev/null Thu Aug 30 16:30:55 2001
+++ tls-shared.exp Fri Feb 7 18:58:27 2003
@@ -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"
+
+

--- /dev/null Thu Aug 30 16:30:55 2001
+++ tls-shared.c Fri Feb 7 18:58:27 2003
@@ -0,0 +1,6 @@
+__thread int i_tls = 1;
+int foo ()
+{
+ return i_tls;
+}
+

--- /dev/null Thu Aug 30 16:30:55 2001
+++ tls-main.c Fri Feb 7 18:58:26 2003
@@ -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 */
+}
+