Home | History | Annotate | Download | only in mtest06
      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:	July - 16 - 2001 Created by Manoj Iyer, IBM Austin TX.	      */
     24 /*			         email:manjo (at) austin.ibm.com		      */
     25 /*                                                                            */
     26 /*		July - 30 - 2001 Modified - Added function write_to_mem.      */
     27 /*							                      */
     28 /*              Aug  - 14 - 2001 Modified - Added code to remove the shared   */
     29 /*			         memory segment ids.                          */
     30 /*							                      */
     31 /*              Aug  - 15 - 2001 Modified - Added for loop to run the test    */
     32 /*			         repeatedly.                                  */
     33 /*                                                                            */
     34 /*		Oct  - 22 - 2001 Modified - Fixed bad code in main().         */
     35 /*				 removed stray code and options. Pthread_join */
     36 /*			         part fixed, older version was completely bad */
     37 /*                                                                            */
     38 /*		Nov  - 09 - 2001 Modified - Removed compile errors	      */
     39 /*				 - added missing header file string.h         */
     40 /*				 - removed unused variables.                  */
     41 /*				 - made read_ndx and write_ndx static variable*/
     42 /*                                                                            */
     43 /*		Nov - 91 - 2001	Modified - Changed scope of status variable   */
     44 /*				 - change the status of status variable from  */
     45 /*				   int *status to int status[1]               */
     46 /*                                                                            */
     47 /* File:	shmat1.c						      */
     48 /*			         					      */
     49 /* Description: Test the LINUX memory manager. The program is aimed at        */
     50 /*              stressing the memory manager by repeaded shmat/write/read/    */
     51 /*		shmatd of file/memory of random size (maximum 1000 * 4096)    */
     52 /*		done by multiple processes.				      */
     53 /*			         					      */
     54 /*		Create a file of random size upto 1000 times 4096. 	      */
     55 /*		process X shmats and un-shmats this file in memory.	      */
     56 /*		process Y changes content of the file to Y, ie writes to it.  */
     57 /*		process Z reads from this memory location, and varifies the   */
     58 /*		the content of the file.			              */
     59 /*			         					      */
     60 /******************************************************************************/
     61 
     62 /* Include Files							      */
     63 #include <stdio.h>		/* definitions for standard I/O               */
     64 #include <unistd.h>		/* required by usleep()                       */
     65 #include <errno.h>		/* definitions for errno                      */
     66 #include <sched.h>		/* definitions for sched_yield()              */
     67 #include <stdlib.h>		/* definitions for WEXIT macros               */
     68 #include <signal.h>		/* required by sigaction & sig handling fncs  */
     69 #include <sys/time.h>		/* definitions of settimer()                  */
     70 #include <sys/wait.h>		/* definitions of itimer structure            */
     71 #include <pthread.h>		/* definitions for pthread_create etc         */
     72 #include <setjmp.h>		/* required by setjmp longjmp                 */
     73 #include <sys/ucontext.h>	/* required by the signal handler             */
     74 #include <sys/ipc.h>		/* required by shmat shmget etc               */
     75 #include <sys/shm.h>		/* required by shmat shmget etc               */
     76 #include <string.h>		/* required by strncmp                        */
     77 
     78 /* Defines								      */
     79 #ifndef TRUE
     80 #define TRUE 1
     81 #endif
     82 #ifndef FALSE
     83 #define FALSE 0
     84 #endif
     85 #define prtln() printf(" I AM HERE ==> %s %d\n", __FILE__, __LINE__);
     86 
     87 #define STR_SHMAT  "  "
     88 #define STR_WRITER "    "
     89 #define STR_READER "      "
     90 
     91 /* Global Variables						              */
     92 void *map_address;		/* pointer to file in memory                  */
     93 sigjmp_buf jmpbuf;		/* argument to setjmp and longjmp             */
     94 int fsize;			/* size of the file to be created.            */
     95 int done_shmat = 0;		/* disallow read and writes before shmat      */
     96 
     97 /******************************************************************************/
     98 /*									      */
     99 /* Function:	sig_handler						      */
    100 /*									      */
    101 /* Description:	handle SIGALRM raised by set_timer(), SIGALRM is raised when  */
    102 /*		the timer expires. If any other signal is recived exit the    */
    103 /*		test.							      */
    104 /*									      */
    105 /* Input:	signal - signal number, intrested in SIGALRM!		      */
    106 /*									      */
    107 /* Return:	exit -1 if unexpected signal is recived			      */
    108 /*		exit 0 if SIGALRM is recieved			              */
    109 /*									      */
    110 /******************************************************************************/
    111 static void sig_handler(int signal,	/* signal number, set to handle SIGALRM       */
    112 			int code, ucontext_t *ut)
    113 {				/* contains pointer to sigcontext structure   */
    114 #ifdef __i386__
    115 	unsigned long except;	/* exception type.                            */
    116 	int ret = 0;		/* exit code from signal handler.             */
    117 	struct sigcontext *scp =	/* pointer to sigcontext structure            */
    118 	    (struct sigcontext *)&ut->uc_mcontext;
    119 #endif
    120 
    121 	if (signal == SIGALRM) {
    122 		fprintf(stdout, "Test ended, success\n");
    123 		exit(0);
    124 	}
    125 #ifdef __i386__
    126 	else {
    127 		except = scp->trapno;
    128 		fprintf(stderr, "signal caught - [%d] ", signal);
    129 	}
    130 
    131 	switch (except) {
    132 	case 10:
    133 		fprintf(stderr,
    134 			"Exception - invalid TSS, exception #[%ld]\n", except);
    135 		break;
    136 	case 11:
    137 		fprintf(stderr,
    138 			"Exception - segment not present, exception #[%ld]\n",
    139 			except);
    140 		break;
    141 	case 12:
    142 		fprintf(stderr,
    143 			"Exception - stack segment not present, exception #[%ld]\n",
    144 			except);
    145 		break;
    146 	case 13:
    147 		fprintf(stderr,
    148 			"Exception - general protection, exception #[%ld]\n",
    149 			except);
    150 		break;
    151 	case 14:
    152 		fprintf(stderr,
    153 			"Exception - page fault, exception #[%ld]\n", except);
    154 		ret = 1;
    155 		break;
    156 	default:
    157 		fprintf(stderr,
    158 			"Exception type not handled... unknown exception #[%ld]\n",
    159 			except);
    160 		break;
    161 	}
    162 
    163 	if (ret) {
    164 		if (scp->edi == (int)map_address) {
    165 			fprintf(stdout,
    166 				"page fault at [%#lx] - ignore\n", scp->edi);
    167 			siglongjmp(jmpbuf, 1);
    168 		} else if (scp->esi == (int)map_address) {
    169 			fprintf(stdout,
    170 				"page fault at [%#lx] - ignore\n", scp->esi);
    171 			siglongjmp(jmpbuf, 1);
    172 		} else {
    173 			fprintf(stderr,
    174 				"address at which sigfault occured: [%lx]\n"
    175 				"address at which sigfault occured: [%lx]\n"
    176 				"address at which memory was shmat: [%p]\n",
    177 				(unsigned long)scp->edi,
    178 				(unsigned long)scp->esi, map_address);
    179 			fprintf(stderr, "bad page fault exit test\n");
    180 			exit(-1);
    181 		}
    182 	} else
    183 		exit(-1);
    184 #else
    185 	fprintf(stderr, "caught signal %d -- exiting.\n", signal);
    186 	exit(-1);
    187 #endif
    188 }
    189 
    190 										/******************************************************************************//*                                                                            */
    191 /* Function:	usage							      */
    192 /*									      */
    193 /* Description:	Print the usage message.				      */
    194 /*									      */
    195 /* Return:	exits with -1						      */
    196 /*									      */
    197 /******************************************************************************/
    198 static void usage(char *progname)
    199 {				/* name of this program                       */
    200 	fprintf(stderr,
    201 		"Usage: %s -h -l -x\n"
    202 		"\t -h help, usage message.\n"
    203 		"\t -l number of map - write - unmap.    default: 1000\n"
    204 		"\t -x time for which test is to be run. default: 24 Hrs\n",
    205 		progname);
    206 	exit(-1);
    207 }
    208 
    209 /******************************************************************************/
    210 /*									      */
    211 /* Function:	shmat_shmdt						      */
    212 /*									      */
    213 /* Description:	Thread X function.					      */
    214 /*		shmat a random size file and shm-detach this file, this is    */
    215 /*		done for user defined number of times.			      */
    216 /*									      */
    217 /* Input:	arg[0]		   number of times shmat shmdt is done        */
    218 /*									      */
    219 /* Return:	-1 on error.				                      */
    220 /*               0 on errorless completion of the loop.                       */
    221 /*									      */
    222 /******************************************************************************/
    223 void *shmat_shmdt(void *args)
    224 {				/* arguments to the thread X function.          */
    225 	int shm_ndx = 0;	/* index to number of shmat/shmdt             */
    226 	key_t shmkey = 0;	/* IPC_PRIVATE (key for shmget)               */
    227 	int shmid;		/* shared memory id                           */
    228 	long *locargs =		/* local pointer to arguments                 */
    229 	    (long *)args;
    230 
    231 	while (shm_ndx++ < (int)locargs[0]) {
    232 		/* put the reader and writer threads to sleep                         */
    233 		done_shmat = 0;
    234 
    235 		/* generate a random size, we will ask for this amount of shared mem  */
    236 		srand(time(NULL) % 100);
    237 		fsize = (1 + (int)(1000.0 * rand() / (RAND_MAX + 1.0))) * 4096;
    238 
    239 		if ((shmid = shmget(shmkey, fsize, IPC_CREAT | 0666)) == -1) {
    240 			perror("shmat_shmdt(): shmget()");
    241 			pthread_exit((void *)-1);
    242 		} else {
    243 			fprintf(stdout,
    244 				"%s[%#lx]: shmget(): success, got segment of size %d\n",
    245 				STR_SHMAT, pthread_self(), fsize);
    246 		}
    247 
    248 		if ((map_address = shmat(shmid, NULL, 0))
    249 		    == (void *)-1) {
    250 			fprintf(stderr, "shmat_shmat(): map address = %p\n",
    251 				map_address);
    252 			perror("shmat_shmdt(): shmat()");
    253 			pthread_exit((void *)-1);
    254 		} else {
    255 			/* Wake up the reader and writer threads.                             */
    256 			/* Write 'X' into map_address. We are not sure about
    257 			   reader/writer interleaving. So the reader may expect
    258 			   to find 'X' or 'Y'
    259 			 */
    260 			memset(map_address, 'X', 1);
    261 			done_shmat = 1;
    262 			usleep(0);
    263 		}
    264 
    265 		fprintf(stdout, "%s[%#lx]: Map address = %p\n",
    266 			STR_SHMAT, pthread_self(), map_address);
    267 		fprintf(stdout,
    268 			"%s[%#lx]: Num iter: [%d] Total Num Iter: [%d]\n",
    269 			STR_SHMAT, pthread_self(), shm_ndx, (int)locargs[0]);
    270 		usleep(0);
    271 		sched_yield();
    272 
    273 		/* put the threads to sleep before un-shmatting                       */
    274 		done_shmat = 0;
    275 		if (shmdt((void *)map_address) == -1) {
    276 			perror("shmat_shmdt(): shmdt()");
    277 			pthread_exit((void *)-1);
    278 		}
    279 		if (shmctl(shmid, IPC_RMID, NULL)) {
    280 			perror("shmat_shmdt(): shmctl()");
    281 			pthread_exit((void *)-1);
    282 		}
    283 	}
    284 	pthread_exit(NULL);
    285 }
    286 
    287 /******************************************************************************/
    288 /*									      */
    289 /* Function:	write_to_mem						      */
    290 /*									      */
    291 /* Description:	Thread Y function.					      */
    292 /*		Writes 'Y' to the memory location shmat by process X.         */
    293 /*									      */
    294 /* Input:	arg[0]		   number of times write is performed         */
    295 /*									      */
    296 /* Return:	-1 on error.				                      */
    297 /*               0 on errorless completion of the loop.                       */
    298 /*									      */
    299 /******************************************************************************/
    300 void *write_to_mem(void *args)
    301 {
    302 	static int write_ndx = 0;	/* index to the number of writes to perform   */
    303 	long *locargs =		/* local pointer to the arguments             */
    304 	    (long *)args;
    305 
    306 	while (write_ndx++ < (int)locargs[0]) {
    307 		/* wait for the thread to shmat, and dont sleep on the processor. */
    308 		while (!done_shmat)
    309 			usleep(0);
    310 
    311 		if (sigsetjmp(jmpbuf, 1) == 1) {
    312 			fprintf(stdout,
    313 				"page fault ocurred due a write after an shmdt from [%p]\n",
    314 				map_address);
    315 		}
    316 
    317 		fprintf(stdout,
    318 			"%s[%#lx]: write_to_mem(): memory address: [%p]\n",
    319 			STR_WRITER, pthread_self(), map_address);
    320 		memset(map_address, 'Y', 1);
    321 		usleep(1);
    322 		sched_yield();
    323 	}
    324 	pthread_exit(NULL);
    325 }
    326 
    327 /******************************************************************************/
    328 /*									      */
    329 /* Function:	read_from_mem						      */
    330 /*									      */
    331 /* Description:	Thread Z function.					      */
    332 /*		reads from the memory location shmat by process X.            */
    333 /*									      */
    334 /* Input:	arg[0]		   number of times read is performed          */
    335 /*									      */
    336 /* Return:	-1 on error.				                      */
    337 /*               0 on errorless completion of the loop.                       */
    338 /*									      */
    339 /******************************************************************************/
    340 void *read_from_mem(void *args)
    341 {
    342 	static int read_ndx = 0;	/* index to the number of writes to perform   */
    343 	long *locargs =		/* local pointer to the arguments             */
    344 	    (long *)args;
    345 
    346 	while (read_ndx++ < (int)locargs[0]) {
    347 		/* wait for the shmat to happen */
    348 		while (!done_shmat)
    349 			usleep(0);
    350 
    351 		fprintf(stdout,
    352 			"%s[%#lx]: read_from_mem():  memory address: [%p]\n",
    353 			STR_READER, pthread_self(), map_address);
    354 		if (sigsetjmp(jmpbuf, 1) == 1) {
    355 			fprintf(stdout,
    356 				"page fault ocurred due a read after an shmdt from %p\n",
    357 				map_address);
    358 		}
    359 
    360 		fprintf(stdout, "%s[%#lx]: read_mem(): content of memory: %s\n",
    361 			STR_READER, pthread_self(), (char *)map_address);
    362 
    363 		if (strncmp(map_address, "Y", 1) != 0) {
    364 			if (strncmp(map_address, "X", 1) != 0) {
    365 				pthread_exit((void *)-1);
    366 			}
    367 		}
    368 		usleep(1);
    369 		sched_yield();
    370 	}
    371 	pthread_exit(NULL);
    372 }
    373 
    374 /******************************************************************************/
    375 /*                                                                            */
    376 /* Function:    main                                                          */
    377 /*                                                                            */
    378 /* Descrption:	Create a large file of size up to a  Giga Bytes.  write to it */
    379 /*		lower case alphabet 'a'. Map the file and change the contents */
    380 /*		to 'A's (upper case alphabet), write the contents to the file,*/
    381 /*		and unmap the file from memory. Spwan a certian number of     */
    382 /*		LWP's that will do the above.                                 */
    383 /*                                                                            */
    384 /* Return:	exits with -1 on error					      */
    385 /*		exits with a 0 on success.				      */
    386 /*                                                                            */
    387 /******************************************************************************/
    388 int main(int argc,		/* number of input parameters.                        */
    389 	 char **argv)
    390 {				/* pointer to the command line arguments.       */
    391 	int c;			/* command line options                       */
    392 	int num_iter;		/* number of iteration to perform             */
    393 	int thrd_ndx;		/* index into the array of threads.           */
    394 	double exec_time;	/* period for which the test is executed      */
    395 	void *status;		/* exit status for light weight process       */
    396 	int sig_ndx;		/* index into signal handler structure.       */
    397 	pthread_t thid[1000];	/* pids of process that will map/write/unmap  */
    398 	long chld_args[3];	/* arguments to funcs execed by child process */
    399 	extern char *optarg;	/* arguments passed to each option            */
    400 	struct sigaction sigptr;	/* set up signal, for interval timer          */
    401 
    402 	static struct signal_info {
    403 		int signum;	/* signal number that hasto be handled                */
    404 		char *signame;	/* name of the signal to be handled.                  */
    405 	} sig_info[] = {
    406 		{
    407 		SIGHUP, "SIGHUP"}, {
    408 		SIGINT, "SIGINT"}, {
    409 		SIGQUIT, "SIGQUIT"}, {
    410 		SIGABRT, "SIGABRT"}, {
    411 		SIGBUS, "SIGBUS"}, {
    412 		SIGSEGV, "SIGSEGV"}, {
    413 		SIGALRM, "SIGALRM"}, {
    414 		SIGUSR1, "SIGUSR1"}, {
    415 		SIGUSR2, "SIGUSR2"}, {
    416 		-1, "ENDSIG"}
    417 	};
    418 
    419 	/* set up the default values */
    420 	num_iter = 1000;	/* repeate map - write - unmap operation 1000 times   */
    421 	exec_time = 24.0;	/* minimum time period for which to run the tests     */
    422 
    423 	while ((c = getopt(argc, argv, "h:l:x:")) != -1) {
    424 		switch (c) {
    425 		case 'h':
    426 			usage(argv[0]);
    427 			break;
    428 		case 'l':	/* number of times to loop in the thread function     */
    429 			if ((num_iter = atoi(optarg)) == 0)
    430 				num_iter = 1000;
    431 			break;
    432 		case 'x':	/* time in hrs to run this test.                      */
    433 			if ((exec_time = atof(optarg)) == 0)
    434 				exec_time = 24;
    435 			break;
    436 		default:
    437 			usage(argv[0]);
    438 			break;
    439 		}
    440 	}
    441 
    442 	fprintf(stdout,
    443 		"\n\n\nTest is set to run with the following parameters:\n"
    444 		"\tDuration of test: [%f]hrs\n"
    445 		"\tnumber of shmat  shm detach: [%d]\n", exec_time, num_iter);
    446 
    447 	/* set up signals */
    448 	sigptr.sa_handler = (void (*)(int signal))sig_handler;
    449 	sigfillset(&sigptr.sa_mask);
    450 	sigptr.sa_flags = SA_SIGINFO;
    451 	for (sig_ndx = 0; sig_info[sig_ndx].signum != -1; sig_ndx++) {
    452 		sigaddset(&sigptr.sa_mask, sig_info[sig_ndx].signum);
    453 		if (sigaction(sig_info[sig_ndx].signum, &sigptr,
    454 			      NULL) == -1) {
    455 			perror("man(): sigaction()");
    456 			fprintf(stderr,
    457 				"could not set handler for SIGALRM, errno = %d\n",
    458 				errno);
    459 			exit(-1);
    460 		}
    461 	}
    462 
    463 	chld_args[0] = num_iter;
    464 	alarm(exec_time * 3600.00);
    465 
    466 	for (;;) {
    467 		/* create 3 threads */
    468 		if (pthread_create(&thid[0], NULL, shmat_shmdt, chld_args)) {
    469 			perror("main(): pthread_create()");
    470 			exit(-1);
    471 		} else {
    472 			fprintf(stdout,
    473 				"created thread id[%#lx], execs fn shmat_shmdt()\n",
    474 				thid[0]);
    475 		}
    476 		sched_yield();
    477 
    478 		if (pthread_create(&thid[1], NULL, write_to_mem, chld_args)) {
    479 			perror("main(): pthread_create()");
    480 			exit(-1);
    481 		} else {
    482 			fprintf(stdout,
    483 				"created thread id[%#lx], execs fn write_to_mem()\n",
    484 				thid[1]);
    485 		}
    486 		sched_yield();
    487 
    488 		if (pthread_create(&thid[2], NULL, read_from_mem, chld_args)) {
    489 			perror("main(): pthread_create()");
    490 			exit(-1);
    491 		} else {
    492 			fprintf(stdout,
    493 				"created thread id[%#lx], execs fn read_from_mem()\n",
    494 				thid[2]);
    495 		}
    496 		sched_yield();
    497 
    498 		/* wait for the children to terminate */
    499 		for (thrd_ndx = 0; thrd_ndx < 3; thrd_ndx++) {
    500 			if (pthread_join(thid[thrd_ndx], &status)) {
    501 				perror("main(): pthread_create()");
    502 				exit(-1);
    503 			}
    504 			if (status == (void *)-1) {
    505 				fprintf(stderr,
    506 					"thread [%#lx] - process exited with errors %ld\n",
    507 					thid[thrd_ndx], (long)status);
    508 				exit(-1);
    509 			}
    510 		}
    511 	}
    512 	fprintf(stdout, "TEST PASSED\n");
    513 	return 0;
    514 }
    515