440 lines
15 KiB
C
440 lines
15 KiB
C
|
/******************************************************************************/
|
||
|
/* */
|
||
|
/* 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);
|
||
|
}
|