blob: b5458944797512e870b552654ab8e43a505c5003 [file] [log] [blame]
/******************************************************************************/
/* */
/* 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);
}