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@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@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 /******************************************************************************/
my_yield()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 /******************************************************************************/
usage(char * progname)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 /******************************************************************************/
allocate_free(int repeat,int scheme)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 /******************************************************************************/
alloc_mem(void * threadnum)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 /******************************************************************************/
main(int argc,char ** argv)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