Created
January 17, 2013 10:42
-
-
Save epinna/4555156 to your computer and use it in GitHub Desktop.
Slight LTP mallocstress.c modification to distribute stressing threads between CPUs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******************************************************************************/ | |
/* */ | |
/* 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:[email protected] */ | |
/* */ | |
/* 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 ([email protected]) */ | |
/* - Fixed crash on > 30 threads */ | |
/* - Cleaned up, fixed compiler warnings */ | |
/* - Removed mallocs that could fail */ | |
/* - Note that pthread_create fails with EINTR */ | |
/* */ | |
/* Gen - 15 - 2012 Emilio Pinna ([email protected]) */ | |
/* - Added thread dispatch between CPUs */ | |
/* */ | |
/* 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. */ | |
/******************************************************************************/ | |
#define _GNU_SOURCE /* See feature_test_macros(7) */ | |
#include <sched.h> | |
#include <stdio.h> | |
#include <pthread.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <math.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. */ | |
#define CPUNUM 2 /* default number of available CPUs. */ | |
#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 cpu) /* CPU number to set affinity with */ | |
{ | |
int loop; | |
const int MAXPTRS = 50; /* only 42 or so get used on 32 bit machine */ | |
pthread_t thread; | |
cpu_set_t cpuset; | |
int s; | |
dprt(("pid[%d]: allocate_free: repeat %d, scheme %d\n", getpid(), repeat, scheme)); | |
// Fill cpuset | |
CPU_ZERO(&cpuset); | |
CPU_SET(cpu, &cpuset); | |
// Get current thread ID | |
thread = pthread_self(); | |
// Set CPU affinity | |
s = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset); | |
if (s != 0) { | |
fprintf(stderr, "pid[%d]: failed pthread_setaffinity_np\n", getpid()); | |
return 1; | |
} | |
#ifdef DEBUG | |
// Get CPU affinity | |
s = pthread_getaffinity_np(thread, sizeof(cpu_set_t), &cpuset); | |
if (s != 0) { | |
fprintf(stderr, "pid[%d]: failed pthread_getaffinity_np\n", getpid()); | |
return 1; | |
} | |
int j; | |
dprt(("pthread_getaffinity_np() contains:\n")); | |
for (j = 0; j < CPU_SETSIZE; j++) | |
if (CPU_ISSET(j, &cpuset)) | |
dprt((" CPU %d\n", j)); | |
#endif | |
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, (unsigned int)size)); | |
/* Malloc the next block */ | |
ptrs[num_alloc] = (long *)malloc(size); | |
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, ((uintptr_t)threadnum) % CPUNUM); | |
fprintf(stdout, | |
"Thread [%d] on CPU [%lu]: allocate_free() returned %d, %s. Thread exiting.\n", | |
(int)(uintptr_t)threadnum, ((uintptr_t)threadnum) % 2, 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)); | |
thrdid = malloc(sizeof(pthread_t) * num_thrd); | |
if (thrdid == NULL) | |
{ | |
perror("main(): allocating space for thrdid[] malloc()"); | |
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); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment