1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2020 Peter Holm <pho@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/types.h> 30 #include <sys/resource.h> 31 #include <sys/sysctl.h> 32 #include <sys/time.h> 33 #include <sys/wait.h> 34 35 #include <vm/vm_param.h> 36 37 #include <err.h> 38 #include <errno.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <unistd.h> 42 43 #define INCARNATIONS 32 44 45 static unsigned long size, original; 46 static int runtime, utime; 47 48 void 49 usage(void) 50 { 51 fprintf(stderr, "Usage: %s [-d delay] [-p pct] [-t runtime] " 52 "[-v]\n", 53 getprogname()); 54 exit(1); 55 } 56 57 static unsigned long 58 usermem(void) 59 { 60 unsigned long mem; 61 size_t nlen = sizeof(mem); 62 63 if (sysctlbyname("hw.usermem", &mem, &nlen, NULL, 0) == -1) 64 err(1, "sysctlbyname() %s:%d", __FILE__, __LINE__); 65 66 #if defined(DEBUG) 67 printf("Total free user memory %lu Mb\n", 68 mem / 1024 / 1024); 69 #endif 70 71 return (mem); 72 } 73 static int64_t 74 swap(void) 75 { 76 struct xswdev xsw; 77 size_t mibsize, size; 78 int mib[16], n; 79 int64_t sz; 80 81 mibsize = sizeof mib / sizeof mib[0]; 82 sz = 0; 83 84 if (sysctlnametomib("vm.swap_info", mib, &mibsize) == -1) 85 err(1, "sysctlnametomib()"); 86 87 for (n = 0; ; ++n) { 88 mib[mibsize] = n; 89 size = sizeof xsw; 90 if (sysctl(mib, mibsize + 1, &xsw, &size, NULL, 0) == -1) 91 break; 92 if (xsw.xsw_version != XSWDEV_VERSION) 93 errx(1, "xswdev version mismatch"); 94 sz = sz + xsw.xsw_nblks - xsw.xsw_used; 95 } 96 if (errno != ENOENT) 97 err(1, "sysctl()"); 98 99 #if defined(DEBUG) 100 printf("Total free swap space %jd Mb\n", 101 sz * getpagesize() / 1024 / 1024); 102 #endif 103 104 return (sz * getpagesize()); 105 } 106 107 static void 108 setup(void) 109 { 110 struct rlimit rlp; 111 112 size = size / INCARNATIONS; 113 original = size; 114 if (size == 0) 115 errx(1, "Argument too small"); 116 117 if (getrlimit(RLIMIT_DATA, &rlp) < 0) 118 err(1,"getrlimit"); 119 rlp.rlim_cur -= 1024 * 1024; 120 121 if (size > (unsigned long)rlp.rlim_cur) 122 size = rlp.rlim_cur; 123 124 #if 0 125 printf("setup: pid %d. Total %luMb\n", 126 getpid(), size / 1024 / 1024 * INCARNATIONS); 127 #endif 128 129 if (size == 0) 130 errx(1, "Argument too small"); 131 132 return; 133 } 134 135 static int 136 test(void) 137 { 138 volatile char *c; 139 int page; 140 unsigned long i, j; 141 time_t start; 142 143 c = malloc(size); 144 while (c == NULL) { 145 size -= 1024 * 1024; 146 c = malloc(size); 147 } 148 if (size != original) 149 printf("Malloc size changed from %ld Mb to %ld Mb\n", 150 original / 1024 / 1024, size / 1024 / 1024); 151 page = getpagesize(); 152 start = time(NULL); 153 while ((time(NULL) - start) < runtime) { 154 i = j = 0; 155 while (i < size) { 156 c[i] = 1; 157 i += page; 158 if ((time(NULL) - start) >= runtime) 159 break; 160 usleep(utime); 161 } 162 } 163 free((void *)c); 164 165 _exit(0); 166 } 167 168 int 169 main(int argc, char **argv) 170 { 171 int64_t s; 172 pid_t pids[INCARNATIONS]; 173 unsigned long u; 174 int ch, i, pct, verbose; 175 176 s = swap(); 177 u = usermem(); 178 179 runtime = 120; /* 2 minutes */ 180 utime = 1000; /* 0.001 sec */ 181 verbose = 0; 182 if (s == 0) 183 pct = 80; 184 else 185 pct = 101; 186 while ((ch = getopt(argc, argv, "d:p:t:v")) != -1) { 187 switch(ch) { 188 case 'd': /* delay in usec */ 189 if (sscanf(optarg, "%d", &utime) != 1) 190 usage(); 191 break; 192 case 'p': /* % of usermem */ 193 if (sscanf(optarg, "%d", &pct) != 1) 194 usage(); 195 break; 196 case 't': /* runtime in sec*/ 197 if (sscanf(optarg, "%d", &runtime) != 1) 198 usage(); 199 break; 200 case 'v': /* verbose*/ 201 verbose = 1; 202 break; 203 default: 204 usage(); 205 } 206 } 207 208 if (s == 0 && pct > 80) 209 errx(1, "pct range with no swap is 0-80"); 210 size = u / 100 * pct; 211 if (verbose == 1) 212 fprintf(stderr, "pct = %d, sleep = %d usec, size = %lumb\n", 213 pct, utime, size / 1024 / 1024); 214 setup(); 215 216 for (i = 0; i < INCARNATIONS; i++) 217 if ((pids[i] = fork()) == 0) 218 test(); 219 220 for (i = 0; i < INCARNATIONS; i++) 221 if (waitpid(pids[i], NULL, 0) != pids[i]) 222 err(1, "paitpid(%d)", pids[i]); 223 224 return (0); 225 } 226