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@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 */ 92void *map_address; /* pointer to file in memory */ 93sigjmp_buf jmpbuf; /* argument to setjmp and longjmp */ 94int fsize; /* size of the file to be created. */ 95int 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/******************************************************************************/ 111static 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/******************************************************************************/ 198static 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/******************************************************************************/ 223void *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/******************************************************************************/ 300void *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/******************************************************************************/ 340void *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/******************************************************************************/ 388int 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