Home | History | Annotate | Download | only in mtest07
      1 /******************************************************************************/
      2 /*									      */
      3 /* Copyright (c) International Business Machines  Corp., 2001		      */
      4 /*									      */
      5 /* This program is free software;  you can redistribute it and/or modify      */
      6 /* it under the terms of the GNU General Public License as published by       */
      7 /* the Free Software Foundation; either version 2 of the License, or          */
      8 /* (at your option) any later version.					      */
      9 /*									      */
     10 /* This program is distributed in the hope that it will be useful,	      */
     11 /* but WITHOUT ANY WARRANTY;  without even the implied warranty of	      */
     12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See	              */
     13 /* the GNU General Public License for more details.			      */
     14 /*									      */
     15 /* You should have received a copy of the GNU General Public License	      */
     16 /* along with this program;  if not, write to the Free Software		      */
     17 /* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA    */
     18 /*									      */
     19 /******************************************************************************/
     20 
     21 /******************************************************************************/
     22 /*                                                                            */
     23 /* History:     Nov - 04 - 2001 Created - Manoj Iyer, IBM Austin TX.          */
     24 /*                               email:manjo (at) austin.ibm.com                   */
     25 /*                                                                            */
     26 /*		Nov - 06 - 2001 Modified - Manoj Iyer, IBM Austin TX.         */
     27 /*				- added function alloc_mem()                  */
     28 /*    								              */
     29 /*		Nov - 08 - 2001 Modified - Manoj Iyer, IBM Austin TX.         */
     30 /*				- added logic to allocate memory in the size  */
     31 /*				  of fibanocci numbers.                       */
     32 /*				- fixed segmetation fault.                    */
     33 /*									      */
     34 /*		Nov - 09 - 2001 Modified - Manoj Iyer, IBM Austin TX.         */
     35 /*				- separated alocation logic to allocate_free()*/
     36 /*				  function.                                   */
     37 /*				- introduced logic to randomly pick allocation*/
     38 /*				  scheme. size = fibannoci number, pow of 2 or*/
     39 /*				  power of 3.                                 */
     40 /*				- changed comments.                           */
     41 /*				- Added test to LTP.                          */
     42 /*                                                                            */
     43 /*		Nov - 09 - 2001 Modified - Manoj Iyer,IBM Austin TX.	      */
     44 /*				- Removed compile errors.                     */
     45 /*				- too many missing arguments.                 */
     46 /*								              */
     47 /*		Nov - 19 - 2001 Modified - Manoj Iyer, IBM Austin TX.	      */
     48 /*				- fixed segmentation fault. 		      */
     49 /*				  changed variable th_status from dynamic to  */
     50 /*				  static array.			              */
     51 /*                                                                            */
     52 /*		May - 15 - 2002 Dan Kegel (dank (at) kegel.com)                    */
     53 /*		                - Fixed crash on > 30 threads                 */
     54 /*		                - Cleaned up, fixed compiler warnings         */
     55 /*		                - Removed mallocs that could fail             */
     56 /*		                - Note that pthread_create fails with EINTR   */
     57 /*                                                                            */
     58 /* File:	mallocstress.c						      */
     59 /*									      */
     60 /* Description:	This program stresses the VMM and C library                   */
     61 /*              by spawning N threads which                                   */
     62 /*              malloc blocks of increasing size until malloc returns NULL.   */
     63 /******************************************************************************/
     64 #include <stdio.h>
     65 #include <pthread.h>
     66 #include <stdlib.h>
     67 #include <unistd.h>
     68 #include <math.h>
     69 #include <assert.h>
     70 #include <errno.h>
     71 #include <stdint.h>
     72 #include <sys/types.h>
     73 #include <sys/ipc.h>
     74 #include <sys/sem.h>
     75 
     76 #define MAXL    100		/* default number of loops to do malloc and free      */
     77 #define MAXT     60		/* default number of threads to create.               */
     78 
     79 #ifdef DEBUG
     80 #define dprt(args)	printf args
     81 #else
     82 #define dprt(args)
     83 #endif
     84 
     85 #define OPT_MISSING(prog, opt)   do{\
     86 			       fprintf(stderr, "%s: option -%c ", prog, opt); \
     87                                fprintf(stderr, "requires an argument\n"); \
     88                                usage(prog); \
     89                                    } while (0)
     90 
     91 int num_loop = MAXL;		/* number of loops to perform                     */
     92 int semid;
     93 
     94 /* Define SPEW_SIGNALS to tickle thread_create bug (it fails if interrupted). */
     95 #define SPEW_SIGNALS
     96 
     97 /******************************************************************************/
     98 /*								 	      */
     99 /* Function:	my_yield						      */
    100 /*									      */
    101 /* Description:	Yield control to another thread.                              */
    102 /*              Generate a signal, too.                                       */
    103 /*									      */
    104 /******************************************************************************/
    105 static void my_yield()
    106 {
    107 #ifdef SPEW_SIGNALS
    108 	/* usleep just happens to use signals in glibc at moment.
    109 	 * This is good because it allows us to test whether pthread_create
    110 	 * improperly returns EINTR (which would violate SUSv3)
    111 	 */
    112 	usleep(0);
    113 #else
    114 	/* If you want this test to pass, don't define SPEW_SIGNALS,
    115 	 * as pthread_create is broken at moment, and fails if interrupted
    116 	 */
    117 	static const struct timespec t0 = { 0, 0 };
    118 	nanosleep(&t0, NULL);
    119 #endif
    120 }
    121 
    122 /******************************************************************************/
    123 /*								 	      */
    124 /* Function:	usage							      */
    125 /*									      */
    126 /* Description:	Print the usage message.				      */
    127 /*									      */
    128 /* Input:	char *progname - name of this program                         */
    129 /*									      */
    130 /* Return:	exits with -1						      */
    131 /*									      */
    132 /******************************************************************************/
    133 static void usage(char *progname)
    134 {				/* name of this program                       */
    135 	fprintf(stderr,
    136 		"Usage: %s -d NUMDIR -f NUMFILES -h -t NUMTHRD\n"
    137 		"\t -h Help!\n"
    138 		"\t -l Number of loops:               Default: 1000\n"
    139 		"\t -t Number of threads to generate: Default: 30\n", progname);
    140 	exit(-1);
    141 }
    142 
    143 /******************************************************************************/
    144 /* Function:	allocate_free				                      */
    145 /*								              */
    146 /* Description:	This function does the allocation and free by calling malloc  */
    147 /*		and free fuctions. The size of the memory to be malloced is   */
    148 /*		determined by the caller of this function. The size can be    */
    149 /*		a number from the fibannoaci series, power of 2 or 3 or 5     */
    150 /*									      */
    151 /* Input:	int repeat - number of times the alloc/free is repeated.      */
    152 /*		int scheme  - 0 to 3; selects how fast memory size grows      */
    153 /*								              */
    154 /* Return:	1 on failure						      */
    155 /*		0 on success						      */
    156 /******************************************************************************/
    157 int allocate_free(int repeat,	/* number of times to repeat allocate/free    */
    158 		  int scheme)
    159 {				/* how fast to increase block size            */
    160 	int loop;
    161 	const int MAXPTRS = 50;	/* only 42 or so get used on 32 bit machine */
    162 
    163 	dprt(("pid[%d]: allocate_free: repeat %d, scheme %d\n", getpid(),
    164 	      repeat, scheme));
    165 
    166 	for (loop = 0; loop < repeat; loop++) {
    167 		size_t oldsize = 5;	/* remember size for fibannoci series     */
    168 		size_t size = sizeof(long);	/* size of next block in ptrs[]           */
    169 		long *ptrs[MAXPTRS];	/* the pointers allocated in this loop    */
    170 		int num_alloc;	/* number of elements in ptrs[] so far    */
    171 		int i;
    172 
    173 		dprt(("pid[%d]: allocate_free: loop %d of %d\n", getpid(), loop,
    174 		      repeat));
    175 
    176 		/* loop terminates in one of three ways:
    177 		 * 1. after MAXPTRS iterations
    178 		 * 2. if malloc fails
    179 		 * 3. if new size overflows
    180 		 */
    181 		for (num_alloc = 0; num_alloc < MAXPTRS; num_alloc++) {
    182 			size_t newsize = 0;
    183 
    184 			dprt(("pid[%d]: loop %d/%d; num_alloc=%d; size=%u\n",
    185 			      getpid(), loop, repeat, num_alloc, size));
    186 
    187 			/* Malloc the next block */
    188 			ptrs[num_alloc] = malloc(size);
    189 			if (ptrs[num_alloc] == NULL) {
    190 				/* terminate loop if malloc couldn't give us the memory we asked for */
    191 				break;
    192 			}
    193 			ptrs[num_alloc][0] = num_alloc;
    194 
    195 			/* Increase size according to one of four schedules. */
    196 			switch (scheme) {
    197 			case 0:
    198 				newsize = size + oldsize;
    199 				oldsize = size;
    200 				break;
    201 			case 1:
    202 				newsize = size * 2;
    203 				break;
    204 			case 2:
    205 				newsize = size * 3;
    206 				break;
    207 			case 3:
    208 				newsize = size * 5;
    209 				break;
    210 			default:
    211 				assert(0);
    212 			}
    213 			/* terminate loop on overflow */
    214 			if (newsize < size)
    215 				break;
    216 			size = newsize;
    217 
    218 			my_yield();
    219 		}
    220 
    221 		for (i = 0; i < num_alloc; i++) {
    222 			dprt(("pid[%d]: freeing ptrs[i] %p\n", getpid(),
    223 			      ptrs[i]));
    224 			if (ptrs[i][0] != i) {
    225 				fprintf(stderr,
    226 					"pid[%d]: fail: bad sentinel value\n",
    227 					getpid());
    228 				return 1;
    229 			}
    230 			free(ptrs[i]);
    231 			my_yield();
    232 		}
    233 
    234 		my_yield();
    235 	}
    236 	/* Success! */
    237 	return 0;
    238 }
    239 
    240 /******************************************************************************/
    241 /* Function:	alloc_mem				                      */
    242 /*								              */
    243 /* Description:	Decide how fast to increase block sizes, then call            */
    244 /*              allocate_free() to actually to the test.                      */
    245 /*								              */
    246 /* Input:	threadnum is the thread number, 0...N-1                       */
    247 /*              global num_loop is how many iterations to run                 */
    248 /*								              */
    249 /* Return:	pthread_exit -1	on failure				      */
    250 /*		pthread_exit  0 on success			              */
    251 /*								              */
    252 /******************************************************************************/
    253 void *alloc_mem(void *threadnum)
    254 {
    255 	struct sembuf sop[1];
    256 	sop[0].sem_num = 0;
    257 	sop[0].sem_op = 0;
    258 	sop[0].sem_flg = 0;
    259 	/* waiting for other threads starting */
    260 	if (semop(semid, sop, 1) == -1) {
    261 		if (errno != EIDRM)
    262 			perror("semop");
    263 		return (void *)-1;
    264 	}
    265 
    266 	/* thread N will use growth scheme N mod 4 */
    267 	int err = allocate_free(num_loop, ((uintptr_t) threadnum) % 4);
    268 	fprintf(stdout,
    269 		"Thread [%d]: allocate_free() returned %d, %s.  Thread exiting.\n",
    270 		(int)(uintptr_t) threadnum, err,
    271 		(err ? "failed" : "succeeded"));
    272 	return (void *)(uintptr_t) (err ? -1 : 0);
    273 }
    274 
    275 /******************************************************************************/
    276 /*								 	      */
    277 /* Function:	main							      */
    278 /*									      */
    279 /* Description:	This is the entry point to the program. This function will    */
    280 /*		parse the input arguments and set the values accordingly. If  */
    281 /*		no arguments (or desired) are provided default values are used*/
    282 /*		refer the usage function for the arguments that this program  */
    283 /*		takes. It also creates the threads which do most of the dirty */
    284 /*		work. If the threads exits with a value '0' the program exits */
    285 /*		with success '0' else it exits with failure '-1'.             */
    286 /*									      */
    287 /* Return:	-1 on failure						      */
    288 /*		 0 on success						      */
    289 /*									      */
    290 /******************************************************************************/
    291 int main(int argc,		/* number of input parameters                 */
    292 	 char **argv)
    293 {				/* pointer to the command line arguments.     */
    294 	int c;			/* command line options                       */
    295 	int num_thrd = MAXT;	/* number of threads to create                */
    296 	int thrd_ndx;		/* index into the array of thread ids         */
    297 	pthread_t *thrdid;	/* the threads                                */
    298 	extern int optopt;	/* options to the program                     */
    299 	struct sembuf sop[1];
    300 	int ret = 0;
    301 
    302 	while ((c = getopt(argc, argv, "hl:t:")) != -1) {
    303 		switch (c) {
    304 		case 'h':
    305 			usage(argv[0]);
    306 			break;
    307 		case 'l':
    308 			if ((num_loop = atoi(optarg)) == 0)
    309 				OPT_MISSING(argv[0], optopt);
    310 			else if (num_loop < 1) {
    311 				fprintf(stdout,
    312 					"WARNING: bad argument. Using default\n");
    313 				num_loop = MAXL;
    314 			}
    315 			break;
    316 		case 't':
    317 			if ((num_thrd = atoi(optarg)) == 0)
    318 				OPT_MISSING(argv[0], optopt);
    319 			else if (num_thrd < 1) {
    320 				fprintf(stdout,
    321 					"WARNING: bad argument. Using default\n");
    322 				num_thrd = MAXT;
    323 			}
    324 			break;
    325 		default:
    326 			usage(argv[0]);
    327 			break;
    328 		}
    329 	}
    330 
    331 	dprt(("number of times to loop in the thread = %d\n", num_loop));
    332 
    333 	thrdid = malloc(sizeof(pthread_t) * num_thrd);
    334 	if (thrdid == NULL) {
    335 		perror("main(): allocating space for thrdid[] malloc()");
    336 		return 1;
    337 	}
    338 
    339 	semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
    340 	if (semid < 0) {
    341 		perror("Semaphore creation failed  Reason:");
    342 	}
    343 
    344 	sop[0].sem_num = 0;
    345 	sop[0].sem_op = 1;
    346 	sop[0].sem_flg = 0;
    347 	if (semop(semid, sop, 1) == -1) {
    348 		perror("semop");
    349 		ret = -1;
    350 		goto out;
    351 	}
    352 
    353 	for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx++) {
    354 		if (pthread_create(&thrdid[thrd_ndx], NULL, alloc_mem,
    355 				   (void *)(uintptr_t) thrd_ndx)) {
    356 			int err = errno;
    357 			if (err == EINTR) {
    358 				fprintf(stderr,
    359 					"main(): pthread_create failed with EINTR!\n");
    360 				ret = -1;
    361 				goto out;
    362 			}
    363 			perror("main(): pthread_create()");
    364 			ret = -11;
    365 			goto out;
    366 		}
    367 	}
    368 	my_yield();
    369 
    370 	sop[0].sem_op = -1;
    371 	if (semop(semid, sop, 1) == -1) {
    372 		perror("semop");
    373 		ret = -1;
    374 		goto out;
    375 	}
    376 
    377 	for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx++) {
    378 		void *th_status;	/* exit status of LWP */
    379 		if (pthread_join(thrdid[thrd_ndx], &th_status) != 0) {
    380 			perror("main(): pthread_join()");
    381 			ret = -1;
    382 			goto out;
    383 		} else {
    384 			if ((intptr_t) th_status != 0) {
    385 				fprintf(stderr,
    386 					"main(): thread [%d] - exited with errors\n",
    387 					thrd_ndx);
    388 				ret = -1;
    389 				goto out;
    390 			}
    391 			dprt(("main(): thread [%d]: exited without errors\n",
    392 			      thrd_ndx));
    393 		}
    394 		my_yield();
    395 	}
    396 	printf("main(): test passed.\n");
    397 out:
    398 	if (semctl(semid, 0, IPC_RMID) == -1) {
    399 		perror("semctl\n");
    400 		ret = -1;
    401 	}
    402 	if (thrdid) {
    403 		free(thrdid);
    404 		thrdid = NULL;
    405 	}
    406 	exit(ret);
    407 }
    408