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  *  FILE		: mtest01.c
22  *  DESCRIPTION : mallocs memory <chunksize> at a time until malloc fails.
23  *  HISTORY:
24  *	04/10/2001 Paul Larson (plars@us.ibm.com)
25  *	  written
26  *	11/09/2001 Manoj Iyer (manjo@austin.ibm.com)
27  *	  Modified.
28  *	  - Removed compile warnings.
29  *	  - Added header file #include <unistd.h> definition for getopt()
30  *	05/13/2003 Robbie Williamson (robbiew@us.ibm.com)
31  *	  Modified.
32  *	  - Rewrote the test to be able to execute on large memory machines.
33  *
34  */
35 
36 #include <sys/types.h>
37 #include <sys/sysinfo.h>
38 #include <sys/wait.h>
39 #include <limits.h>
40 #include <signal.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 
45 #include "test.h"
46 
47 #define FIVE_HUNDRED_MB (unsigned long long)(500*1024*1024)
48 #define ONE_GB	(unsigned long long)(1024*1024*1024)
49 #define THREE_GB (unsigned long long)(3*ONE_GB)
50 
51 char *TCID = "mtest01";
52 int TST_TOTAL = 1;
53 static sig_atomic_t pid_count;
54 static sig_atomic_t sigchld_count;
55 static pid_t *pid_list;
56 
handler(int signo)57 static void handler(int signo)
58 {
59 	if (signo == SIGCHLD)
60 		sigchld_count++;
61 	pid_count++;
62 }
63 
cleanup(void)64 static void cleanup(void)
65 {
66 	int i = 0;
67 
68 	while (pid_list[i] > 0) {
69 		kill(pid_list[i], SIGKILL);
70 		i++;
71 	}
72 
73 	free(pid_list);
74 }
75 
main(int argc,char * argv[])76 int main(int argc, char *argv[])
77 {
78 	int c;
79 	char *mem;
80 	float percent;
81 	unsigned int maxpercent = 0, dowrite = 0, verbose = 0, j;
82 	unsigned long bytecount, alloc_bytes, max_pids;
83 	unsigned long long original_maxbytes, maxbytes = 0;
84 	unsigned long long pre_mem = 0, post_mem = 0;
85 	unsigned long long total_ram, total_free, D, C;
86 	int chunksize = 1024 * 1024;	/* one meg at a time by default */
87 	struct sysinfo sstats;
88 	int i, pid_cntr;
89 	pid_t pid;
90 	struct sigaction act;
91 
92 	act.sa_handler = handler;
93 	act.sa_flags = 0;
94 	sigemptyset(&act.sa_mask);
95 	sigaction(SIGRTMIN, &act, 0);
96 	sigaction(SIGCHLD, &act, 0);
97 
98 	while ((c = getopt(argc, argv, "c:b:p:wvh")) != -1) {
99 		switch (c) {
100 		case 'c':
101 			chunksize = atoi(optarg);
102 			break;
103 		case 'b':
104 			if (maxpercent != 0)
105 				tst_brkm(TBROK, NULL,
106 					 "ERROR: -b option cannot be used with -p "
107 					 "option at the same time");
108 			maxbytes = atoll(optarg);
109 			break;
110 		case 'p':
111 			if (maxbytes != 0)
112 				tst_brkm(TBROK, NULL,
113 					 "ERROR: -p option cannot be used with -b "
114 					 "option at the same time");
115 			maxpercent = atoi(optarg);
116 			if (maxpercent <= 0)
117 				tst_brkm(TBROK, NULL,
118 					 "ERROR: -p option requires number greater "
119 					 "than 0");
120 			if (maxpercent > 99)
121 				tst_brkm(TBROK, NULL,
122 					 "ERROR: -p option cannot be greater than "
123 					 "99");
124 			break;
125 		case 'w':
126 			dowrite = 1;
127 			break;
128 		case 'v':
129 			verbose = 1;
130 			break;
131 		case 'h':
132 		default:
133 			printf
134 			    ("usage: %s [-c <bytes>] [-b <bytes>|-p <percent>] [-v]\n",
135 			     argv[0]);
136 			printf
137 			    ("\t-c <num>\tsize of chunk in bytes to malloc on each pass\n");
138 			printf
139 			    ("\t-b <bytes>\tmaximum number of bytes to allocate before stopping\n");
140 			printf
141 			    ("\t-p <bytes>\tpercent of total memory used at which the program stops\n");
142 			printf
143 			    ("\t-w\t\twrite to the memory after allocating\n");
144 			printf("\t-v\t\tverbose\n");
145 			printf("\t-h\t\tdisplay usage\n");
146 			exit(1);
147 		}
148 	}
149 
150 	sysinfo(&sstats);
151 	total_ram = sstats.totalram + sstats.totalswap;
152 	total_free = sstats.freeram + sstats.freeswap;
153 	/* Total Free Pre-Test RAM */
154 	pre_mem = sstats.mem_unit * total_free;
155 	max_pids = total_ram / (unsigned long)FIVE_HUNDRED_MB + 1;
156 
157 	if ((pid_list = malloc(max_pids * sizeof(pid_t))) == NULL)
158 		tst_brkm(TBROK | TERRNO, NULL, "malloc failed.");
159 	memset(pid_list, 0, max_pids * sizeof(pid_t));
160 
161 	/* Currently used memory */
162 	C = sstats.mem_unit * (total_ram - total_free);
163 	tst_resm(TINFO, "Total memory already used on system = %llu kbytes",
164 		 C / 1024);
165 
166 	if (maxpercent) {
167 		percent = (float)maxpercent / 100.00;
168 
169 		/* Desired memory needed to reach maxpercent */
170 		D = percent * (sstats.mem_unit * total_ram);
171 		tst_resm(TINFO,
172 			 "Total memory used needed to reach maximum = %llu kbytes",
173 			 D / 1024);
174 
175 		/* Are we already using more than maxpercent? */
176 		if (C > D) {
177 			tst_resm(TFAIL,
178 				 "More memory than the maximum amount you specified "
179 				 " is already being used");
180 			free(pid_list);
181 			tst_exit();
182 		}
183 
184 		/* set maxbytes to the extra amount we want to allocate */
185 		maxbytes = D - C;
186 		tst_resm(TINFO, "Filling up %d%% of ram which is %llu kbytes",
187 			 maxpercent, maxbytes / 1024);
188 	}
189 	original_maxbytes = maxbytes;
190 	i = 0;
191 	pid_cntr = 0;
192 	pid = fork();
193 	if (pid < 0)
194 		tst_brkm(TBROK | TERRNO, cleanup, "fork failed");
195 	if (pid != 0) {
196 		pid_cntr++;
197 		pid_list[i] = pid;
198 	}
199 
200 #if defined (_s390_)		/* s390's 31bit addressing requires smaller chunks */
201 	while (pid != 0 && maxbytes > FIVE_HUNDRED_MB) {
202 		i++;
203 		maxbytes -= FIVE_HUNDRED_MB;
204 		pid = fork();
205 		if (pid < 0)
206 			tst_brkm(TBROK | TERRNO, cleanup, "fork failed");
207 		if (pid != 0) {
208 			pid_cntr++;
209 			pid_list[i] = pid;
210 		}
211 	}
212 	if (maxbytes > FIVE_HUNDRED_MB)
213 		alloc_bytes = FIVE_HUNDRED_MB;
214 	else
215 		alloc_bytes = (unsigned long)maxbytes;
216 
217 #elif __WORDSIZE == 32
218 	while (pid != 0 && maxbytes > ONE_GB) {
219 		i++;
220 		maxbytes -= ONE_GB;
221 		pid = fork();
222 		if (pid < 0)
223 		    tst_brkm(TBROK | TERRNO, cleanup, "fork failed");
224 		if (pid != 0) {
225 			pid_cntr++;
226 			pid_list[i] = pid;
227 		}
228 	}
229 	if (maxbytes > ONE_GB)
230 		alloc_bytes = ONE_GB;
231 	else
232 		alloc_bytes = (unsigned long)maxbytes;
233 
234 #elif __WORDSIZE == 64
235 	while (pid != 0 && maxbytes > THREE_GB) {
236 		i++;
237 		maxbytes -= THREE_GB;
238 		pid = fork();
239 		if (pid < 0)
240 			tst_brkm(TBROK | TERRNO, cleanup, "fork failed");
241 		if (pid != 0) {
242 			pid_cntr++;
243 			pid_list[i] = pid;
244 		}
245 	}
246 	if (maxbytes > THREE_GB)
247 		alloc_bytes = THREE_GB;
248 	else
249 		alloc_bytes = maxbytes;
250 #endif
251 
252 	if (pid == 0) {
253 		bytecount = chunksize;
254 		while (1) {
255 			if ((mem = malloc(chunksize)) == NULL) {
256 				tst_resm(TBROK | TERRNO,
257 					 "stopped at %lu bytes", bytecount);
258 				free(pid_list);
259 				tst_exit();
260 			}
261 			if (dowrite)
262 				for (j = 0; j < chunksize; j++)
263 					*(mem + j) = 'a';
264 			if (verbose)
265 				tst_resm(TINFO,
266 					 "allocated %lu bytes chunksize is %d",
267 					 bytecount, chunksize);
268 			bytecount += chunksize;
269 			if (alloc_bytes && bytecount >= alloc_bytes)
270 				break;
271 		}
272 		if (dowrite)
273 			tst_resm(TINFO, "... %lu bytes allocated and used.",
274 				 bytecount);
275 		else
276 			tst_resm(TINFO, "... %lu bytes allocated only.",
277 				 bytecount);
278 		kill(getppid(), SIGRTMIN);
279 		while (1)
280 			sleep(1);
281 	} else {
282 		sysinfo(&sstats);
283 
284 		if (dowrite) {
285 			/* Total Free Post-Test RAM */
286 			post_mem =
287 			    (unsigned long long)sstats.mem_unit *
288 			    sstats.freeram;
289 			post_mem =
290 			    post_mem +
291 			    (unsigned long long)sstats.mem_unit *
292 			    sstats.freeswap;
293 
294 			while ((((unsigned long long)pre_mem - post_mem) <
295 				(unsigned long long)original_maxbytes) &&
296 			       pid_count < pid_cntr && !sigchld_count) {
297 				sleep(1);
298 				sysinfo(&sstats);
299 				post_mem =
300 				    (unsigned long long)sstats.mem_unit *
301 				    sstats.freeram;
302 				post_mem =
303 				    post_mem +
304 				    (unsigned long long)sstats.mem_unit *
305 				    sstats.freeswap;
306 			}
307 		}
308 
309 		if (sigchld_count) {
310 			tst_resm(TFAIL, "child process exited unexpectedly");
311 		} else if (dowrite) {
312 			tst_resm(TPASS, "%llu kbytes allocated and used.",
313 				 original_maxbytes / 1024);
314 		} else {
315 			tst_resm(TPASS, "%llu kbytes allocated only.",
316 				 original_maxbytes / 1024);
317 		}
318 
319 	}
320 	cleanup();
321 	tst_exit();
322 }
323