1/* ************************************************************************************
2 *	mem_alloc.c
3 *	@description : This program will consume memory using sbrk() to a size where
4 *		       there is about COMMITED_AS KB left in free{swap+ram}.
5 *		       The program realized that a process can consume so much memory,
6 *		       space, so it will fork more child to consume as much as memory
7 *		       possible, aiming for final free{swap+ram} < COMMITED_AS.
8 *		       EXEPTION: If overcommit_momory is set, the program will only
9 *			         consume as much as momory as oom-killer allows, and
10 *				 will exit when then limit reached even the
11 *				 free{swap+ram} not < COMMITTED_AS KB.
12 *	@author	     : Sarunya Jimenez (sjimene@us.ibm.com)
13 * ********************************************************************************** */
14
15/*
16 * Copyright (C) 2003-2006 IBM
17 *
18 * This program is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU General Public License as
20 * published by the Free Software Foundation; either version 2 of the
21 * License, or (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful, but
24 * WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
26 * General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
31 * 02111-1307, USA.
32 */
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <unistd.h>
37#include <signal.h>
38#include <sys/types.h>
39#include <sys/sysinfo.h>
40#include <sys/mman.h>
41#include <errno.h>
42
43/////////////////////////// GLOBAL STATIC VAIRABLE FOR SIGNAL HANDLER /////////////////
44static volatile sig_atomic_t sigflag;	// set nonzero by sig handler
45static sigset_t newmask, oldmask, zeromask;
46////////////////////////////////////////////////////////////////////////////////////////
47
48//////////////////////////////// GLOBAL DEFINES ////////////////////////////////////////
49#define KB_VALUE        1024	// value in bytes -> 1024 bytes
50#define COMMITTED_AS  102400	// value in KB    -> 102400 KB -> 100MB
51#define MALLOC_SIZE   0x10000	// = 64KB... for each sbrk(MALLOC_SIZE) in
52					// malloc_data()
53					// MUST ALWAYS BE POSSITIVE VALUE
54#define PAGE_SIZE     0x400	// = 1024 KB
55/////////////////////////////////////////////////////////////////////////////////////////
56
57//////////////////////////////// GLOBAL VARIABLES ////////////////////////////////////////
58long sbrk_num;			// global sbrk_num to keep track of # of times sbrk() get called
59char *start_addr;		// heap @before a process allocate memory - get updated in eat_mem()
60char *end_addr;			// heap @after a process allocate memory  - get updated in alloc_data()
61			// and dealloc_data()
62//////////////////////////////////////////////////////////////////////////////////////////
63
64//////////////////////////////// ERROR HANDLING PRINT FUNCTIONS //////////////////////////
65/* ========================================================================================
66 *	Print linux error message, will exit the current process.
67 * ======================================================================================== */
68void unix_error(char *msg)
69{
70	printf("LINUX ERROR: %s: %s\n", msg, strerror(errno));
71	exit(0);
72}
73
74/* ========================================================================================
75 *	Print functionality-error message for user process, will not exit the current process.
76 * ======================================================================================== */
77void user_error(char *msg)
78{
79	printf("APPLICATION ERROR: %s\n", msg);
80}
81
82/////////////////////////////////////////////////////////////////////////////////////////////
83
84//////////////////////////// SIGNAL HANDLING FUNCTIONS ///////////////////////////////////////
85/* =====================================================================================
86 *	One Signal Handler for SIGUSR1 and SIGUSR2.
87 * ===================================================================================== */
88static void sig_usr(int signo)	// signal hanlder for SIGUSR1 and SIGUSR2
89{
90	sigflag = 1;
91}
92
93/* ========================================================================================
94 *	SET UP signal handler before TELL_PARENT(), WAIT_PARENT(), TELL_CHILD(), WAIT_CHILD().
95 *	- This function must be called before fork() and TELL/WAIT_PARENT/CHILD() functions.
96 * ======================================================================================== */
97void TELL_WAIT(void)
98{
99	if (signal(SIGUSR1, sig_usr) == SIG_ERR)
100		unix_error("signal (SIGUSR1) FAILED");
101	if (signal(SIGUSR2, sig_usr) == SIG_ERR)
102		unix_error("signal (SIGUSR2) FAILED");
103
104	sigemptyset(&zeromask);
105	sigemptyset(&newmask);
106	sigaddset(&newmask, SIGUSR1);
107	sigaddset(&newmask, SIGUSR2);
108
109	if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
110		unix_error("signal (SIG_BLOCK) FAILED");
111}
112
113/* ========================================================================================
114 *	TELL parent that we are done: used in child process.
115 *	- This function must be called after TELL_WAIT() setup the SIGUSR1 & SIGUSR2 propery.
116 *	- INPUT: parent process ID; can be obtained through getppid().
117 * ======================================================================================== */
118void TELL_PARENT(pid_t pid)
119{
120	kill(pid, SIGUSR2);	// send signal SIGUSR2 to pid process
121}
122
123/* ========================================================================================
124 *	TELL child that we are done: used in parent process.
125 *	- This function must be called after TELL_WAIT() setup the SIGUSR1 & SIGUSR2 propery.
126 *	- INPUT: child process ID; can be obtained through pid = fork() where pid > 0.
127 * ======================================================================================== */
128void TELL_CHILD(pid_t pid)
129{
130	kill(pid, SIGUSR1);	// send signal SIGUSR1 to pid process
131}
132
133/* ========================================================================================
134 *	WAIT for parent: used in child process.
135 *	- This function must be called after TELL_WAIT() setup the SIGUSR1 & SIGUSR2 propery.
136 * ======================================================================================== */
137void WAIT_PARENT(void)
138{
139	while (sigflag == 0)
140		sigsuspend(&zeromask);	// wait for child
141	sigflag = 0;
142
143	// reset signal mask to original value
144	if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
145		unix_error("signal (SIG_SETMASK) FAILED");
146}
147
148/* ========================================================================================
149 *	WAIT for child: used in parent process.
150 *	- This function must be called after TELL_WAIT() setup the SIGUSR1 & SIGUSR2 propery.
151 * ======================================================================================== */
152void WAIT_CHILD(void)
153{
154	while (sigflag == 0)
155		sigsuspend(&zeromask);	// wait for parent
156	sigflag = 0;
157
158	// reset signal mask to original value
159	if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
160		unix_error("signal (SIG_SETMASK) FAILED");
161}
162
163/////////////////////////////////////////////////////////////////////////////////////////////
164
165/////////////////////////////////////// MEMORY ALLOCATION FUNCTIONS /////////////////////////
166/* =====================================================================================
167 *	SET sbrk_num @start of each process to count # of sbrk() calls within that process.
168 *	- INPUT: input number for globak sbrk_num to be set to.
169 * ===================================================================================== */
170void set_sbrk_num(int in)
171{
172	sbrk_num = in;
173}
174
175/* ========================================================================================
176 *	PRINT system information; e.g. free {ram, swap}, total {ram, swap}.
177 * ======================================================================================== */
178void print_sysinfo(void)
179{
180	struct sysinfo si;
181	sysinfo(&si);
182
183	printf
184	    ("freeram (%luKB),  freeswap (%luKB), totalram (%luKB), totalswap (%luKB)\n",
185	     (si.freeram / KB_VALUE) * si.mem_unit,
186	     (si.freeswap / KB_VALUE) * si.mem_unit,
187	     (si.totalram / KB_VALUE) * si.mem_unit,
188	     (si.totalswap / KB_VALUE) * si.mem_unit);
189}
190
191/* ========================================================================================
192 *	CALCULATE freeswap space.
193 *	- OUTPUT: Return size of free swap space in KB.
194 * ======================================================================================== */
195long unsigned freeswap(void)
196{
197	struct sysinfo si;
198	sysinfo(&si);
199
200	return ((si.freeswap / KB_VALUE) * si.mem_unit);
201}
202
203/* ========================================================================================
204 *	CALCULATE freeram space.
205 *	- OUTPUT: Return size of free ram space in KB.
206 * ======================================================================================== */
207long unsigned freeram(void)
208{
209	struct sysinfo si;
210	sysinfo(&si);
211
212	return ((si.freeram / KB_VALUE) * si.mem_unit);
213}
214
215/* ========================================================================================
216 *	ALLOCATE data using sbrk(incr).
217 *  	- Global sbrk_num will be updated for each time calling sbrk() to increase heap size.
218 *	- OUTPUT: Return 1 if success,
219 *			 0 if failed, and will decrement the heap space for future library calls
220 * ======================================================================================== */
221int malloc_data(void)
222{
223	int return_value = 0;	// default return = true;
224	intptr_t incr = MALLOC_SIZE;	// 64KB
225	char *src = NULL;	// to hold addr return from sbrk(incr)
226	long i;			// loop counter
227
228	src = sbrk(incr);
229
230	if (((void *)src == (void *)-1) && (errno == ENOMEM)) {	// error handling
231		src = sbrk(-(2 * incr));	// freeing some space for later library calls
232		sbrk_num -= 2;
233		end_addr = src + (-(2 * incr));	// update end of heap
234	} else {		// sucess case
235		// must write to data, write once for each 1KB
236		for (i = 0x0; i < incr; i += PAGE_SIZE)
237			src[i] = '*';
238		++sbrk_num;	// update global sbrk() call counter when success
239		return_value = 1;	// update return value to true
240		end_addr = src + incr;	// update end of heap
241	}
242
243	return return_value;
244}
245
246/* ========================================================================================
247 *	DEALLOCATE data using sbrk(-incr).
248 *  	- Global sbrk_num will be updated for each time calling sbrk() to decrease heap size.
249 *	- OUTPUT: Return 1 if success,
250 *			 0 if failed.
251 * ======================================================================================== */
252int dealloc_data(void)
253{
254	int return_value = 0;	// default return = true
255	intptr_t incr = MALLOC_SIZE;	// 64KB
256	char *src = NULL;	// to hold adrr return from sbrk(incr)
257	long i;			// loop counter
258	long old_sbrk_num = sbrk_num;	// save old sbrk_num counter, because sbrk_num will be updated
259
260	for (i = 0; i < old_sbrk_num; ++i) {
261		src = sbrk(-incr);
262
263		// error handling: Fatal Fail
264		if (((void *)src == (void *)-1) && (errno == ENOMEM))
265			goto OUT;	// error
266
267		--sbrk_num;	// update # of sbrk() call
268		end_addr = src + (-incr);	// update end of heap
269	}
270	return_value = 1;	// update return value to true
271
272OUT:
273	return return_value;
274}
275
276/* ========================================================================================
277 *	Write to the memory because of Copy-On-Write behavior from LINUX kernel.
278 *	IDEA: Because fork() is implemented through Copy-On-Write. This technique
279 *	      delay/prevent the copy of data, child & parent share memory, and their
280 *	      duplication of the address of child & parent are shared read-only. For parent
281 *	      & child to have its very own separate space, both must write to their own data.
282 *	      So this function will deal with the write for the child process created
283 *	      by fork().
284 *	OUTPUT: Return 1 if success,
285 *		       0 if failed.
286 * ======================================================================================== */
287int handle_COW(void)
288{
289	int return_value = 0;	// default return = true
290	intptr_t incr = MALLOC_SIZE;	// 64KB
291	char *src = NULL;	// to hold adrr return from sbrk(incr)
292	char *i;		// loop counter
293
294	// error handling: Make sure the start_addr is not NULL
295	if (start_addr == NULL) {
296		user_error("start_addr from parent is not initialized");
297		goto OUT;
298	}
299	// error handling: Make sure the end_addr is not NULL
300	if (end_addr == NULL) {
301		user_error("end_addr from parent is not initialized");
302		goto OUT;
303	}
304	// Writing to heap
305	if (start_addr < end_addr) {	// Heap grows up to higher address
306		for (i = start_addr; i < end_addr; i += PAGE_SIZE) {
307			if ((freeswap() + freeram()) < COMMITTED_AS)
308				goto OUT;
309			*i = 'u';
310		}
311		return_value = 1;
312	} else if (start_addr > end_addr) {	// Heap grows down to lower address
313		for (i = end_addr; i > start_addr; i -= PAGE_SIZE) {
314			if ((freeswap() + freeram()) < COMMITTED_AS)
315				goto OUT;
316			*i = 'd';
317		}
318		return_value = 1;
319	} else;			// Heap doesn't grows
320
321OUT:
322	return return_value;
323}
324
325/* ========================================================================================
326 *	EAT lots and lots of memory...
327 *	- If a process can eat all of the free resouces
328 *	  specified, that process will exit the program.
329 * ======================================================================================== */
330void eat_mem(void)
331{
332	// saving the current heap pointer befoer start to allocate more memory
333	start_addr = NULL;
334	end_addr = NULL;
335	start_addr = sbrk(0);
336
337	// eating memory
338	while ((freeswap() + freeram()) > COMMITTED_AS) {
339		if (!malloc_data())
340			return;
341	}
342
343	print_sysinfo();
344	exit(0);
345}
346
347/* ========================================================================================
348 *	EAT lots and lots of memory...If a process can eat all of the free resouces
349 *	specified, that process will exit the program
350 * ======================================================================================== */
351void eat_mem_no_exit(void)
352{
353	// saving the current heap pointer befoer start to allocate more memory
354	start_addr = NULL;
355	end_addr = NULL;
356	start_addr = sbrk(0);
357
358	// eating memory
359	while ((freeswap() + freeram()) > COMMITTED_AS) {
360		if (!malloc_data())
361			break;
362	}
363}
364
365///////////////////////////////////////////////////////////////////////////////////////////////////
366
367///////////////////////////////// MAIN PROGRAM ////////////////////////////////////////////////////
368int main(int argc, char **argv)
369{
370	pid_t pid;		// used for fork()
371	print_sysinfo();	// sytem resouces before start allocation
372	set_sbrk_num(0);	// at start of process, ensure sbrk_num is set
373	eat_mem();
374
375	// @beyound this point -> 1 process can't consume all memory so it must fork a child to consume more
376	// memory
377START:
378	pid = fork();
379	pid = pid < 0 ? -1 : pid;
380
381	switch (pid) {
382	case -1:
383		if (!dealloc_data())
384			unix_error
385			    ("SBRK(-incr) FROM DEALLOC_DATA() FAILED. FATAL!!!");
386		goto LAST_CONDITION;
387
388	case 0:
389		if (!handle_COW()) {	// Re-touch child pages
390			print_sysinfo();	// FINAL RESULT, LAST RESOURCES
391			exit(0);	// child can't allocate no more, DONE!!!
392		}
393		goto START;
394
395	default:
396		if (waitpid(-1, NULL, 0) != pid)	// Parent Waiting
397			unix_error("WAIT_PID FAILED. FATAL!!!");
398		exit(0);
399	}
400
401LAST_CONDITION:
402	TELL_WAIT();		// set up parent/child signal handler
403	pid = fork();
404	pid = pid < 0 ? -1 : pid;
405
406	switch (pid) {
407	case -1:
408		unix_error("FORK FAILED.");
409
410	case 0:
411		eat_mem_no_exit();
412		WAIT_PARENT();
413		print_sysinfo();	// FINAL RESULT, LAST RESOUCES
414		TELL_PARENT(getppid());
415		exit(0);
416
417	default:
418		eat_mem_no_exit();
419		TELL_CHILD(pid);
420		WAIT_CHILD();
421		exit(0);
422	}
423}
424
425///////////////////////////////////////////////////////////////////////////////////////////////////////////////
426