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 /******************************************************************************/ 22 /* */ 23 /* History: Nov - 04 - 2001 Created - Manoj Iyer, IBM Austin TX. */ 24 /* email:manjo (at) austin.ibm.com */ 25 /* */ 26 /* Nov - 06 - 2001 Modified - Manoj Iyer, IBM Austin TX. */ 27 /* - added function alloc_mem() */ 28 /* */ 29 /* Nov - 08 - 2001 Modified - Manoj Iyer, IBM Austin TX. */ 30 /* - added logic to allocate memory in the size */ 31 /* of fibanocci numbers. */ 32 /* - fixed segmetation fault. */ 33 /* */ 34 /* Nov - 09 - 2001 Modified - Manoj Iyer, IBM Austin TX. */ 35 /* - separated alocation logic to allocate_free()*/ 36 /* function. */ 37 /* - introduced logic to randomly pick allocation*/ 38 /* scheme. size = fibannoci number, pow of 2 or*/ 39 /* power of 3. */ 40 /* - changed comments. */ 41 /* - Added test to LTP. */ 42 /* */ 43 /* Nov - 09 - 2001 Modified - Manoj Iyer,IBM Austin TX. */ 44 /* - Removed compile errors. */ 45 /* - too many missing arguments. */ 46 /* */ 47 /* Nov - 19 - 2001 Modified - Manoj Iyer, IBM Austin TX. */ 48 /* - fixed segmentation fault. */ 49 /* changed variable th_status from dynamic to */ 50 /* static array. */ 51 /* */ 52 /* May - 15 - 2002 Dan Kegel (dank (at) kegel.com) */ 53 /* - Fixed crash on > 30 threads */ 54 /* - Cleaned up, fixed compiler warnings */ 55 /* - Removed mallocs that could fail */ 56 /* - Note that pthread_create fails with EINTR */ 57 /* */ 58 /* File: mallocstress.c */ 59 /* */ 60 /* Description: This program stresses the VMM and C library */ 61 /* by spawning N threads which */ 62 /* malloc blocks of increasing size until malloc returns NULL. */ 63 /******************************************************************************/ 64 #include <stdio.h> 65 #include <pthread.h> 66 #include <stdlib.h> 67 #include <unistd.h> 68 #include <math.h> 69 #include <assert.h> 70 #include <errno.h> 71 #include <stdint.h> 72 #include <sys/types.h> 73 #include <sys/ipc.h> 74 #include <sys/sem.h> 75 76 #define MAXL 100 /* default number of loops to do malloc and free */ 77 #define MAXT 60 /* default number of threads to create. */ 78 79 #ifdef DEBUG 80 #define dprt(args) printf args 81 #else 82 #define dprt(args) 83 #endif 84 85 #define OPT_MISSING(prog, opt) do{\ 86 fprintf(stderr, "%s: option -%c ", prog, opt); \ 87 fprintf(stderr, "requires an argument\n"); \ 88 usage(prog); \ 89 } while (0) 90 91 int num_loop = MAXL; /* number of loops to perform */ 92 int semid; 93 94 /* Define SPEW_SIGNALS to tickle thread_create bug (it fails if interrupted). */ 95 #define SPEW_SIGNALS 96 97 /******************************************************************************/ 98 /* */ 99 /* Function: my_yield */ 100 /* */ 101 /* Description: Yield control to another thread. */ 102 /* Generate a signal, too. */ 103 /* */ 104 /******************************************************************************/ 105 static void my_yield() 106 { 107 #ifdef SPEW_SIGNALS 108 /* usleep just happens to use signals in glibc at moment. 109 * This is good because it allows us to test whether pthread_create 110 * improperly returns EINTR (which would violate SUSv3) 111 */ 112 usleep(0); 113 #else 114 /* If you want this test to pass, don't define SPEW_SIGNALS, 115 * as pthread_create is broken at moment, and fails if interrupted 116 */ 117 static const struct timespec t0 = { 0, 0 }; 118 nanosleep(&t0, NULL); 119 #endif 120 } 121 122 /******************************************************************************/ 123 /* */ 124 /* Function: usage */ 125 /* */ 126 /* Description: Print the usage message. */ 127 /* */ 128 /* Input: char *progname - name of this program */ 129 /* */ 130 /* Return: exits with -1 */ 131 /* */ 132 /******************************************************************************/ 133 static void usage(char *progname) 134 { /* name of this program */ 135 fprintf(stderr, 136 "Usage: %s -d NUMDIR -f NUMFILES -h -t NUMTHRD\n" 137 "\t -h Help!\n" 138 "\t -l Number of loops: Default: 1000\n" 139 "\t -t Number of threads to generate: Default: 30\n", progname); 140 exit(-1); 141 } 142 143 /******************************************************************************/ 144 /* Function: allocate_free */ 145 /* */ 146 /* Description: This function does the allocation and free by calling malloc */ 147 /* and free fuctions. The size of the memory to be malloced is */ 148 /* determined by the caller of this function. The size can be */ 149 /* a number from the fibannoaci series, power of 2 or 3 or 5 */ 150 /* */ 151 /* Input: int repeat - number of times the alloc/free is repeated. */ 152 /* int scheme - 0 to 3; selects how fast memory size grows */ 153 /* */ 154 /* Return: 1 on failure */ 155 /* 0 on success */ 156 /******************************************************************************/ 157 int allocate_free(int repeat, /* number of times to repeat allocate/free */ 158 int scheme) 159 { /* how fast to increase block size */ 160 int loop; 161 const int MAXPTRS = 50; /* only 42 or so get used on 32 bit machine */ 162 163 dprt(("pid[%d]: allocate_free: repeat %d, scheme %d\n", getpid(), 164 repeat, scheme)); 165 166 for (loop = 0; loop < repeat; loop++) { 167 size_t oldsize = 5; /* remember size for fibannoci series */ 168 size_t size = sizeof(long); /* size of next block in ptrs[] */ 169 long *ptrs[MAXPTRS]; /* the pointers allocated in this loop */ 170 int num_alloc; /* number of elements in ptrs[] so far */ 171 int i; 172 173 dprt(("pid[%d]: allocate_free: loop %d of %d\n", getpid(), loop, 174 repeat)); 175 176 /* loop terminates in one of three ways: 177 * 1. after MAXPTRS iterations 178 * 2. if malloc fails 179 * 3. if new size overflows 180 */ 181 for (num_alloc = 0; num_alloc < MAXPTRS; num_alloc++) { 182 size_t newsize = 0; 183 184 dprt(("pid[%d]: loop %d/%d; num_alloc=%d; size=%u\n", 185 getpid(), loop, repeat, num_alloc, size)); 186 187 /* Malloc the next block */ 188 ptrs[num_alloc] = malloc(size); 189 if (ptrs[num_alloc] == NULL) { 190 /* terminate loop if malloc couldn't give us the memory we asked for */ 191 break; 192 } 193 ptrs[num_alloc][0] = num_alloc; 194 195 /* Increase size according to one of four schedules. */ 196 switch (scheme) { 197 case 0: 198 newsize = size + oldsize; 199 oldsize = size; 200 break; 201 case 1: 202 newsize = size * 2; 203 break; 204 case 2: 205 newsize = size * 3; 206 break; 207 case 3: 208 newsize = size * 5; 209 break; 210 default: 211 assert(0); 212 } 213 /* terminate loop on overflow */ 214 if (newsize < size) 215 break; 216 size = newsize; 217 218 my_yield(); 219 } 220 221 for (i = 0; i < num_alloc; i++) { 222 dprt(("pid[%d]: freeing ptrs[i] %p\n", getpid(), 223 ptrs[i])); 224 if (ptrs[i][0] != i) { 225 fprintf(stderr, 226 "pid[%d]: fail: bad sentinel value\n", 227 getpid()); 228 return 1; 229 } 230 free(ptrs[i]); 231 my_yield(); 232 } 233 234 my_yield(); 235 } 236 /* Success! */ 237 return 0; 238 } 239 240 /******************************************************************************/ 241 /* Function: alloc_mem */ 242 /* */ 243 /* Description: Decide how fast to increase block sizes, then call */ 244 /* allocate_free() to actually to the test. */ 245 /* */ 246 /* Input: threadnum is the thread number, 0...N-1 */ 247 /* global num_loop is how many iterations to run */ 248 /* */ 249 /* Return: pthread_exit -1 on failure */ 250 /* pthread_exit 0 on success */ 251 /* */ 252 /******************************************************************************/ 253 void *alloc_mem(void *threadnum) 254 { 255 struct sembuf sop[1]; 256 sop[0].sem_num = 0; 257 sop[0].sem_op = 0; 258 sop[0].sem_flg = 0; 259 /* waiting for other threads starting */ 260 if (semop(semid, sop, 1) == -1) { 261 if (errno != EIDRM) 262 perror("semop"); 263 return (void *)-1; 264 } 265 266 /* thread N will use growth scheme N mod 4 */ 267 int err = allocate_free(num_loop, ((uintptr_t) threadnum) % 4); 268 fprintf(stdout, 269 "Thread [%d]: allocate_free() returned %d, %s. Thread exiting.\n", 270 (int)(uintptr_t) threadnum, err, 271 (err ? "failed" : "succeeded")); 272 return (void *)(uintptr_t) (err ? -1 : 0); 273 } 274 275 /******************************************************************************/ 276 /* */ 277 /* Function: main */ 278 /* */ 279 /* Description: This is the entry point to the program. This function will */ 280 /* parse the input arguments and set the values accordingly. If */ 281 /* no arguments (or desired) are provided default values are used*/ 282 /* refer the usage function for the arguments that this program */ 283 /* takes. It also creates the threads which do most of the dirty */ 284 /* work. If the threads exits with a value '0' the program exits */ 285 /* with success '0' else it exits with failure '-1'. */ 286 /* */ 287 /* Return: -1 on failure */ 288 /* 0 on success */ 289 /* */ 290 /******************************************************************************/ 291 int main(int argc, /* number of input parameters */ 292 char **argv) 293 { /* pointer to the command line arguments. */ 294 int c; /* command line options */ 295 int num_thrd = MAXT; /* number of threads to create */ 296 int thrd_ndx; /* index into the array of thread ids */ 297 pthread_t *thrdid; /* the threads */ 298 extern int optopt; /* options to the program */ 299 struct sembuf sop[1]; 300 int ret = 0; 301 302 while ((c = getopt(argc, argv, "hl:t:")) != -1) { 303 switch (c) { 304 case 'h': 305 usage(argv[0]); 306 break; 307 case 'l': 308 if ((num_loop = atoi(optarg)) == 0) 309 OPT_MISSING(argv[0], optopt); 310 else if (num_loop < 1) { 311 fprintf(stdout, 312 "WARNING: bad argument. Using default\n"); 313 num_loop = MAXL; 314 } 315 break; 316 case 't': 317 if ((num_thrd = atoi(optarg)) == 0) 318 OPT_MISSING(argv[0], optopt); 319 else if (num_thrd < 1) { 320 fprintf(stdout, 321 "WARNING: bad argument. Using default\n"); 322 num_thrd = MAXT; 323 } 324 break; 325 default: 326 usage(argv[0]); 327 break; 328 } 329 } 330 331 dprt(("number of times to loop in the thread = %d\n", num_loop)); 332 333 thrdid = malloc(sizeof(pthread_t) * num_thrd); 334 if (thrdid == NULL) { 335 perror("main(): allocating space for thrdid[] malloc()"); 336 return 1; 337 } 338 339 semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666); 340 if (semid < 0) { 341 perror("Semaphore creation failed Reason:"); 342 } 343 344 sop[0].sem_num = 0; 345 sop[0].sem_op = 1; 346 sop[0].sem_flg = 0; 347 if (semop(semid, sop, 1) == -1) { 348 perror("semop"); 349 ret = -1; 350 goto out; 351 } 352 353 for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx++) { 354 if (pthread_create(&thrdid[thrd_ndx], NULL, alloc_mem, 355 (void *)(uintptr_t) thrd_ndx)) { 356 int err = errno; 357 if (err == EINTR) { 358 fprintf(stderr, 359 "main(): pthread_create failed with EINTR!\n"); 360 ret = -1; 361 goto out; 362 } 363 perror("main(): pthread_create()"); 364 ret = -11; 365 goto out; 366 } 367 } 368 my_yield(); 369 370 sop[0].sem_op = -1; 371 if (semop(semid, sop, 1) == -1) { 372 perror("semop"); 373 ret = -1; 374 goto out; 375 } 376 377 for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx++) { 378 void *th_status; /* exit status of LWP */ 379 if (pthread_join(thrdid[thrd_ndx], &th_status) != 0) { 380 perror("main(): pthread_join()"); 381 ret = -1; 382 goto out; 383 } else { 384 if ((intptr_t) th_status != 0) { 385 fprintf(stderr, 386 "main(): thread [%d] - exited with errors\n", 387 thrd_ndx); 388 ret = -1; 389 goto out; 390 } 391 dprt(("main(): thread [%d]: exited without errors\n", 392 thrd_ndx)); 393 } 394 my_yield(); 395 } 396 printf("main(): test passed.\n"); 397 out: 398 if (semctl(semid, 0, IPC_RMID) == -1) { 399 perror("semctl\n"); 400 ret = -1; 401 } 402 if (thrdid) { 403 free(thrdid); 404 thrdid = NULL; 405 } 406 exit(ret); 407 } 408