source: trunk/src/t-test1.c@ 526

Last change on this file since 526 was 481, checked in by katerina, 9 years ago

Enhancements and fixes for tickets #374, #375, #376, #377, #378, and #379.

File size: 14.4 KB
Line 
1/*
2* Copyright (c) 1996-1999, 2001-2004 Wolfram Gloger
3
4Permission to use, copy, modify, distribute, and sell this software
5and its documentation for any purpose is hereby granted without fee,
6provided that (i) the above copyright notices and this permission
7notice appear in all copies of the software and related documentation,
8and (ii) the name of Wolfram Gloger may not be used in any advertising
9or publicity relating to the software.
10
11THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
12EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
13WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
14
15IN NO EVENT SHALL WOLFRAM GLOGER BE LIABLE FOR ANY SPECIAL,
16INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY
17DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY
19OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20PERFORMANCE OF THIS SOFTWARE.
21*/
22
23/*
24 * $Id: t-test1.c,v 1.2 2004/11/04 14:58:45 wg Exp $
25 * by Wolfram Gloger 1996-1999, 2001, 2004
26 * A multi-thread test for malloc performance, maintaining one pool of
27 * allocated bins per thread.
28 */
29/*
30 t-test[12] <n-total> <n-parallel> <n-allocs> <size-max> <bins>
31
32 n-total = total number of threads executed (default 10)
33 n-parallel = number of threads running in parallel (2)
34 n-allocs = number of malloc()'s / free()'s per thread (10000)
35 size-max = max. size requested with malloc() in bytes (10000)
36 bins = number of bins to maintain
37*/
38
39#if defined(HAVE_CONFIG_H)
40#include "config.h"
41#endif
42
43#if (defined __STDC__ && __STDC__) || defined __cplusplus
44# include <stdlib.h>
45#endif
46#include <stdio.h>
47#include <string.h>
48#include <unistd.h>
49#include <sys/types.h>
50#include <sys/time.h>
51#include <sys/resource.h>
52#include <sys/wait.h>
53#include <sys/mman.h>
54
55/*
56#if !USE_MALLOC
57#include <malloc.h>
58#else
59#include "malloc.h"
60#endif
61*/
62
63#ifdef USE_SYSTEM_MALLOC
64extern void *memalign(size_t boundary, size_t size);
65/* #define memalign(a,b) malloc(b) */
66#else
67extern void *memalign(size_t boundary, size_t size);
68#endif
69
70static int verbose = 1;
71
72/* dummy for the samhain safe_fatal logger
73 */
74void safe_fatal(const char * details,
75 const char * file, int line)
76{
77 (void) file;
78 (void) line;
79 fputs("assert failed: ", stderr);
80 puts(details);
81 _exit(EXIT_FAILURE);
82}
83
84/* lran2.h
85 * by Wolfram Gloger 1996.
86 *
87 * A small, portable pseudo-random number generator.
88 */
89
90#ifndef _LRAN2_H
91#define _LRAN2_H
92
93#define LRAN2_MAX 714025l /* constants for portable */
94#define IA 1366l /* random number generator */
95#define IC 150889l /* (see e.g. `Numerical Recipes') */
96
97struct lran2_st {
98 long x, y, v[97];
99};
100
101static void
102lran2_init(struct lran2_st* d, long seed)
103{
104 long x;
105 int j;
106
107 x = (IC - seed) % LRAN2_MAX;
108 if(x < 0) x = -x;
109 for(j=0; j<97; j++) {
110 x = (IA*x + IC) % LRAN2_MAX;
111 d->v[j] = x;
112 }
113 d->x = (IA*x + IC) % LRAN2_MAX;
114 d->y = d->x;
115}
116
117#ifdef __GNUC__
118__inline__
119#endif
120static long
121lran2(struct lran2_st* d)
122{
123 int j = (d->y % 97);
124
125 d->y = d->v[j];
126 d->x = (IA*d->x + IC) % LRAN2_MAX;
127 d->v[j] = d->x;
128 return d->y;
129}
130
131#undef IA
132#undef IC
133
134#endif
135
136/*
137 * $Id: t-test.h,v 1.1 2004/11/04 14:32:21 wg Exp $
138 * by Wolfram Gloger 1996.
139 * Common data structures and functions for testing malloc performance.
140 */
141
142/* Testing level */
143#ifndef TEST
144#define TEST 99
145#endif
146
147/* For large allocation sizes, the time required by copying in
148 realloc() can dwarf all other execution times. Avoid this with a
149 size threshold. */
150#ifndef REALLOC_MAX
151#define REALLOC_MAX 2000
152#endif
153
154struct bin {
155 unsigned char *ptr;
156 unsigned long size;
157};
158
159#if TEST > 0
160
161static void
162mem_init(unsigned char *ptr, unsigned long size)
163{
164 unsigned long i, j;
165
166 if(size == 0) return;
167#if TEST > 3
168 memset(ptr, '\0', size);
169#endif
170 for(i=0; i<size; i+=2047) {
171 j = (unsigned long)ptr ^ i;
172 ptr[i] = ((j ^ (j>>8)) & 0xFF);
173 }
174 j = (unsigned long)ptr ^ (size-1);
175 ptr[size-1] = ((j ^ (j>>8)) & 0xFF);
176}
177
178static int
179mem_check(unsigned char *ptr, unsigned long size)
180{
181 unsigned long i, j;
182
183 if(size == 0) return 0;
184 for(i=0; i<size; i+=2047) {
185 j = (unsigned long)ptr ^ i;
186 if(ptr[i] != ((j ^ (j>>8)) & 0xFF)) return 1;
187 }
188 j = (unsigned long)ptr ^ (size-1);
189 if(ptr[size-1] != ((j ^ (j>>8)) & 0xFF)) return 2;
190 return 0;
191}
192
193static int
194zero_check(unsigned* ptr, unsigned long size)
195{
196 unsigned char* ptr2;
197
198 while(size >= sizeof(*ptr)) {
199 if(*ptr++ != 0)
200 return -1;
201 size -= sizeof(*ptr);
202 }
203 ptr2 = (unsigned char*)ptr;
204 while(size > 0) {
205 if(*ptr2++ != 0)
206 return -1;
207 --size;
208 }
209 return 0;
210}
211
212#endif /* TEST > 0 */
213
214/* Allocate a bin with malloc(), realloc() or memalign(). r must be a
215 random number >= 1024. */
216int n_malloc=0, n_memalign=0, n_realloc=0, n_calloc=0;
217
218static void
219bin_alloc(struct bin *m, unsigned long size, int r)
220{
221#if TEST > 0
222 if(mem_check(m->ptr, m->size)) {
223 fprintf(stderr, "memory corrupt!\n");
224 exit(1);
225 }
226#endif
227 r %= 1024;
228 /*printf("%d ", r);*/
229 if(r < 4) { /* memalign */
230 if(m->size > 0) free(m->ptr);
231 m->ptr = (unsigned char *)memalign(sizeof(int) << r, size);
232 ++n_memalign;
233 } else if(r < 20) { /* calloc */
234 if(m->size > 0) free(m->ptr);
235 m->ptr = (unsigned char *)calloc(size, 1);
236#if TEST > 0
237 if(zero_check((unsigned*)m->ptr, size)) {
238 unsigned long i;
239 for(i=0; i<size; i++)
240 if(m->ptr[i] != 0)
241 break;
242 fprintf(stderr, "calloc'ed memory non-zero (ptr=%p, i=%ld)!\n", m->ptr, i);
243 exit(1);
244 }
245#endif
246 ++n_calloc;
247 } else if(r < 100 && m->size < REALLOC_MAX) { /* realloc */
248 if(m->size == 0) m->ptr = NULL;
249 m->ptr = realloc(m->ptr, size);
250 ++n_realloc;
251 } else { /* plain malloc */
252 if(m->size > 0) free(m->ptr);
253 m->ptr = (unsigned char *)malloc(size);
254 ++n_malloc;
255 }
256 if(!m->ptr) {
257 fprintf(stderr, "out of memory (r=%d, size=%ld)!\n", r, (long)size);
258 exit(1);
259 }
260 m->size = size;
261#if TEST > 0
262 mem_init(m->ptr, m->size);
263#endif
264}
265
266/* Free a bin. */
267
268static void
269bin_free(struct bin *m)
270{
271 if(m->size == 0) return;
272#if TEST > 0
273 if(mem_check(m->ptr, m->size)) {
274 fprintf(stderr, "memory corrupt!\n");
275 exit(1);
276 }
277#endif
278 free(m->ptr);
279 m->size = 0;
280}
281
282/*
283 * Local variables:
284 * tab-width: 4
285 * End:
286 */
287
288
289struct user_data {
290 int bins, max;
291 unsigned long size;
292 long seed;
293};
294
295/*
296 * $Id: thread-st.h$
297 * pthread version
298 * by Wolfram Gloger 2004
299 */
300
301#include <pthread.h>
302#include <stdio.h>
303
304pthread_cond_t finish_cond = PTHREAD_COND_INITIALIZER;
305pthread_mutex_t finish_mutex = PTHREAD_MUTEX_INITIALIZER;
306
307#ifndef USE_PTHREADS_STACKS
308#define USE_PTHREADS_STACKS 0
309#endif
310
311#ifndef STACKSIZE
312#define STACKSIZE 32768
313#endif
314
315struct thread_st {
316 char *sp; /* stack pointer, can be 0 */
317 void (*func)(struct thread_st* st); /* must be set by user */
318 pthread_t id;
319 int flags;
320 struct user_data u;
321};
322
323static void
324mthread_init(void)
325{
326#if !defined(USE_SYSTEM_MALLOC) && defined(USE_MALLOC_LOCK)
327 extern int dnmalloc_pthread_init(void);
328 dnmalloc_pthread_init();
329#endif
330
331 if (verbose)
332 printf("Using posix threads.\n");
333 pthread_cond_init(&finish_cond, NULL);
334 pthread_mutex_init(&finish_mutex, NULL);
335}
336
337static void *
338mthread_wrapper(void *ptr)
339{
340 struct thread_st *st = (struct thread_st*)ptr;
341
342 /*printf("begin %p\n", st->sp);*/
343 st->func(st);
344 pthread_mutex_lock(&finish_mutex);
345 st->flags = 1;
346 pthread_mutex_unlock(&finish_mutex);
347 pthread_cond_signal(&finish_cond);
348 /*printf("end %p\n", st->sp);*/
349 return NULL;
350}
351
352/* Create a thread. */
353static int
354mthread_create(struct thread_st *st)
355{
356 st->flags = 0;
357 {
358 pthread_attr_t* attr_p = 0;
359#if USE_PTHREADS_STACKS
360 pthread_attr_t attr;
361
362 pthread_attr_init (&attr);
363 if(!st->sp)
364 st->sp = malloc(STACKSIZE+16);
365 if(!st->sp)
366 return -1;
367 if(pthread_attr_setstacksize(&attr, STACKSIZE))
368 fprintf(stderr, "error setting stacksize");
369 else
370 pthread_attr_setstackaddr(&attr, st->sp + STACKSIZE);
371 /*printf("create %p\n", st->sp);*/
372 attr_p = &attr;
373#endif
374 return pthread_create(&st->id, attr_p, mthread_wrapper, st);
375 }
376 return 0;
377}
378
379/* Wait for one of several subthreads to finish. */
380static void
381wait_for_thread(struct thread_st st[], int n_thr,
382 int (*end_thr)(struct thread_st*))
383{
384 int i;
385
386 pthread_mutex_lock(&finish_mutex);
387 for(;;) {
388 int term = 0;
389 for(i=0; i<n_thr; i++)
390 if(st[i].flags) {
391 /*printf("joining %p\n", st[i].sp);*/
392 if(pthread_join(st[i].id, NULL) == 0) {
393 st[i].flags = 0;
394 if(end_thr)
395 end_thr(&st[i]);
396 } else
397 fprintf(stderr, "can't join\n");
398 ++term;
399 }
400 if(term > 0)
401 break;
402 pthread_cond_wait(&finish_cond, &finish_mutex);
403 }
404 pthread_mutex_unlock(&finish_mutex);
405}
406
407/*
408 * Local variables:
409 * tab-width: 4
410 * End:
411 */
412
413
414#define N_TOTAL 10
415#ifndef N_THREADS
416#define N_THREADS 2
417#endif
418#ifndef N_TOTAL_PRINT
419#define N_TOTAL_PRINT 50
420#endif
421#ifndef MEMORY
422#define MEMORY 8000000l
423#endif
424#define SIZE 10000
425#define I_MAX 10000
426#define ACTIONS_MAX 30
427#ifndef TEST_FORK
428#define TEST_FORK 0
429#endif
430
431#define RANDOM(d,s) (lran2(d) % (s))
432
433struct bin_info {
434 struct bin *m;
435 unsigned long size, bins;
436};
437
438#if TEST > 0
439
440void
441bin_test(struct bin_info *p)
442{
443 unsigned int b;
444
445 for(b=0; b<p->bins; b++) {
446 if(mem_check(p->m[b].ptr, p->m[b].size)) {
447 fprintf(stderr, "memory corrupt!\n");
448 abort();
449 }
450 }
451}
452
453#endif
454
455void
456malloc_test(struct thread_st *st)
457{
458 unsigned int b;
459 int i, j, actions, pid = 1;
460 struct bin_info p;
461 struct lran2_st ld; /* data for random number generator */
462
463 lran2_init(&ld, st->u.seed);
464#if TEST_FORK>0
465 if(RANDOM(&ld, TEST_FORK) == 0) {
466 int status;
467
468#if !USE_THR
469 pid = fork();
470#else
471 pid = fork1();
472#endif
473 if(pid > 0) {
474 /*printf("forked, waiting for %d...\n", pid);*/
475 waitpid(pid, &status, 0);
476 printf("done with %d...\n", pid);
477 if(!WIFEXITED(status)) {
478 printf("child term with signal %d\n", WTERMSIG(status));
479 exit(1);
480 }
481 return;
482 }
483 exit(0);
484 }
485#endif
486 p.m = (struct bin *)malloc(st->u.bins*sizeof(*p.m));
487 p.bins = st->u.bins;
488 p.size = st->u.size;
489 for(b=0; b<p.bins; b++) {
490 p.m[b].size = 0;
491 p.m[b].ptr = NULL;
492 if(RANDOM(&ld, 2) == 0)
493 bin_alloc(&p.m[b], RANDOM(&ld, p.size) + 1, lran2(&ld));
494 }
495 for(i=0; i<=st->u.max;) {
496#if TEST > 1
497 bin_test(&p);
498#endif
499 actions = RANDOM(&ld, ACTIONS_MAX);
500#if USE_MALLOC && MALLOC_DEBUG
501 if(actions < 2) { mallinfo(); }
502#endif
503 for(j=0; j<actions; j++) {
504 b = RANDOM(&ld, p.bins);
505 bin_free(&p.m[b]);
506 }
507 i += actions;
508 actions = RANDOM(&ld, ACTIONS_MAX);
509 for(j=0; j<actions; j++) {
510 b = RANDOM(&ld, p.bins);
511 bin_alloc(&p.m[b], RANDOM(&ld, p.size) + 1, lran2(&ld));
512#if TEST > 2
513 bin_test(&p);
514#endif
515 }
516#if 0 /* Test illegal free()s while setting MALLOC_CHECK_ */
517 for(j=0; j<8; j++) {
518 b = RANDOM(&ld, p.bins);
519 if(p.m[b].ptr) {
520 int offset = (RANDOM(&ld, 11) - 5)*8;
521 char *rogue = (char*)(p.m[b].ptr) + offset;
522 /*printf("p=%p rogue=%p\n", p.m[b].ptr, rogue);*/
523 free(rogue);
524 }
525 }
526#endif
527 i += actions;
528 }
529 for(b=0; b<p.bins; b++)
530 bin_free(&p.m[b]);
531 free(p.m);
532 if(pid == 0)
533 exit(0);
534}
535
536int n_total=0, n_total_max=N_TOTAL, n_running;
537
538int
539my_end_thread(struct thread_st *st)
540{
541 /* Thread st has finished. Start a new one. */
542#if 0
543 printf("Thread %lx terminated.\n", (long)st->id);
544#endif
545 if(n_total >= n_total_max) {
546 n_running--;
547 } else if(st->u.seed++, mthread_create(st)) {
548 printf("Creating thread #%d failed.\n", n_total);
549 exit(1);
550 } else {
551 n_total++;
552 if (verbose)
553 if(n_total%N_TOTAL_PRINT == 0)
554 printf("n_total = %8d - malloc %12d / memalign %12d / realloc %12d / calloc %12d\n",
555 n_total,
556 n_malloc, n_memalign, n_realloc, n_calloc);
557
558 }
559 return 0;
560}
561
562#if 0
563/* Protect address space for allocation of n threads by LinuxThreads. */
564static void
565protect_stack(int n)
566{
567 char buf[2048*1024];
568 char* guard;
569 size_t guard_size = 2*2048*1024UL*(n+2);
570
571 buf[0] = '\0';
572 guard = (char*)(((unsigned long)buf - 4096)& ~4095UL) - guard_size;
573 printf("Setting up stack guard at %p\n", guard);
574 if(mmap(guard, guard_size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED,
575 -1, 0)
576 != guard)
577 printf("failed!\n");
578}
579#endif
580
581int
582main(int argc, char *argv[])
583{
584 int i, bins;
585 int n_thr=N_THREADS;
586 int i_max=I_MAX;
587 unsigned long size=SIZE;
588 struct thread_st *st;
589
590#if USE_MALLOC && USE_STARTER==2
591 ptmalloc_init();
592 printf("ptmalloc_init\n");
593#endif
594
595 if((argc > 1) && (0 == strcmp(argv[1], "-h") || 0 == strcmp(argv[1], "--help")))
596 {
597 printf("%s <n-total> <n-parallel> <n-allocs> <size-max> <bins>\n\n", argv[0]);
598 printf(" n-total = total number of threads executed (default 10)\n");
599 printf(" n-parallel = number of threads running in parallel (2)\n");
600 printf(" n-allocs = number of malloc()'s / free()'s per thread (10000)\n");
601 printf(" size-max = max. size requested with malloc() in bytes (10000)\n");
602 printf(" bins = number of bins to maintain\n");
603 return 0;
604 }
605
606 if(argc > 1) n_total_max = atoi(argv[1]);
607 if(n_total_max < 1) n_thr = 1;
608 if(argc > 2) n_thr = atoi(argv[2]);
609 if(n_thr < 1) n_thr = 1;
610 if(n_thr > 100) n_thr = 100;
611 if(argc > 3) i_max = atoi(argv[3]);
612
613 if(argc > 4) size = atol(argv[4]);
614 if(size < 2) size = 2;
615
616 bins = MEMORY/(size*n_thr);
617 if(argc > 5) bins = atoi(argv[5]);
618 if(bins < 4) bins = 4;
619
620 /*protect_stack(n_thr);*/
621
622 mthread_init();
623 printf("total=%d threads=%d i_max=%d size=%ld bins=%d\n",
624 n_total_max, n_thr, i_max, size, bins);
625
626 st = (struct thread_st *)malloc(n_thr*sizeof(*st));
627 if(!st) exit(-1);
628
629#if !defined NO_THREADS && (defined __sun__ || defined sun)
630 /* I know of no other way to achieve proper concurrency with Solaris. */
631 thr_setconcurrency(n_thr);
632#endif
633
634 /* Start all n_thr threads. */
635 for(i=0; i<n_thr; i++) {
636 st[i].u.bins = bins;
637 st[i].u.max = i_max;
638 st[i].u.size = size;
639 st[i].u.seed = ((long)i_max*size + i) ^ bins;
640 st[i].sp = 0;
641 st[i].func = malloc_test;
642 if(mthread_create(&st[i])) {
643 fprintf(stderr, "Creating thread #%d failed.\n", i);
644 n_thr = i;
645 exit(1);
646 }
647 if (verbose)
648 printf("Created thread %lx.\n", (long)st[i].id);
649 }
650
651 /* Start an extra thread so we don't run out of stacks. */
652 if(0) {
653 struct thread_st lst;
654 lst.u.bins = 10; lst.u.max = 20; lst.u.size = 8000; lst.u.seed = 8999;
655 lst.sp = 0;
656 lst.func = malloc_test;
657 if(mthread_create(&lst)) {
658 fprintf(stderr, "Creating thread #%d failed.\n", i);
659 exit(1);
660 } else {
661 wait_for_thread(&lst, 1, NULL);
662 }
663 }
664
665 for(n_running=n_total=n_thr; n_running>0;) {
666 wait_for_thread(st, n_thr, my_end_thread);
667 }
668 for(i=0; i<n_thr; i++) {
669 free(st[i].sp);
670 }
671 free(st);
672#if USE_MALLOC
673 malloc_stats();
674#endif
675 if (verbose)
676 printf("Done.\n");
677 return 0;
678}
679
680/*
681 * Local variables:
682 * tab-width: 4
683 * End:
684 */
Note: See TracBrowser for help on using the repository browser.