/* * * 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 */ /* * FILE : mtest01.c * DESCRIPTION : mallocs memory <chunksize> at a time until malloc fails. * HISTORY: * 04/10/2001 Paul Larson (plars@us.ibm.com) * written * 11/09/2001 Manoj Iyer (manjo@austin.ibm.com) * Modified. * - Removed compile warnings. * - Added header file #include <unistd.h> definition for getopt() * 05/13/2003 Robbie Williamson (robbiew@us.ibm.com) * Modified. * - Rewrote the test to be able to execute on large memory machines. * */ #include <sys/types.h> #include <sys/sysinfo.h> #include <sys/wait.h> #include <limits.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include "test.h" #define FIVE_HUNDRED_MB (unsigned long long)(500*1024*1024) #define ONE_GB (unsigned long long)(1024*1024*1024) #define THREE_GB (unsigned long long)(3*ONE_GB) char *TCID = "mtest01"; int TST_TOTAL = 1; static sig_atomic_t pid_count; static sig_atomic_t sigchld_count; static pid_t *pid_list; static void handler(int signo) { if (signo == SIGCHLD) sigchld_count++; pid_count++; } static void cleanup(void) { int i = 0; while (pid_list[i] > 0) { kill(pid_list[i], SIGKILL); i++; } free(pid_list); } int main(int argc, char *argv[]) { int c; char *mem; float percent; unsigned int maxpercent = 0, dowrite = 0, verbose = 0, j; unsigned long bytecount, alloc_bytes, max_pids; unsigned long long original_maxbytes, maxbytes = 0; unsigned long long pre_mem = 0, post_mem = 0; unsigned long long total_ram, total_free, D, C; int chunksize = 1024 * 1024; /* one meg at a time by default */ struct sysinfo sstats; int i, pid_cntr; pid_t pid; struct sigaction act; act.sa_handler = handler; act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGRTMIN, &act, 0); sigaction(SIGCHLD, &act, 0); while ((c = getopt(argc, argv, "c:b:p:wvh")) != -1) { switch (c) { case 'c': chunksize = atoi(optarg); break; case 'b': if (maxpercent != 0) tst_brkm(TBROK, NULL, "ERROR: -b option cannot be used with -p " "option at the same time"); maxbytes = atoll(optarg); break; case 'p': if (maxbytes != 0) tst_brkm(TBROK, NULL, "ERROR: -p option cannot be used with -b " "option at the same time"); maxpercent = atoi(optarg); if (maxpercent <= 0) tst_brkm(TBROK, NULL, "ERROR: -p option requires number greater " "than 0"); if (maxpercent > 99) tst_brkm(TBROK, NULL, "ERROR: -p option cannot be greater than " "99"); break; case 'w': dowrite = 1; break; case 'v': verbose = 1; break; case 'h': default: printf ("usage: %s [-c <bytes>] [-b <bytes>|-p <percent>] [-v]\n", argv[0]); printf ("\t-c <num>\tsize of chunk in bytes to malloc on each pass\n"); printf ("\t-b <bytes>\tmaximum number of bytes to allocate before stopping\n"); printf ("\t-p <bytes>\tpercent of total memory used at which the program stops\n"); printf ("\t-w\t\twrite to the memory after allocating\n"); printf("\t-v\t\tverbose\n"); printf("\t-h\t\tdisplay usage\n"); exit(1); } } sysinfo(&sstats); total_ram = sstats.totalram + sstats.totalswap; total_free = sstats.freeram + sstats.freeswap; /* Total Free Pre-Test RAM */ pre_mem = sstats.mem_unit * total_free; max_pids = total_ram * sstats.mem_unit / (unsigned long)FIVE_HUNDRED_MB + 10; if ((pid_list = malloc(max_pids * sizeof(pid_t))) == NULL) tst_brkm(TBROK | TERRNO, NULL, "malloc failed."); memset(pid_list, 0, max_pids * sizeof(pid_t)); /* Currently used memory */ C = sstats.mem_unit * (total_ram - total_free); tst_resm(TINFO, "Total memory already used on system = %llu kbytes", C / 1024); if (maxpercent) { percent = (float)maxpercent / 100.00; /* Desired memory needed to reach maxpercent */ D = percent * (sstats.mem_unit * total_ram); tst_resm(TINFO, "Total memory used needed to reach maximum = %llu kbytes", D / 1024); /* Are we already using more than maxpercent? */ if (C > D) { tst_resm(TFAIL, "More memory than the maximum amount you specified " " is already being used"); free(pid_list); tst_exit(); } /* set maxbytes to the extra amount we want to allocate */ maxbytes = D - C; tst_resm(TINFO, "Filling up %d%% of ram which is %llu kbytes", maxpercent, maxbytes / 1024); } original_maxbytes = maxbytes; i = 0; pid_cntr = 0; pid = fork(); if (pid < 0) tst_brkm(TBROK | TERRNO, cleanup, "fork failed"); if (pid != 0) { pid_cntr++; pid_list[i] = pid; } #if defined (_s390_) /* s390's 31bit addressing requires smaller chunks */ while (pid != 0 && maxbytes > FIVE_HUNDRED_MB) { i++; if (i >= max_pids) tst_brkm(TBROK, cleanup, "max_pids needs to be increased"); maxbytes -= FIVE_HUNDRED_MB; pid = fork(); if (pid < 0) tst_brkm(TBROK | TERRNO, cleanup, "fork failed"); if (pid != 0) { pid_cntr++; pid_list[i] = pid; } } if (maxbytes > FIVE_HUNDRED_MB) alloc_bytes = FIVE_HUNDRED_MB; else alloc_bytes = (unsigned long)maxbytes; #elif __WORDSIZE == 32 while (pid != 0 && maxbytes > ONE_GB) { i++; if (i >= max_pids) tst_brkm(TBROK, cleanup, "max_pids needs to be increased"); maxbytes -= ONE_GB; pid = fork(); if (pid < 0) tst_brkm(TBROK | TERRNO, cleanup, "fork failed"); if (pid != 0) { pid_cntr++; pid_list[i] = pid; } } if (maxbytes > ONE_GB) alloc_bytes = ONE_GB; else alloc_bytes = (unsigned long)maxbytes; #elif __WORDSIZE == 64 while (pid != 0 && maxbytes > THREE_GB) { i++; if (i >= max_pids) tst_brkm(TBROK, cleanup, "max_pids needs to be increased"); maxbytes -= THREE_GB; pid = fork(); if (pid < 0) tst_brkm(TBROK | TERRNO, cleanup, "fork failed"); if (pid != 0) { pid_cntr++; pid_list[i] = pid; } } alloc_bytes = MIN(THREE_GB, maxbytes); #endif if (pid == 0) { bytecount = chunksize; while (1) { if ((mem = malloc(chunksize)) == NULL) { tst_resm(TBROK | TERRNO, "stopped at %lu bytes", bytecount); free(pid_list); tst_exit(); } if (dowrite) for (j = 0; j < chunksize; j++) *(mem + j) = 'a'; if (verbose) tst_resm(TINFO, "allocated %lu bytes chunksize is %d", bytecount, chunksize); bytecount += chunksize; if (alloc_bytes && bytecount >= alloc_bytes) break; } if (dowrite) tst_resm(TINFO, "... %lu bytes allocated and used.", bytecount); else tst_resm(TINFO, "... %lu bytes allocated only.", bytecount); kill(getppid(), SIGRTMIN); while (1) sleep(1); } else { sysinfo(&sstats); if (dowrite) { /* Total Free Post-Test RAM */ post_mem = (unsigned long long)sstats.mem_unit * sstats.freeram; post_mem = post_mem + (unsigned long long)sstats.mem_unit * sstats.freeswap; while ((((unsigned long long)pre_mem - post_mem) < (unsigned long long)original_maxbytes) && pid_count < pid_cntr && !sigchld_count) { sleep(1); sysinfo(&sstats); post_mem = (unsigned long long)sstats.mem_unit * sstats.freeram; post_mem = post_mem + (unsigned long long)sstats.mem_unit * sstats.freeswap; } } if (sigchld_count) { tst_resm(TFAIL, "child process exited unexpectedly"); } else if (dowrite) { tst_resm(TPASS, "%llu kbytes allocated and used.", original_maxbytes / 1024); } else { tst_resm(TPASS, "%llu kbytes allocated only.", original_maxbytes / 1024); } } cleanup(); tst_exit(); }