[parisc-linux] pthread attributes and stack positions (gcc related?)
Carlos O'Donell
carlos@baldric.uwo.ca
Sun, 28 Sep 2003 13:42:26 -0400
--wRRV7LY7NUeQGEoC
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
I've noticed that we've regressed on a pthread test "tst-attr1" and it
seems to complain that our thread variables are stored outside the stack
that the thred "thinks" is it's stack.
The following code is a simplified case of tst-attr1.c in the glibc
testsuite. I would recommend that you look at the original code too.
The question I have is... how is this supposed to work? If anyone is
more cluefull than me on this I would appreciate a pointer in the right
direction :)
===
./test-attr
In child
&a=0xfaf00ac8, stackaddr=0x40046e60, stacksize=0xbffb91a0
./test-attr: pthread_attr_getstack returned range does not cover main's stack
In parent
&a=0xfaf00ac8, stackaddr=0x40046e60, stacksize=0xbffb91a0
./test-attr: pthread_attr_getstack returned range does not cover main's stack
===
The variable is definately on the process stack. The thread's stack
address seems to be inside the libraries 'writable' space and the stack
size is wrong (or uninitialized). Perhaps I should just be looking for
arch-dependant init code that we might be missing.
=== tst-attr1 from glibc ===
tst-attr1: pthread_attr_getstack returned range does not cover main's stack
thread stack 0x40245000-0x40a44000 (0x7ff000)
thread guardsize 4096
thread stack 0x40a45000-0x40c44000 (0x1ff000)
thread guardsize 4096
thread stack 0x40245000-0x40444000 (0x1ff000)
thread guardsize 65536
===
Attached is the Makefile to build the simplified tst-attr1.c (requires
test-skeleton.c wrapper, included aswell) or rather test-attr.c
Input and thoughts appreciated. This failure in glibc is rather
orthogonal to the release of 2.3.2, I'm still concentrating on the last
sysdep cancel failure (last bug to fix before a working release).
c.
--wRRV7LY7NUeQGEoC
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=Makefile
all: test-attr.o
gcc -lpthread -o test-attr test-attr.o
test:
./test-attr
clean:
rm -rf *.o
rm -rf test-attr
--wRRV7LY7NUeQGEoC
Content-Type: text/x-csrc; charset=us-ascii
Content-Disposition: attachment; filename="tst-attr1.c"
/* pthread_getattr_np test.
Copyright (C) 2003 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#include <errno.h>
#include <error.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stackinfo.h>
static void *
tf (void *arg)
{
pthread_attr_t a, *ap, a2;
int err;
void *result = NULL;
if (arg == NULL)
{
ap = &a2;
err = pthread_attr_init (ap);
if (err)
{
error (0, err, "pthread_attr_init failed");
return tf;
}
}
else
ap = (pthread_attr_t *) arg;
err = pthread_getattr_np (pthread_self (), &a);
if (err)
{
error (0, err, "pthread_getattr_np failed");
result = tf;
}
int detachstate1, detachstate2;
err = pthread_attr_getdetachstate (&a, &detachstate1);
if (err)
{
error (0, err, "pthread_attr_getdetachstate failed");
result = tf;
}
else
{
err = pthread_attr_getdetachstate (ap, &detachstate2);
if (err)
{
error (0, err, "pthread_attr_getdetachstate failed");
result = tf;
}
else if (detachstate1 != detachstate2)
{
error (0, 0, "detachstate differs %d != %d",
detachstate1, detachstate2);
result = tf;
}
}
void *stackaddr;
size_t stacksize;
err = pthread_attr_getstack (&a, &stackaddr, &stacksize);
if (err)
{
error (0, err, "pthread_attr_getstack failed");
result = tf;
}
else if ((void *) &a < stackaddr
|| (void *) &a >= stackaddr + stacksize)
{
error (0, 0, "pthread_attr_getstack returned range does not cover thread's stack");
result = tf;
}
else
printf ("thread stack %p-%p (0x%zx)\n", stackaddr, stackaddr + stacksize,
stacksize);
size_t guardsize1, guardsize2;
err = pthread_attr_getguardsize (&a, &guardsize1);
if (err)
{
error (0, err, "pthread_attr_getguardsize failed");
result = tf;
}
else
{
err = pthread_attr_getguardsize (ap, &guardsize2);
if (err)
{
error (0, err, "pthread_attr_getguardsize failed");
result = tf;
}
else if (guardsize1 != guardsize2)
{
error (0, 0, "guardsize differs %zd != %zd",
guardsize1, guardsize2);
result = tf;
}
else
printf ("thread guardsize %zd\n", guardsize1);
}
int scope1, scope2;
err = pthread_attr_getscope (&a, &scope1);
if (err)
{
error (0, err, "pthread_attr_getscope failed");
result = tf;
}
else
{
err = pthread_attr_getscope (ap, &scope2);
if (err)
{
error (0, err, "pthread_attr_getscope failed");
result = tf;
}
else if (scope1 != scope2)
{
error (0, 0, "scope differs %d != %d",
scope1, scope2);
result = tf;
}
}
err = pthread_attr_destroy (&a);
if (err)
{
error (0, err, "pthread_attr_destroy failed");
result = tf;
}
if (ap == &a2)
{
err = pthread_attr_destroy (ap);
if (err)
{
error (0, err, "pthread_attr_destroy failed");
result = tf;
}
}
return result;
}
static int
do_test (void)
{
int result = 0;
pthread_attr_t a;
int err = pthread_attr_init (&a);
if (err)
{
error (0, err, "pthread_attr_init failed");
result = 1;
}
err = pthread_attr_destroy (&a);
if (err)
{
error (0, err, "pthread_attr_destroy failed");
result = 1;
}
err = pthread_getattr_np (pthread_self (), &a);
if (err)
{
error (0, err, "pthread_getattr_np failed");
result = 1;
}
int detachstate;
err = pthread_attr_getdetachstate (&a, &detachstate);
if (err)
{
error (0, err, "pthread_attr_getdetachstate failed");
result = 1;
}
else if (detachstate != PTHREAD_CREATE_JOINABLE)
{
error (0, 0, "initial thread not joinable");
result = 1;
}
void *stackaddr;
size_t stacksize;
err = pthread_attr_getstack (&a, &stackaddr, &stacksize);
if (err)
{
error (0, err, "pthread_attr_getstack failed");
result = 1;
}
else if ((void *) &a < stackaddr
|| (void *) &a >= stackaddr + stacksize)
{
error (0, 0, "pthread_attr_getstack returned range does not cover main's stack");
result = 1;
}
else
printf ("initial thread stack %p-%p (0x%zx)\n", stackaddr,
stackaddr + stacksize, stacksize);
size_t guardsize;
err = pthread_attr_getguardsize (&a, &guardsize);
if (err)
{
error (0, err, "pthread_attr_getguardsize failed");
result = 1;
}
else if (guardsize != 0)
{
error (0, 0, "pthread_attr_getguardsize returned %zd != 0",
guardsize);
result = 1;
}
int scope;
err = pthread_attr_getscope (&a, &scope);
if (err)
{
error (0, err, "pthread_attr_getscope failed");
result = 1;
}
else if (scope != PTHREAD_SCOPE_SYSTEM)
{
error (0, 0, "pthread_attr_getscope returned %d != PTHREAD_SCOPE_SYSTEM",
scope);
result = 1;
}
int inheritsched;
err = pthread_attr_getinheritsched (&a, &inheritsched);
if (err)
{
error (0, err, "pthread_attr_getinheritsched failed");
result = 1;
}
else if (inheritsched != PTHREAD_INHERIT_SCHED)
{
error (0, 0, "pthread_attr_getinheritsched returned %d != PTHREAD_INHERIT_SCHED",
inheritsched);
result = 1;
}
err = pthread_attr_destroy (&a);
if (err)
{
error (0, err, "pthread_attr_destroy failed");
result = 1;
}
pthread_t th;
err = pthread_create (&th, NULL, tf, NULL);
if (err)
{
error (0, err, "pthread_create #1 failed");
result = 1;
}
else
{
void *ret;
err = pthread_join (th, &ret);
if (err)
{
error (0, err, "pthread_join #1 failed");
result = 1;
}
else if (ret != NULL)
result = 1;
}
err = pthread_attr_init (&a);
if (err)
{
error (0, err, "pthread_attr_init failed");
result = 1;
}
err = pthread_create (&th, &a, tf, &a);
if (err)
{
error (0, err, "pthread_create #2 failed");
result = 1;
}
else
{
void *ret;
err = pthread_join (th, &ret);
if (err)
{
error (0, err, "pthread_join #2 failed");
result = 1;
}
else if (ret != NULL)
result = 1;
}
err = pthread_attr_setguardsize (&a, 16 * sysconf (_SC_PAGESIZE));
if (err)
{
error (0, err, "pthread_attr_setguardsize failed");
result = 1;
}
err = pthread_create (&th, &a, tf, &a);
if (err)
{
error (0, err, "pthread_create #3 failed");
result = 1;
}
else
{
void *ret;
err = pthread_join (th, &ret);
if (err)
{
error (0, err, "pthread_join #3 failed");
result = 1;
}
else if (ret != NULL)
result = 1;
}
err = pthread_attr_destroy (&a);
if (err)
{
error (0, err, "pthread_attr_destroy failed");
result = 1;
}
return result;
}
#define TEST_FUNCTION do_test ()
#include "../test-skeleton.c"
--wRRV7LY7NUeQGEoC
Content-Type: text/x-csrc; charset=us-ascii
Content-Disposition: attachment; filename="test-attr.c"
#include <errno.h>
#include <error.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void){
pthread_attr_t a;
int result = 0;
void *stackaddr = NULL;
size_t stacksize = 0;
int err;
pid_t ret;
ret = fork();
if( ret != 0 ){
wait(NULL);
printf("In parent\n");
} else if ( ret == -1 ) {
printf("Error forking.\n");
exit(1);
} else {
printf("In child\n");
}
err = pthread_attr_init (&a);
if (err){
error(0, err, "pthread_attr_init failed");
exit(1);
}
err = pthread_getattr_np (pthread_self (), &a);
if (err){
error (0, err, "pthread_getattr_np failed");
exit(1);
}
err = pthread_attr_getstack (&a, &stackaddr, &stacksize);
printf("&a=0x%x, stackaddr=0x%x, stacksize=0x%x\n",
(unsigned int)(&a),(unsigned int)stackaddr,stacksize);
if (err){
error (0, err, "pthread_attr_getstack failed");
result = 1;
} else if ((void *) &a < stackaddr || (void *) &a >= stackaddr + stacksize){
error (0, 0, "pthread_attr_getstack returned range does not cover main's stack");
result = 1;
} else
printf ("initial thread stack %p-%p (0x%zx)\n",
stackaddr, stackaddr + stacksize, stacksize);
exit(0);
}
--wRRV7LY7NUeQGEoC
Content-Type: text/x-csrc; charset=us-ascii
Content-Disposition: attachment; filename="test-skeleton.c"
/* Skeleton for test programs.
Copyright (C) 1998, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#include <errno.h>
#include <getopt.h>
#include <search.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <time.h>
/* The test function is normally called `do_test' and it is called
with argc and argv as the arguments. We nevertheless provide the
possibility to overwrite this name. */
#ifndef TEST_FUNCTION
# define TEST_FUNCTION do_test (argc, argv)
#endif
#ifndef TEST_DATA_LIMIT
# define TEST_DATA_LIMIT (64 << 20) /* Data limit (bytes) to run with. */
#endif
#define OPT_DIRECT 1000
#define OPT_TESTDIR 1001
static struct option options[] =
{
#ifdef CMDLINE_OPTIONS
CMDLINE_OPTIONS
#endif
{ "direct", no_argument, NULL, OPT_DIRECT },
{ "test-dir", required_argument, NULL, OPT_TESTDIR },
{ NULL, 0, NULL, 0 }
};
/* PID of the test itself. */
static pid_t pid;
/* Directory to place temporary files in. */
static const char *test_dir;
/* List of temporary files. */
struct temp_name_list
{
struct qelem q;
const char *name;
} *temp_name_list;
/* Add temporary files in list. */
static void
__attribute__ ((unused))
add_temp_file (const char *name)
{
struct temp_name_list *newp
= (struct temp_name_list *) calloc (sizeof (*newp), 1);
if (newp != NULL)
{
newp->name = name;
if (temp_name_list == NULL)
temp_name_list = (struct temp_name_list *) &newp->q;
else
insque (newp, temp_name_list);
}
}
/* Delete all temporary files. */
static void
delete_temp_files (void)
{
while (temp_name_list != NULL)
{
remove (temp_name_list->name);
temp_name_list = (struct temp_name_list *) temp_name_list->q.q_forw;
}
}
/* Create a temporary file. */
static int
__attribute__ ((unused))
create_temp_file (const char *base, char **filename)
{
char *fname;
int fd;
fname = (char *) malloc (strlen (test_dir) + 1 + strlen (base)
+ sizeof ("XXXXXX"));
if (fname == NULL)
{
puts ("out of memory");
return -1;
}
strcpy (stpcpy (stpcpy (stpcpy (fname, test_dir), "/"), base), "XXXXXX");
fd = mkstemp (fname);
if (fd == -1)
{
printf ("cannot open temporary file '%s': %m\n", fname);
free (fname);
return -1;
}
add_temp_file (fname);
if (filename != NULL)
*filename = fname;
return fd;
}
/* Timeout handler. We kill the child and exit with an error. */
static void
__attribute__ ((noreturn))
timeout_handler (int sig __attribute__ ((unused)))
{
int killed;
int status;
/* Send signal. */
kill (pid, SIGKILL);
/* Wait for it to terminate. */
int i;
for (i = 0; i < 5; ++i)
{
killed = waitpid (pid, &status, WNOHANG|WUNTRACED);
if (killed != 0)
break;
/* Delay, give the system time to process the kill. If the
nanosleep() call return prematurely, all the better. We
won't restart it since this probably means the child process
finally died. */
struct timespec ts = { .tv_sec = 0, .tv_nsec = 100000000 };
nanosleep (&ts, NULL);
}
if (killed != 0 && killed != pid)
{
perror ("Failed to killed test process");
exit (1);
}
#ifdef CLEANUP_HANDLER
CLEANUP_HANDLER;
#endif
/* If we expected this signal: good! */
#ifdef EXPECTED_SIGNAL
if (EXPECTED_SIGNAL == SIGALRM)
exit (0);
#endif
if (killed == 0 || (WIFSIGNALED (status) && WTERMSIG (status) == SIGKILL))
fputs ("Timed out: killed the child process\n", stderr);
else if (WIFSTOPPED (status))
fprintf (stderr, "Timed out: the child process was %s\n",
strsignal (WSTOPSIG (status)));
else if (WIFSIGNALED (status))
fprintf (stderr, "Timed out: the child process got signal %s\n",
strsignal (WTERMSIG (status)));
else
fprintf (stderr, "Timed out: killed the child process but it exited %d\n",
WEXITSTATUS (status));
/* Exit with an error. */
exit (1);
}
/* We provide the entry point here. */
int
main (int argc, char *argv[])
{
int direct = 0; /* Directly call the test function? */
int status;
int opt;
pid_t termpid;
#ifdef STDOUT_UNBUFFERED
setbuf (stdout, NULL);
#endif
while ((opt = getopt_long (argc, argv, "+", options, NULL)) != -1)
switch (opt)
{
case '?':
exit (1);
case OPT_DIRECT:
direct = 1;
break;
case OPT_TESTDIR:
test_dir = optarg;
break;
#ifdef CMDLINE_PROCESS
CMDLINE_PROCESS
#endif
}
/* Set TMPDIR to specified test directory. */
if (test_dir != NULL)
{
setenv ("TMPDIR", test_dir, 1);
if (chdir (test_dir) < 0)
{
perror ("chdir");
exit (1);
}
}
else
{
test_dir = getenv ("TMPDIR");
if (test_dir == NULL || test_dir[0] == '\0')
test_dir = "/tmp";
}
/* Make sure we see all message, even those on stdout. */
setvbuf (stdout, NULL, _IONBF, 0);
/* make sure temporary files are deleted. */
atexit (delete_temp_files);
/* Correct for the possible parameters. */
argv[optind - 1] = argv[0];
argv += optind - 1;
argc -= optind - 1;
/* Call the initializing function, if one is available. */
#ifdef PREPARE
PREPARE (argc, argv);
#endif
/* If we are not expected to fork run the function immediately. */
if (direct)
return TEST_FUNCTION;
/* Set up the test environment:
- prevent core dumps
- set up the timer
- fork and execute the function. */
pid = fork ();
if (pid == 0)
{
/* This is the child. */
#ifdef RLIMIT_CORE
/* Try to avoid dumping core. */
struct rlimit core_limit;
core_limit.rlim_cur = 0;
core_limit.rlim_max = 0;
setrlimit (RLIMIT_CORE, &core_limit);
#endif
#ifdef RLIMIT_DATA
/* Try to avoid eating all memory if a test leaks. */
struct rlimit data_limit;
if (getrlimit (RLIMIT_DATA, &data_limit) == 0)
{
if (TEST_DATA_LIMIT == RLIM_INFINITY)
data_limit.rlim_cur = data_limit.rlim_max;
else if (data_limit.rlim_cur > (rlim_t) TEST_DATA_LIMIT)
data_limit.rlim_cur = MIN ((rlim_t) TEST_DATA_LIMIT,
data_limit.rlim_max);
if (setrlimit (RLIMIT_DATA, &data_limit) < 0)
perror ("setrlimit: RLIMIT_DATA");
}
else
perror ("getrlimit: RLIMIT_DATA");
#endif
/* We put the test process in its own pgrp so that if it bogusly
generates any job control signals, they won't hit the whole build. */
setpgid (0, 0);
/* Execute the test function and exit with the return value. */
exit (TEST_FUNCTION);
}
else if (pid < 0)
{
perror ("Cannot fork test program");
exit (1);
}
/* Set timeout. */
#ifndef TIMEOUT
/* Default timeout is two seconds. */
# define TIMEOUT 2
#endif
signal (SIGALRM, timeout_handler);
alarm (TIMEOUT);
/* Wait for the regular termination. */
termpid = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0));
if (termpid == -1)
{
printf ("Waiting for test program failed: %m\n");
exit (1);
}
if (termpid != pid)
{
printf ("Oops, wrong test program terminated: expected %ld, got %ld\n",
(long int) pid, (long int) termpid);
exit (1);
}
#ifndef EXPECTED_SIGNAL
/* We don't expect any signal. */
# define EXPECTED_SIGNAL 0
#endif
if (WTERMSIG (status) != EXPECTED_SIGNAL)
{
if (EXPECTED_SIGNAL != 0)
{
if (WTERMSIG (status) == 0)
fprintf (stderr,
"Expected signal '%s' from child, got none\n",
strsignal (EXPECTED_SIGNAL));
else
fprintf (stderr,
"Incorrect signal from child: got `%s', need `%s'\n",
strsignal (WTERMSIG (status)),
strsignal (EXPECTED_SIGNAL));
}
else
fprintf (stderr, "Didn't expect signal from child: got `%s'\n",
strsignal (WTERMSIG (status)));
exit (1);
}
/* Simply exit with the return value of the test. */
#ifndef EXPECTED_STATUS
return WEXITSTATUS (status);
#else
if (WEXITSTATUS (status) != EXPECTED_STATUS)
{
fprintf (stderr, "Expected status %d, got %d\n",
EXPECTED_STATUS, WEXITSTATUS (status));
exit (1);
}
return 0;
#endif
}
--wRRV7LY7NUeQGEoC--