| /******************************************************************************/ |
| /* */ |
| /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ |
| /* */ |
| /******************************************************************************/ |
| |
| /******************************************************************************/ |
| /* */ |
| /* History: July - 16 - 2001 Created by Manoj Iyer, IBM Austin TX. */ |
| /* email:manjo@austin.ibm.com */ |
| /* */ |
| /* July - 24 - 2001 Modified. Added loop and pass arguments to */ |
| /* the thread function. Fixed usage function. */ |
| /* added option to MAP_PRIVATE or MAP_SHARED. */ |
| /* */ |
| /* Aug - 01 - 2001 Modified. Added include file signal.h */ |
| /* has defines required by signal handler. */ |
| /* */ |
| /* Oct - 25 - 2001 Modified. Fixed bug in main(). Pthread_join */ |
| /* sets the return value of the thread in thread*/ |
| /* return_status parameter. */ |
| /* Nov - 09 - 2001 Modified. Removed compile errors */ |
| /* - incomplete comment in line 301 */ |
| /* - missing argument to printf in pthread_join */ |
| /* */ |
| /* Apr - 16 - 2003 Modified - replaced tempnam() use with */ |
| /* mkstemp(). -Robbie Williamson */ |
| /* email:robbiew@us.ibm.com */ |
| /* */ |
| /* May - 12 - 2003 Modified - remove the really large files */ |
| /* when we are done with the test - Paul Larson */ |
| /* email:plars@linuxtestproject.org */ |
| /* File: mmap3.c */ |
| /* */ |
| /* Description: Test the LINUX memory manager. The program is aimed at */ |
| /* stressing the memory manager by repeaded map/write/unmap */ |
| /* of file/memory of random size (maximum 1GB) this is done by */ |
| /* multiple processes. */ |
| /* */ |
| /* Create a file of random size upto 1000 times 4096, map it, */ |
| /* change the contents of the file and unmap it. This is repeated*/ |
| /* several times for the specified number of hours by a certain. */ |
| /* number of processes. */ |
| /* */ |
| /******************************************************************************/ |
| |
| /* Include Files */ |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <sys/mman.h> |
| #include <sched.h> |
| #include <stdlib.h> |
| #include <signal.h> |
| #include <sys/time.h> |
| #include <sys/wait.h> |
| #include <pthread.h> |
| #include <signal.h> |
| #include <string.h> |
| #include "test.h" |
| |
| /* Defines */ |
| #ifndef TRUE |
| #define TRUE 1 |
| #endif |
| #ifndef FALSE |
| #define FALSE 0 |
| #endif |
| #define prtln() printf(" I AM HERE ==> %s %d\n", __FILE__, __LINE__); |
| |
| /******************************************************************************/ |
| /* */ |
| /* Function: mkfile */ |
| /* */ |
| /* Description: Create a temparory file of ramdom size. */ |
| /* */ |
| /* Input: NONE */ |
| /* */ |
| /* Output: size - size of the temp file created */ |
| /* */ |
| /* Return: int fd - file descriptor if the file was created. */ |
| /* -1 - if it failed to create. */ |
| /* */ |
| /******************************************************************************/ |
| static int mkfile(int *size) |
| { /* size of the file to be generated in GB */ |
| int fd; /* file descriptior of tmpfile */ |
| int index = 0; /* index into the file, fill it with a's */ |
| char buff[4096]; /* buffer that will be written to the file. */ |
| char template[PATH_MAX]; /* template for temp file name */ |
| |
| /* fill the buffer with a's and open a temp file */ |
| memset(buff, 'a', 4096); |
| snprintf(template, PATH_MAX, "ashfileXXXXXX"); |
| if ((fd = mkstemp(template)) == -1) { |
| perror("mkfile(): mkstemp()"); |
| return -1; |
| } |
| unlink(template); |
| |
| srand(time(NULL) % 100); |
| *size = (1 + (int)(1000.0 * rand() / (RAND_MAX + 1.0))) * 4096; |
| |
| /* fill the file with the character a */ |
| while (index < *size) { |
| index += 4096; |
| if (write(fd, buff, 4096) == -1) { |
| perror("mkfile(): write()"); |
| return -1; |
| } |
| } |
| |
| /* make sure a's are written to the file. */ |
| if (fsync(fd) == -1) { |
| perror("mkfile(): fsync()"); |
| return -1; |
| } |
| return fd; |
| } |
| |
| /******************************************************************************/ |
| /* */ |
| /* Function: sig_handler */ |
| /* */ |
| /* Description: handle SIGALRM raised by set_timer(), SIGALRM is raised when */ |
| /* the timer expires. If any other signal is recived exit the */ |
| /* test. */ |
| /* *//* Input: signal - signal number, intrested in SIGALRM! */ |
| /* */ |
| /* Return: exit 1 if unexpected signal is recived */ |
| /* exit 0 if SIGALRM is recieved */ |
| /* */ |
| /******************************************************************************/ |
| static void sig_handler(int signal) |
| { /* signal number, set to handle SIGALRM */ |
| if (signal != SIGALRM) { |
| fprintf(stderr, |
| "sig_handlder(): unexpected signal caught [%d]\n", |
| signal); |
| exit(-1); |
| } else |
| fprintf(stdout, "Test ended, success\n"); |
| exit(0); |
| } |
| |
| /******************************************************************************//* */ |
| /* Function: usage */ |
| /* */ |
| /* Description: Print the usage message. */ |
| /* */ |
| /* Return: exits with -1 */ |
| /* */ |
| /******************************************************************************/ |
| static void usage(char *progname) |
| { /* name of this program */ |
| fprintf(stderr, |
| "Usage: %s -h -l -n -p -x\n" |
| "\t -h help, usage message.\n" |
| "\t -l number of map - write - unmap. default: 1000\n" |
| "\t -n number of LWP's to create. default: 20\n" |
| "\t -p set mapping to MAP_PRIVATE. default: MAP_SHARED\n" |
| "\t -x time for which test is to be run. default: 24 Hrs\n", |
| progname); |
| exit(-1); |
| } |
| |
| /******************************************************************************/ |
| /* */ |
| /* Function: map_write_unmap */ |
| /* */ |
| /* Description: map a file write a character to it and unmap, this is done for*/ |
| /* user defined number of times. */ |
| /* */ |
| /* Input: arg[0] number of times map - write -unmap is done */ |
| /* arg[1] Map type: */ |
| /* TRUE - MAP_PRIVATE */ |
| /* FALSE - MAP_SHARED */ |
| /* */ |
| /* Return: MWU_FAIL on error. */ |
| /* MWU_SUCCESS on error less completion of the loop. */ |
| /* */ |
| /******************************************************************************/ |
| void *map_write_unmap(void *args) |
| { /* file descriptor of the file to be mapped. */ |
| int fsize; /* size of the file to be created. */ |
| int fd; /* temporary file descriptor */ |
| int mwu_ndx = 0; /* index to number of map/write/unmap */ |
| caddr_t *map_address; /* pointer to file in memory */ |
| int map_type; /* MAP_PRIVATE or MAP_SHARED */ |
| long *mwuargs = /* local pointer to arguments */ |
| (long *)args; |
| |
| while (mwu_ndx++ < (int)mwuargs[0]) { |
| if ((fd = mkfile(&fsize)) == -1) { |
| fprintf(stderr, |
| "main(): mkfile(): Failed to create temp file.\n"); |
| pthread_exit((void *)-1); |
| } |
| |
| if ((int)mwuargs[1]) |
| map_type = MAP_PRIVATE; |
| else |
| map_type = MAP_SHARED; |
| if ((map_address = |
| mmap(0, (size_t) fsize, PROT_WRITE | PROT_READ, map_type, |
| (int)fd, 0)) |
| == (caddr_t *) - 1) { |
| perror("map_write_unmap(): mmap()"); |
| pthread_exit((void *)-1); |
| } |
| |
| memset(map_address, 'A', fsize); |
| |
| fprintf(stdout, |
| "Map address = %p\nNum iter: [%d]\nTotal Num Iter: [%d]", |
| map_address, mwu_ndx, (int)mwuargs[0]); |
| usleep(1); |
| if (munmap(map_address, (size_t) fsize) == -1) { |
| perror("map_write_unmap(): mmap()"); |
| pthread_exit((void *)-1); |
| } |
| close(fd); |
| } |
| pthread_exit(NULL); |
| } |
| |
| /******************************************************************************/ |
| /* */ |
| /* Function: main */ |
| /* */ |
| /* Descrption: Create a large file of size up to a Giga Bytes. write to it */ |
| /* lower case alphabet 'a'. Map the file and change the contents */ |
| /* to 'A's (upper case alphabet), write the contents to the file,*/ |
| /* and unmap the file from memory. Spwan a certian number of */ |
| /* LWP's that will do the above. */ |
| /* */ |
| /* Return: exits with -1 on error */ |
| /* exits with a 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_iter; /* number of iteration to perform */ |
| int num_thrd; /* number of threads to create */ |
| int thrd_ndx; /* index into the array of threads. */ |
| float exec_time; /* period for which the test is executed */ |
| void *status; /* exit status for light weight process */ |
| int sig_ndx; /* index into signal handler structure. */ |
| pthread_t thid[1000]; /* pids of process that will map/write/unmap */ |
| long chld_args[3]; /* arguments to funcs execed by child process */ |
| extern char *optarg; /* arguments passed to each option */ |
| struct sigaction sigptr; /* set up signal, for interval timer */ |
| int map_private = /* if TRUE mapping is private, ie, MAP_PRIVATE */ |
| FALSE; |
| |
| static struct signal_info { |
| int signum; /* signal number that hasto be handled */ |
| char *signame; /* name of the signal to be handled. */ |
| } sig_info[] = { |
| { |
| SIGHUP, "SIGHUP"}, { |
| SIGINT, "SIGINT"}, { |
| SIGQUIT, "SIGQUIT"}, { |
| SIGABRT, "SIGABRT"}, { |
| SIGBUS, "SIGBUS"}, { |
| SIGSEGV, "SIGSEGV"}, { |
| SIGALRM, "SIGALRM"}, { |
| SIGUSR1, "SIGUSR1"}, { |
| SIGUSR2, "SIGUSR2"}, { |
| -1, "ENDSIG"} |
| }; |
| |
| /* set up the default values */ |
| num_iter = 1000; /* repeate map - write - unmap operation 1000 times */ |
| num_thrd = 40; /* number of LWP's to create */ |
| exec_time = 24; /* minimum time period for which to run the tests */ |
| |
| while ((c = getopt(argc, argv, "h:l:n:px:")) != -1) { |
| switch (c) { |
| case 'h': |
| usage(argv[0]); |
| break; |
| case 'l': |
| if ((num_iter = atoi(optarg)) == 0) |
| num_iter = 1000; |
| break; |
| case 'n': |
| if ((num_thrd = atoi(optarg)) == 0) |
| num_thrd = 20; |
| break; |
| case 'p': |
| map_private = TRUE; |
| break; |
| case 'x': |
| if ((exec_time = atof(optarg)) == 0) |
| exec_time = 24; |
| break; |
| default: |
| usage(argv[0]); |
| break; |
| } |
| } |
| |
| /* set up signals */ |
| sigptr.sa_handler = (void (*)(int signal))sig_handler; |
| sigfillset(&sigptr.sa_mask); |
| sigptr.sa_flags = SA_SIGINFO; |
| for (sig_ndx = 0; sig_info[sig_ndx].signum != -1; sig_ndx++) { |
| sigaddset(&sigptr.sa_mask, sig_info[sig_ndx].signum); |
| if (sigaction(sig_info[sig_ndx].signum, &sigptr, |
| NULL) == -1) { |
| perror("man(): sigaction()"); |
| fprintf(stderr, |
| "could not set handler for SIGALRM, errno = %d\n", |
| errno); |
| exit(-1); |
| } |
| } |
| chld_args[0] = num_iter; |
| chld_args[1] = map_private; |
| alarm(exec_time * 3600.00); |
| |
| fprintf(stdout, |
| "\n\n\nTest is set to run with the following parameters:\n" |
| "\tDuration of test: [%f]hrs\n" |
| "\tNumber of threads created: [%d]\n" |
| "\tnumber of map-write-unmaps: [%d]\n" |
| "\tmap_private?(T=1 F=0): [%d]\n\n\n\n", exec_time, num_thrd, |
| num_iter, map_private); |
| |
| for (;;) { |
| /* create num_thrd number of threads. */ |
| for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx++) { |
| if (pthread_create |
| (&thid[thrd_ndx], NULL, map_write_unmap, |
| chld_args)) { |
| perror("main(): pthread_create()"); |
| exit(-1); |
| } |
| sched_yield(); |
| } |
| |
| /* wait for the children to terminate */ |
| for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx++) { |
| if (pthread_join(thid[thrd_ndx], &status)) { |
| perror("main(): pthread_create()"); |
| exit(-1); |
| } else { |
| if (status) { |
| fprintf(stderr, |
| "thread [%d] - process exited with errors %ld\n", |
| WEXITSTATUS((long)status), |
| (long)status); |
| exit(-1); |
| } |
| } |
| } |
| } |
| exit(0); |
| } |