glibc/tests/Regression/bz789238-FJ6-2-Bug-malloc-deadlock-in-case-of/mallocstress.c

440 lines
15 KiB
C
Raw Normal View History

/******************************************************************************/
/* */
/* Copyright (c) International Business Machines Corp., 2001 */
/* */
/* 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 */
/* */
/******************************************************************************/
/******************************************************************************/
/* */
/* History: Nov - 04 - 2001 Created - Manoj Iyer, IBM Austin TX. */
/* email:manjo@austin.ibm.com */
/* */
/* Nov - 06 - 2001 Modified - Manoj Iyer, IBM Austin TX. */
/* - added function alloc_mem() */
/* */
/* Nov - 08 - 2001 Modified - Manoj Iyer, IBM Austin TX. */
/* - added logic to allocate memory in the size */
/* of fibanocci numbers. */
/* - fixed segmetation fault. */
/* */
/* Nov - 09 - 2001 Modified - Manoj Iyer, IBM Austin TX. */
/* - separated alocation logic to allocate_free()*/
/* function. */
/* - introduced logic to randomly pick allocation*/
/* scheme. size = fibannoci number, pow of 2 or*/
/* power of 3. */
/* - changed comments. */
/* - Added test to LTP. */
/* */
/* Nov - 09 - 2001 Modified - Manoj Iyer,IBM Austin TX. */
/* - Removed compile errors. */
/* - too many missing arguments. */
/* */
/* Nov - 19 - 2001 Modified - Manoj Iyer, IBM Austin TX. */
/* - fixed segmentation fault. */
/* changed variable th_status from dynamic to */
/* static array. */
/* */
/* May - 15 - 2002 Dan Kegel (dank@kegel.com) */
/* - Fixed crash on > 30 threads */
/* - Cleaned up, fixed compiler warnings */
/* - Removed mallocs that could fail */
/* - Note that pthread_create fails with EINTR */
/* */
/* File: mallocstress.c */
/* */
/* Description: This program stresses the VMM and C library */
/* by spawning N threads which */
/* malloc blocks of increasing size until malloc returns NULL. */
/******************************************************************************/
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <malloc.h>
#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define MAXL 100 /* default number of loops to do malloc and free */
#define MAXT 60 /* default number of threads to create. */
#ifdef DEBUG
#define dprt(args) printf args
#else
#define dprt(args)
#endif
#define OPT_MISSING(prog, opt) do{\
fprintf(stderr, "%s: option -%c ", prog, opt); \
fprintf(stderr, "requires an argument\n"); \
usage(prog); \
} while (0)
int num_loop = MAXL;/* number of loops to perform */
int semid;
/* Define SPEW_SIGNALS to tickle thread_create bug (it fails if interrupted). */
#define SPEW_SIGNALS
/******************************************************************************/
/* */
/* Function: my_yield */
/* */
/* Description: Yield control to another thread. */
/* Generate a signal, too. */
/* */
/******************************************************************************/
static void
my_yield()
{
#ifdef SPEW_SIGNALS
/* usleep just happens to use signals in glibc at moment.
* This is good because it allows us to test whether pthread_create
* improperly returns EINTR (which would violate SUSv3)
*/
usleep(0);
#else
/* If you want this test to pass, don't define SPEW_SIGNALS,
* as pthread_create is broken at moment, and fails if interrupted
*/
static const struct timespec t0 = {0, 0};
nanosleep(&t0, NULL);
#endif
}
/******************************************************************************/
/* */
/* Function: usage */
/* */
/* Description: Print the usage message. */
/* */
/* Input: char *progname - name of this program */
/* */
/* Return: exits with -1 */
/* */
/******************************************************************************/
static void
usage(char *progname) /* name of this program */
{
fprintf(stderr,
"Usage: %s -d NUMDIR -f NUMFILES -h -t NUMTHRD\n"
"\t -h Help!\n"
"\t -l Number of loops: Default: 1000\n"
"\t -t Number of threads to generate: Default: 30\n",
progname);
exit(-1);
}
/******************************************************************************/
/* Function: allocate_free */
/* */
/* Description: This function does the allocation and free by calling malloc */
/* and free fuctions. The size of the memory to be malloced is */
/* determined by the caller of this function. The size can be */
/* a number from the fibannoaci series, power of 2 or 3 or 5 */
/* */
/* Input: int repeat - number of times the alloc/free is repeated. */
/* int scheme - 0 to 3; selects how fast memory size grows */
/* */
/* Return: 1 on failure */
/* 0 on success */
/******************************************************************************/
int
allocate_free(int repeat, /* number of times to repeat allocate/free */
int scheme) /* how fast to increase block size */
{
int loop;
const int MAXPTRS = 50; /* only 42 or so get used on 32 bit machine */
dprt(("pid[%d]: allocate_free: repeat %d, scheme %d\n", getpid(), repeat, scheme));
for (loop = 0; loop < repeat; loop++)
{
size_t oldsize = 5; /* remember size for fibannoci series */
size_t size = sizeof(long); /* size of next block in ptrs[] */
long *ptrs[MAXPTRS]; /* the pointers allocated in this loop */
int num_alloc; /* number of elements in ptrs[] so far */
int i;
dprt(("pid[%d]: allocate_free: loop %d of %d\n", getpid(), loop, repeat));
/* loop terminates in one of three ways:
* 1. after MAXPTRS iterations
* 2. if malloc fails
* 3. if new size overflows
*/
for (num_alloc=0; num_alloc < MAXPTRS; num_alloc++)
{
size_t newsize = 0;
dprt(("pid[%d]: loop %d/%d; num_alloc=%d; size=%u\n",
getpid(), loop, repeat, num_alloc, size));
/* Malloc the next block */
#ifdef MEMALIGN
ptrs[num_alloc] = (long *)memalign(sysconf(_SC_PAGESIZE), size);
#elif CALLOC
ptrs[num_alloc] = (long *)calloc(size, 1);
#elif VALLOC
ptrs[num_alloc] = (long *)valloc(size);
#else
ptrs[num_alloc] = (long *)malloc(size);
#endif
if (ptrs[num_alloc] == NULL)
{
/* terminate loop if malloc couldn't give us the memory we asked for */
break;
}
ptrs[num_alloc][0] = num_alloc;
/* Increase size according to one of four schedules. */
switch (scheme) {
case 0:
newsize = size + oldsize;
oldsize = size;
break;
case 1:
newsize = size * 2;
break;
case 2:
newsize = size * 3;
break;
case 3:
newsize = size * 5;
break;
default:
assert(0);
}
/* terminate loop on overflow */
if (newsize < size)
break;
size = newsize;
my_yield();
}
for (i = 0; i < num_alloc; i++)
{
dprt(("pid[%d]: freeing ptrs[i] %p\n", getpid(), ptrs[i]));
if (ptrs[i][0] != i) {
fprintf(stderr, "pid[%d]: fail: bad sentinel value\n", getpid());
return 1;
}
free(ptrs[i]);
my_yield();
}
my_yield();
}
/* Success! */
return 0;
}
/******************************************************************************/
/* Function: alloc_mem */
/* */
/* Description: Decide how fast to increase block sizes, then call */
/* allocate_free() to actually to the test. */
/* */
/* Input: threadnum is the thread number, 0...N-1 */
/* global num_loop is how many iterations to run */
/* */
/* Return: pthread_exit -1 on failure */
/* pthread_exit 0 on success */
/* */
/******************************************************************************/
void *
alloc_mem(void * threadnum)
{
struct sembuf sop[1];
sop[0].sem_num = 0;
sop[0].sem_op = 0;
sop[0].sem_flg = 0;
/* waiting for other threads starting */
if (semop(semid, sop, 1) == -1) {
if (errno != EIDRM)
perror("semop");
return (void *) -1;
}
/* thread N will use growth scheme N mod 4 */
int err = allocate_free(num_loop, ((uintptr_t)threadnum) % 4);
fprintf(stdout,
"Thread [%d]: allocate_free() returned %d, %s. Thread exiting.\n",
(int)(uintptr_t)threadnum, err, (err ? "failed" : "succeeded"));
return (void *)(uintptr_t)(err ? -1 : 0);
}
/******************************************************************************/
/* */
/* Function: main */
/* */
/* Description: This is the entry point to the program. This function will */
/* parse the input arguments and set the values accordingly. If */
/* no arguments (or desired) are provided default values are used*/
/* refer the usage function for the arguments that this program */
/* takes. It also creates the threads which do most of the dirty */
/* work. If the threads exits with a value '0' the program exits */
/* with success '0' else it exits with failure '-1'. */
/* */
/* Return: -1 on failure */
/* 0 on success */
/* */
/******************************************************************************/
int
main(int argc, /* number of input parameters */
char **argv) /* pointer to the command line arguments. */
{
int c; /* command line options */
int num_thrd = MAXT;/* number of threads to create */
int thrd_ndx; /* index into the array of thread ids */
pthread_t *thrdid; /* the threads */
extern int optopt; /* options to the program */
struct sembuf sop[1];
int ret = 0;
while ((c = getopt(argc, argv, "hl:t:")) != -1)
{
switch(c)
{
case 'h':
usage(argv[0]);
break;
case 'l':
if ((num_loop = atoi(optarg)) == 0)
OPT_MISSING(argv[0], optopt);
else
if (num_loop < 1)
{
fprintf(stdout,
"WARNING: bad argument. Using default\n");
num_loop = MAXL;
}
break;
case 't':
if ((num_thrd = atoi(optarg)) == 0)
OPT_MISSING(argv[0], optopt);
else
if (num_thrd < 1)
{
fprintf(stdout,
"WARNING: bad argument. Using default\n");
num_thrd = MAXT;
}
break;
default :
usage(argv[0]);
break;
}
}
dprt(("number of times to loop in the thread = %d\n", num_loop));
#ifdef MEMALIGN
thrdid = memalign(sysconf(_SC_PAGESIZE), sizeof(pthread_t) * num_thrd);
#elif CALLOC
thrdid = calloc(sizeof(pthread_t), num_thrd);
#elif VALLOC
thrdid = valloc(sizeof(pthread_t) * num_thrd);
#else
thrdid = malloc(sizeof(pthread_t) * num_thrd);
#endif
if (thrdid == NULL)
{
perror("main(): allocating space for thrdid[]");
return 1;
}
semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
if (semid < 0) {
perror("Semaphore creation failed Reason:");
}
sop[0].sem_num = 0;
sop[0].sem_op = 1;
sop[0].sem_flg = 0;
if (semop(semid, sop, 1) == -1) {
perror("semop");
ret = -1;
goto out;
}
for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx++)
{
if (pthread_create(&thrdid[thrd_ndx], NULL, alloc_mem,
(void *)(uintptr_t)thrd_ndx))
{
int err = errno;
if (err == EINTR) {
fprintf(stderr, "main(): pthread_create failed with EINTR!\n");
ret = -1;
goto out;
}
perror("main(): pthread_create()");
ret = -11;
goto out;
}
}
my_yield();
sop[0].sem_op = -1;
if (semop(semid, sop, 1) == -1) {
perror("semop");
ret = -1;
goto out;
}
for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx++)
{
void *th_status; /* exit status of LWP */
if (pthread_join(thrdid[thrd_ndx], &th_status) != 0)
{
perror("main(): pthread_join()");
ret = -1;
goto out;
}
else
{
if ((intptr_t)th_status != 0)
{
fprintf(stderr,
"main(): thread [%d] - exited with errors\n", thrd_ndx);
ret = -1;
goto out;
}
dprt(("main(): thread [%d]: exited without errors\n", thrd_ndx));
}
my_yield();
}
printf("main(): test passed.\n");
out:
if (semctl(semid, 0, IPC_RMID) == -1) {
perror("semctl\n");
ret = -1;
}
if (thrdid) {
free(thrdid);
thrdid = NULL;
}
exit(ret);
}