Home | History | Annotate | Download | only in mem
      1 /*
      2  * mem01.c - Basic memory and swapper stress test
      3  *
      4  * Copyright (C) 2001 Stephane Fillod <f4cfe (at) free.fr>
      5  * 	Original idea from Rene Cougnenc (on t'a pas oubli mec)
      6  *
      7  * Copyright (C) 2012 Cyril Hrubis <chrubis (at) suse.cz>
      8  *
      9  * This program is free software; you can redistribute it and/or modify it
     10  * under the terms of version 2 of the GNU General Public License as
     11  * published by the Free Software Foundation.
     12  *
     13  * This program is distributed in the hope that it would be useful, but
     14  * WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     16  *
     17  * Further, this software is distributed without any warranty that it is
     18  * free of the rightful claim of any third person regarding infringement
     19  * or the like.  Any license provided herein, whether implied or
     20  * otherwise, applies only to this software file.  Patent licenses, if
     21  * any, provided herein do not apply to combinations of this program with
     22  * other software, or any other product whatsoever.
     23  *
     24  * You should have received a copy of the GNU General Public License along
     25  * with this program; if not, write the Free Software Foundation, Inc.,
     26  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
     27  *
     28  */
     29 
     30 #include <unistd.h>
     31 #include <errno.h>
     32 #include <string.h>
     33 #include <signal.h>
     34 #include <sys/types.h>
     35 #include <sys/sysinfo.h>
     36 #include <stdio.h>
     37 #include <stdlib.h>
     38 #include <sys/user.h>
     39 #include <time.h>
     40 #include <limits.h>
     41 
     42 #include "test.h"
     43 
     44 /* in KB */
     45 #define PROGRESS_LEAP 100
     46 
     47 /*
     48  * TODO:
     49  *  - add option for growing direction, when doing linear touching
     50  *  - add option for touch running time (or infinite loop?)
     51  *  - make it multithreaded with random access to test r/w mm_sem
     52  */
     53 
     54 char *TCID = "mem01";
     55 int TST_TOTAL = 1;
     56 
     57 static int m_opt = 0;		/* memsize */
     58 static char *m_copt;
     59 
     60 static int r_opt = 0;		/* random access versus linear */
     61 static int v_opt = 0;		/* verbose progress indication */
     62 
     63 static void cleanup(void)
     64 {
     65 	tst_rmdir();
     66 	tst_exit();
     67 }
     68 
     69 static void setup(void)
     70 {
     71 	tst_sig(NOFORK, DEF_HANDLER, cleanup);
     72 
     73 	TEST_PAUSE;
     74 
     75 	tst_tmpdir();
     76 }
     77 
     78 static void help(void)
     79 {
     80 	printf("  -m x    size of malloc in MB (default from /proc/meminfo)\n");
     81 	printf("  -r      random touching versus linear\n");
     82 	printf("  -v      verbose progress indication\n");
     83 }
     84 
     85 /*
     86  * return MemFree+SwapFree, from /proc/meminfo
     87  * returned value is in bytes.
     88  */
     89 size_t get_memsize(void)
     90 {
     91 	struct sysinfo info;
     92 	unsigned long long res;
     93 	unsigned long long freeswap;
     94 	unsigned long long freeram;
     95 	int ret;
     96 
     97 	ret = sysinfo(&info);
     98 	if (ret != 0) {
     99 		tst_resm(TFAIL,
    100 			 "Could not retrieve memory information using sysinfo()");
    101 		cleanup();
    102 	}
    103 
    104 	freeram =
    105 	    (unsigned long long)info.freeram *
    106 	    (unsigned long long)info.mem_unit;
    107 	tst_resm(TINFO, "Free Mem:\t%llu Mb", freeram / 1024 / 1024);
    108 	res = freeram;
    109 
    110 	freeswap =
    111 	    (unsigned long long)info.freeswap *
    112 	    (unsigned long long)info.mem_unit;
    113 	tst_resm(TINFO, "Free Swap:\t%llu Mb", freeswap / 1024 / 1024);
    114 	res = res + freeswap;
    115 
    116 	tst_resm(TINFO, "Total Free:\t%llu Mb", res / 1024 / 1024);
    117 #if defined (__s390__)
    118 	if (res > 1 * 1024 * 1024 * 1024)
    119 		res = 500 * 1024 * 1024;	/* s390's unique 31bit architecture needs smaller default */
    120 #elif __WORDSIZE == 32
    121 	if (res > 1 * 1024 * 1024 * 1024)
    122 		res = 1 * 1024 * 1024 * 1024;
    123 #elif __WORDSIZE == 64
    124 	if (res > (unsigned long long)3 * 1024 * 1024 * 1024)
    125 		res = (unsigned long long)3 *1024 * 1024 * 1024;
    126 #endif
    127 
    128 	/* Always reserve 16MB memory to avoid OOM Killer. */
    129 	res -= 16 * 1024 * 1024;
    130 	tst_resm(TINFO, "Total Tested:\t%llu Mb", res / 1024 / 1024);
    131 	return (size_t) res;
    132 }
    133 
    134 /*
    135  * add the -m option whose parameter is the
    136  * memory size (MB) to allocate.
    137  */
    138 option_t options[] = {
    139 	{"m:", &m_opt, &m_copt}
    140 	,
    141 	{"r", &r_opt, NULL}
    142 	,
    143 	{"v", &v_opt, NULL}
    144 	,
    145 	{NULL, NULL, NULL}
    146 };
    147 
    148 int main(int argc, char *argv[])
    149 {
    150 	size_t memsize = 0;	/* at first in MB, limited to 4Gb on 32 bits */
    151 	int pagesize;
    152 
    153 	int i;
    154 	int lc;
    155 	char *p, *bigmalloc;
    156 	int loop_count;		/* limited to 16Go on 32 bits systems */
    157 
    158 	pagesize = sysconf(_SC_PAGESIZE);
    159 
    160 	tst_parse_opts(argc, argv, options, help);
    161 
    162 	if (m_opt) {
    163 		memsize = (size_t) atoi(m_copt) * 1024 * 1024;
    164 
    165 		if (memsize < 1) {
    166 			tst_brkm(TBROK, cleanup, "Invalid arg for -m: %s",
    167 				 m_copt);
    168 		}
    169 	}
    170 
    171 	if (r_opt)
    172 		srand(time(NULL));
    173 
    174 	setup();
    175 
    176 	for (lc = 0; TEST_LOOPING(lc); lc++) {
    177 
    178 		tst_count = 0;
    179 
    180 		if (!m_opt) {
    181 			/* find out by ourselves! */
    182 			memsize = get_memsize();
    183 			if (memsize < 1) {
    184 				tst_brkm(TBROK, cleanup,
    185 					 "Unable to guess maxmemsize from /proc/meminfo");
    186 			}
    187 		}
    188 
    189 		/* Allocate (virtual) memory */
    190 		bigmalloc = p = malloc(memsize);
    191 
    192 		if (!p) {
    193 			tst_resm(TFAIL, "malloc - alloc of %zuMB failed",
    194 				 memsize / 1024 / 1024);
    195 			cleanup();
    196 		}
    197 
    198 		/*
    199 		 * Dirty all the pages, to force physical RAM allocation
    200 		 * and exercise eventually the swapper
    201 		 */
    202 		tst_resm(TINFO, "touching %zuMB of malloc'ed memory (%s)",
    203 			 memsize / 1024 / 1024, r_opt ? "random" : "linear");
    204 
    205 		loop_count = memsize / pagesize;
    206 
    207 		for (i = 0; i < loop_count; i++) {
    208 			if (v_opt
    209 			    && (i % (PROGRESS_LEAP * 1024 / pagesize) == 0)) {
    210 				printf(".");
    211 				fflush(stdout);
    212 			}
    213 			/*
    214 			 * Make the page dirty,
    215 			 * and make sure compiler won't optimize it away
    216 			 * Touching more than one word per page is useless
    217 			 * because of cache.
    218 			 */
    219 			*(int *)p = 0xdeadbeef ^ i;
    220 
    221 			if (r_opt) {
    222 				p = bigmalloc +
    223 				    (size_t) ((double)(memsize - sizeof(int)) *
    224 					      rand() / (RAND_MAX + 1.0));
    225 			} else {
    226 				p += pagesize;
    227 			}
    228 		}
    229 
    230 		if (v_opt)
    231 			printf("\n");
    232 
    233 		/* This is not mandatory (except in a loop), but it exercise mm again */
    234 		free(bigmalloc);
    235 
    236 		/*
    237 		 * seems that if the malloc'ed area was bad, we'd get SEGV (or kicked
    238 		 * somehow by the OOM killer?), hence we can indicate a PASS.
    239 		 */
    240 		tst_resm(TPASS, "malloc - alloc of %zuMB succeeded",
    241 			 memsize / 1024 / 1024);
    242 
    243 	}
    244 
    245 	cleanup();
    246 
    247 	return 0;
    248 }
    249