]> git.cworth.org Git - vogl/blob - src/voglcore/rmalloc.c
Drop vogl_applaunchr.cpp and vogl_applauncher.h
[vogl] / src / voglcore / rmalloc.c
1 /* =====================================================================
2    File:        rmalloc.c
3    Author:      Rammi
4    Date:        11/16/1995 (started)
5
6    License: (This is the open source ISC license, see
7              http://en.wikipedia.org/wiki/ISC_license
8              for more info)
9
10     Copyright © 2010   Andreas M. Rammelt <rammi@hexco.de>
11
12     Permission to use, copy, modify, and/or distribute this software for any
13     purpose with or without fee is hereby granted, provided that the above
14     copyright notice and this permission notice appear in all copies.
15
16     THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
17     WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18     MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
19     ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20     WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21     ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
22     OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23
24    Content: Debug wrapper functions for the malloc library.
25       For more information see rmalloc.h
26
27    Last Change: $Date: 2010/10/08 09:38:23 $
28    History:     $Log: rmalloc.c,v $
29    History:     Revision 1.16  2010/10/08 09:38:23      rammi
30    History:     Clarified license to ISC license
31    History:
32    History:     Revision 1.15  2009/07/06 12:05:48      rammi
33    History:     Incremented version.
34    History:
35    History:     Revision 1.14  2009/07/06 12:04:36      rammi
36    History:     Avoided abort when calling RM_STAT before something was allocated.
37    History:     Thanks to Matthias Bilger for pointing out the error.
38    History:     Some other messages when calling functions without proper initialization are added, too.
39    History:
40    History:     Revision 1.13  2008/12/28 13:00:26      rammi
41    History:     Fixed Typo in rmalloc.h: unded --> undef
42    History:
43    History:     Revision 1.12  2008/12/27 15:57:40      rammi
44    History:     Updated version
45    History:
46    History:     Revision 1.11  2008/12/27 15:56:44      rammi
47    History:     Embedded patch from F. Culot to avoid overflow in Rcalloc()
48    History:
49    History:     Revision 1.10  2008/09/30 12:37:23      rammi
50    History:     Added Peter Wehrfritz' changes: new SILENT mode and patch against compiler warning if TEST_DEPTH==0
51    History:
52    History:     Revision 1.9  2006/01/29 18:56:12  rammi
53    History:     Streamlined various things due to a proposal by Brant L Gurganus. Thanks Brant!
54    History:
55    History:     Revision 1.8  2003/01/31 15:47:52  rammi
56    History:     Changed signature of Rmalloc_set_flags() and Rmalloc_retag to return pointer.
57    History:
58    History:     Revision 1.7  2003/01/31 15:04:20  rammi
59    History:     Fixed unclosed comment.
60    History:
61    History:     Revision 1.6  2003/01/31 14:51:48  rammi
62    History:     Updated version to 1.16
63    History:
64    History:     Revision 1.5  2003/01/31 14:49:00  rammi
65    History:     Unset RM_STATIC flag in realloc to avoid warning on free
66    History:
67    History:     Revision 1.4  2002/04/22 17:39:34  rammi
68    History:     Added output of BREAK_GENERATION environment variable when used
69    History:
70    History:     Revision 1.3  2002/04/22 16:28:06  rammi
71    History:     Finetuning of generations feature
72    History:
73    History:     Revision 1.2  2002/04/22 15:26:16  rammi
74    History:     Added Karl Brace's generations feature.
75    History:
76
77    Pre-CVS history:
78       04/11/1996 (Rammi)
79       Changed to hashed table for faster access
80       04/15/1996 (Rammi)
81       Included statistics
82       08/16/1997 (Rammi)
83       Automatic output of used memory on exit
84       08/25/1997 (Rammi)
85       Catching signals in critical situations (partly)
86       02/18/1998 (Rammi)
87       Testing memory before outputting statistic
88       Overworked signal handling in IsPossibleFilePos()
89       Made it unnecessary of changing all mallocs etc
90       to upper case
91       02/19/1998 (Rammi)
92       Little changes to compile on Alpha (64bit) without
93       warnings
94       03/10/1998 (Rammi)
95       Added comments.
96       03/24/1998 (Rammi)
97       Allowed compilation without WITH_FLAGS
98       04/07/1998 (Rammi)
99       All output goes to stderr.
100 .               1.11beta is released as 1.11!
101       05/28/1998 (Rammi)
102       Changed comments to english for public release
103       Added some more signal handling.
104       06/01/1998 (Rammi)
105       Added output of (flagged) strings in ELOQUENT mode.
106       Changed all names from my_... to R...
107       This is version 1.12!
108       11/13/1998 (Rammi)
109       Multiple defined label when using ELOQUENT mode now fixed.
110       Added getcwd wrapper as a prototype how to handle library
111       functions returning heap memory.
112       This is version 1.13!
113          06/10/99 (Rammi)
114       The getcwd wrapper had a bug as Greg Silverman pointed
115       out (I should better have tested it!). Also fixed a
116       missing prototype and improved the signal handling in
117       rtest to allow receiving more signals while handling
118       one.
119    ===================================================================== */
120
121 /* =========
122    INCLUDEs:
123    ========= */
124
125 #include <stdio.h>
126 #include <unistd.h>
127 #include <string.h>
128 #include <strings.h>
129 #include <assert.h>
130 #include <setjmp.h>
131 #include <signal.h>
132 #include <stdint.h>
133
134 #undef MALLOC_DEBUG        /* here we need the correct malloc functions */
135 #define RM_NEED_PROTOTYPES /* but we want to compare prototypes */
136 #include "rmalloc.h"
137
138 #ifdef __cplusplus
139 extern "C" {
140 #endif
141
142 /* ========
143            DEFINEs:
144            ======== */
145
146 /* Actual version */
147 #define VERSION "1.21"
148
149 /* ================================================================== */
150 /* ============ Switch settings for different behaviours ============ */
151 /* ============            Please set as needed                          ============ */
152 /* ================================================================== */
153
154 /* This switch sets, how and when the allocated blocks are tested
155          * on correctness. Each block is tested at least when
156          * reallocating/freeing it.
157          * Possible values:
158          *      0:              Minimum testing. Uses less memory, but
159          *                                      does not allow statistics.
160          *      1:              Extra testing possible by using RM_TEST
161          *                                      macro. Statistics possible.
162          *      2:              Testing ALL blocks on every malloc/free.
163          *                                      Statistics possible.
164          */
165 #ifndef RM_TEST_DEPTH
166 #define RM_TEST_DEPTH 1
167 #endif
168
169 #ifndef RM_TEST_ALL_FREQUENCY
170 // Controls the frequency of heap testing when RM_TEST_DEPTH==2, where 1 is on every operation
171 #define RM_TEST_ALL_FREQUENCY 10000
172 #endif
173
174 /* This switch sets whether Karl's generations feature should be used
175          * See the HTML doc for a indepth explanation of generations.
176          * If set generations are used, otherwise no generations are included.
177          */
178 #define GENERATIONS
179
180 #ifdef GENERATIONS
181 /* BREAK_GENERATION_COND is the condition to find the generation  you are
182          * interested in.
183          * Set your debugger to the function rmalloc_generation() to find out which
184          * function stack creates that generation.
185          * You can either set it as a comparision directly to the number of a
186          * generation (known from a previous run), a comparision to the function
187          * GetBreakGenerationEnv() which reads the break generation from the environment
188          * variable BREAK_GENERATION or don't set it so the break feature is not used.
189          * The macro form allows for more complicated conditions, see example below.
190          */
191 #define BREAK_GENERATION_COND(nr) ((nr) == GetBreakGenerationEnv())
192 /* #define BREAK_GENERATION_COND(nr) ((nr) == 125) */
193 /* #define BREAK_GENERATION_COND(nr) ((nr) == 42  ||  (nr) == 4711) */
194
195 /* The maximum number of generations you'd like to see in the statistics */
196 #define MAX_STAT_GENERATIONS 3
197
198 #endif /* GENERATIONS */
199
200 /* Switch on EXTENTED alloc information. (Makes sense if an actual
201          * error is observed such as a multiple free)
202          */
203 /*      #define ELOQUENT */
204
205 /* Only show errors. This doesn't output the initializing output and the
206          * end statistic if there was a leak encountered.
207          */
208 /*      #define SILENT */
209
210 /* Allows setting of special flags with the RM_SET_FLAGS macro.
211          * Needs more memory.
212          */
213 #define WITH_FLAGS
214
215 /* Allows realloc(NULL, ...)
216          * Posix allows this but there are some old malloc libraries
217          * which crash on this. Switch on if you want to be compatible.
218          */
219 #define ALLOW_REALLOC_NULL
220
221 /* Allows free(NULL)
222          * I still consider this an error in my progs because I use
223          * NULL always as a very special value.
224          */
225 #define ALLOW_FREE_NULL
226
227 /* ================================================================== */
228 /* ========================  Other Defines      ========================= */
229 /* ================================================================== */
230
231 /* Alignment */
232 #define ALIGNMENT sizeof(void *) * 2
233
234 /* Output message header: */
235 #define HEAD "<MALLOC_DEBUG>\t"
236
237 /* Alignment padding: */
238 //#define ALIGN(s)      (((s+ALIGNMENT-1)/ALIGNMENT)*ALIGNMENT)
239 #define ALIGN(s) ((s + ALIGNMENT - 1) & (~(ALIGNMENT - 1)))
240
241 /* Magic marker for block start: */
242 #define PREV_STOP 0x55555555
243
244 /* Additional space for block begin to keep alignment: */
245 #define START_SPACE ALIGN(sizeof(begin))
246
247 /* Additional space at end of block */
248 #define END_SPACE (sizeof(End))
249
250 /* Overall additional space per block: */
251 #define EXTRA_SPACE (START_SPACE + END_SPACE)
252
253 /* Hashtable size: */
254 //#define HASHSIZE      257
255 //#define HASHSIZE      65537
256 #define HASHSIZE 98947
257
258 /* Hash function: */
259 //#define HASH(p)               ((((unsigned long)(p))/ALIGNMENT)%HASHSIZE)
260 static inline unsigned int bitmix32(unsigned int a)
261 {
262     a -= (a << 6);
263     a ^= (a >> 17);
264     a -= (a << 9);
265     a ^= (a << 4);
266     a -= (a << 3);
267     a ^= (a << 10);
268     a ^= (a >> 15);
269     return a;
270 }
271
272 #define HASH(p) (bitmix32((((size_t)(p)) / ALIGNMENT)) % HASHSIZE)
273
274 /* ==========================
275            STRUCTs, TYPEDEFs & ENUMs:
276            ========================== */
277
278 /* This Information is added to the beginning of every
279          * allocated block.
280          */
281 typedef struct _begin
282 {
283     unsigned StpA; /* Magic bytes */
284 #if RM_TEST_DEPTH > 0
285     struct _begin *Next, /* for linking in forward */
286         *Prev;           /* and backward direction */
287 #endif
288     const char *File; /* Filepos of allocation command */
289     size_t Size;      /* Size demanded */
290 #ifdef GENERATIONS
291     unsigned Generation; /* All mallocs are numbered */
292 #endif
293 #ifdef WITH_FLAGS
294     unsigned Flags; /* Special flags */
295 #endif
296     unsigned StpB; /* Magic bytes */
297 } begin;
298
299 /*
300          * Global data.
301          */
302 typedef struct _global
303 {
304     unsigned isInitialized; /* Flag: already initialized? */
305     unsigned BlockCount;    /* Number of allocated blocks */
306 } global;
307
308 /* =======
309            CONSTs:
310            ======= */
311
312 /* Magic block end: */
313 static unsigned char End[] =
314     {
315         0xA5, 0xA5, 0xA5, 0xA5, /* odd */
316         0x5B, 0x5B, 0x5B, 0x5B, /* odd */
317         0xAB, 0xAB, 0xAB, 0xAB, /* odd */
318         0xAA, 0x55, 0xAA, 0x55  /* odd */
319     };
320
321 /* ========
322            GLOBALs:
323            ======== */
324
325 #ifdef GENERATIONS
326 /* The current generation. This is simply incremented which each call. */
327 static unsigned cur_generation = 0;
328 #endif
329
330 /* =======
331            LOCALs:
332            ======= */
333
334 #if RM_TEST_DEPTH > 0
335 /* Stop marker for linked list of allocated blocks: */
336 static begin ChainTempl =
337     {
338         PREV_STOP,
339         &ChainTempl,
340         &ChainTempl,
341         "<Special>",
342         0,
343 #ifdef GENERATIONS
344         0,
345 #endif
346 #ifdef WITH_FLAGS
347         0,
348 #endif
349         PREV_STOP
350     };
351
352 /* Hashed linked lists: */
353 static begin Chain[HASHSIZE];
354
355 /* Global data used: */
356 static global Global =
357     {
358         0, /* is initialized?   */
359         0  /* number of blocks */
360     };
361 #endif /* RM_TEST_DEPTH */
362
363 /* Internally used longjmp target used if errors occure. */
364 static jmp_buf errorbuf;
365
366 /* ========
367            FORWARD:
368            ======== */
369
370 static int FindBlk(const unsigned char *P, const char *file);
371
372 /* ===============================================================
373                  IMPLEMENTATION
374            =============================================================== */
375
376 static void *DefMalloc(size_t size, void *pUser)
377 {
378     (void)pUser;
379     return malloc(size);
380 }
381
382 static void DefFree(void *p, void *pUser)
383 {
384     (void)pUser;
385     free(p);
386 }
387
388 static void *DefRealloc(void *ptr, size_t size, void *pUser)
389 {
390     (void)pUser;
391     return realloc(ptr, size);
392 }
393
394 static rmalloc_malloc_func_t g_malloc_func = DefMalloc;
395 static rmalloc_free_func_t g_free_func = DefFree;
396 static rmalloc_realloc_func_t g_realloc_func = DefRealloc;
397 static void *g_pMalloc_user_ptr;
398
399 void Rmalloc_set_callbacks(rmalloc_malloc_func_t pMalloc, rmalloc_free_func_t pFree, rmalloc_realloc_func_t pRealloc, void *pUser)
400 {
401     g_malloc_func = pMalloc;
402     g_free_func = pFree;
403     g_realloc_func = pRealloc;
404     g_pMalloc_user_ptr = pUser;
405 }
406
407 #define CALL_MALLOC(s) (*g_malloc_func)(s, g_pMalloc_user_ptr)
408 #define CALL_FREE(p) (*g_free_func)(p, g_pMalloc_user_ptr)
409 #define CALL_REALLOC(p, s) (*g_realloc_func)(p, s, g_pMalloc_user_ptr)
410
411 /* =============================================================================
412            Function:            FatalSignal // local //
413            Author:              Rammi
414            Date:                08/25/1997
415
416            Return:              ---
417
418            Parameter:           signum    signal number
419
420            Purpose:     Signal handler für fatal signals (SIGBUS, SIGSEGV)
421            ============================================================================= */
422 static void FatalSignal(int signum)
423 {
424     /* --- jump to a save place --- */
425     longjmp(errorbuf, signum);
426 }
427
428 /* =============================================================================
429            Function:            IsPossibleFilePos       // local //
430            Author:              Rammi
431            Date:                11/30/1996
432
433            Return:              != 0    possibly ok
434                           0             seems not so
435
436            Parameter:           file    possible filename
437                  size   possible size
438
439            Purpose:     Decide whether file could be a filename and
440                  size a block size.
441            ============================================================================= */
442 static int IsPossibleFilePos(const char *file, int size)
443 {
444     void (*old_sigsegv_handler)(int) = SIG_DFL;
445     void (*old_sigbus_handler)(int) = SIG_DFL;
446     char *dp;
447     int ret;
448
449     if (setjmp(errorbuf))
450     {
451         /* uh oh, we got a kick in the ass */
452         signal(SIGSEGV, old_sigsegv_handler);
453         signal(SIGBUS, old_sigbus_handler);
454         return 0;
455     }
456
457     /* --- the following is dangerous! So catch signals --- */
458     old_sigsegv_handler = signal(SIGSEGV, FatalSignal);
459     old_sigbus_handler = signal(SIGBUS, FatalSignal);
460
461     if (!file)
462     {
463         ret = 0;
464     }
465     else
466     {
467         dp = strchr(file, ':'); /* file pos needs : */
468
469         ret = (dp && dp - file > 3 &&
470                (!strncmp(dp - 2, ".c", 2) || !strncmp(dp - 2, ".h", 2) || !strncmp(dp - 4, ".inc", 4) || !strncmp(dp - 4, ".inl", 4) || !strncmp(dp - 4, ".cpp", 4) || !strncmp(dp - 4, ".cxx", 4) || !strncmp(dp - 4, ".hpp", 4)) && atoi(dp + 1) > 0 && size >= 0);
471     }
472
473     /* --- danger is over! --- */
474     signal(SIGSEGV, old_sigsegv_handler);
475     signal(SIGBUS, old_sigbus_handler);
476
477     return ret;
478 }
479
480 #ifdef GENERATIONS
481 /* =============================================================================
482            Function:            GetBreakGenerationEnv   // lokal //
483            Author:              Rammi
484            Date:                04/22/2002
485
486            Return:                      the content of the BREAK_GENERATION environment variable
487                           or 0
488
489            Parameter:           ---
490
491            Purpose:     Return the content of the environment variable
492                           BREAK_GENERATION (a number indicating which is the
493                           generation you want to break with the debugger).
494
495            ============================================================================= */
496 static unsigned GetBreakGenerationEnv(void)
497 {
498     /* The environment variable is buffered here for faster access. */
499     static char *breakGenerationEnv = (char *)-1;
500     /** The result of the conversion to unsigned is buffered here. */
501     static unsigned result = 0;
502
503     if (breakGenerationEnv == (char *)-1)
504     {
505         /* 1st call: get the environment variable */
506         breakGenerationEnv = getenv("BREAK_GENERATION");
507         if (breakGenerationEnv != NULL)
508         {
509             /* try conversion */
510             result = atoi(breakGenerationEnv);
511             fprintf(stderr,
512                     HEAD "Using environment variable BREAK_GENERATION=%d\n",
513                     result);
514         }
515     }
516
517     return result;
518 }
519 #endif /* GENERATIONS */
520
521 /* =============================================================================
522            Function:            ControlBlock    // lokal //
523            Author:              Rammi
524            Date:                11/16/1995
525
526            Return:              ---
527            Parameter:           Bkl Pos of allocated block (original)
528                  file   file pos from where initial lib function
529                     was called
530
531            Purpose:     Control integrity of block
532
533            ============================================================================= */
534 static void ControlBlock(begin *B, const char *file)
535 {
536     unsigned char *p = (((unsigned char *)B) + START_SPACE);
537 #if RM_TEST_DEPTH > 0
538     int DoAbort = 0;
539 #endif
540     /* === the very beginning === */
541     if (B->StpA != PREV_STOP)
542     {
543 #if RM_TEST_DEPTH > 0
544         DoAbort = 1;
545 #endif
546         fprintf(stderr, HEAD
547                 "Corrupted block begin (overwritten from elsewhere)\n"
548                 "\tshould be: %08x\n"
549                 "\tis:            %08x\n"
550 #ifdef GENERATIONS
551                 "\tblock was allocated in %s [%u Bytes, generation %u]\n"
552 #else
553                 "\tblock was allocated in %s [%u Bytes]\n"
554 #endif
555                 "\terror was detected in  %s\n",
556                 PREV_STOP,
557                 B->StpA,
558                 B->File,
559                 (unsigned)B->Size,
560 #ifdef GENERATIONS
561                 B->Generation,
562 #endif
563                 file);
564     }
565
566     /* === begin of user data === */
567     if (B->StpB != PREV_STOP)
568     {
569 #if RM_TEST_DEPTH > 0
570         DoAbort = 1;
571 #endif
572         fprintf(stderr, HEAD
573                 "Corrupted block begin (possibly written back)\n"
574                 "\tshould be: %08x\n"
575                 "\tis:            %08x\n"
576 #ifdef GENERATIONS
577                 "\tblock was allocated in %s [%u Bytes, generation %u]\n"
578 #else
579                 "\tblock was allocated in %s [%u Bytes]\n"
580 #endif
581                 "\terror was detected in  %s\n",
582                 PREV_STOP,
583                 B->StpB,
584                 B->File,
585                 (unsigned)B->Size,
586 #ifdef GENERATIONS
587                 B->Generation,
588 #endif
589                 file);
590     }
591
592     /* === end of user data === */
593     if (memcmp(p + B->Size, &End, END_SPACE) != 0)
594     {
595         unsigned char *E = (unsigned char *)(p + B->Size);
596         unsigned i;
597         int found = 0;
598 #if RM_TEST_DEPTH > 0
599         DoAbort = 1;
600 #endif
601         fprintf(stderr, HEAD
602                 "Corrupted block end (possibly written past the end)\n"
603                 "\tshould be:");
604         for (i = 0; i < END_SPACE; i++)
605         {
606             fprintf(stderr, i % 4 ? "%02x" : " %02x", End[i]);
607         }
608
609         fprintf(stderr,
610                 "\n\tis:           ");
611         for (i = 0; i < END_SPACE; i++)
612         {
613             fprintf(stderr, i % sizeof(int) ? "%02x" : " %02x", E[i]);
614         }
615         fprintf(stderr, "\n"
616 #ifdef GENERATIONS
617                         "\tblock was allocated in %s [%u Bytes, generation %u]\n"
618 #else
619                         "\tblock was allocated in %s [%u Bytes]\n"
620 #endif
621                         "\terror was detected in %s\n",
622                 B->File,
623                 (unsigned)B->Size,
624 #ifdef GENERATIONS
625                 B->Generation,
626 #endif
627                 file);
628
629 #if RM_TEST_DEPTH > 0
630         if (!((unsigned long)E % sizeof(void *)) &&
631             !(*(unsigned long *)E % sizeof(void *))) /* because of alignment */
632         {
633             /* Special service: look if memory was overwritten with pointer */
634             if (FindBlk(*(unsigned char **)E, file))
635             {
636                 begin *b = (begin *)((*(unsigned char **)E) - START_SPACE);
637                 if (IsPossibleFilePos(b->File, b->Size))
638                 {
639                     fprintf(stderr,
640                             "\tFirst %d bytes of overwritten memory can be interpreted\n"
641                             "\t\tas a pointer to a block "
642                             " allocated in:\n"
643 #ifdef GENERATIONS
644                             "\t\t%s [%u Bytes, generation %u]\n",
645 #else
646                             "\t\t%s [%u Bytes]\n",
647 #endif
648                             (unsigned int)sizeof(void *),
649                             b->File,
650                             (unsigned)b->Size
651 #ifdef GENERATIONS
652                             ,
653                             b->Generation
654 #endif
655                             );
656                     found = 1;
657                 }
658             }
659         }
660         if (!found)
661 #endif
662         {
663             /* Look, what we can find... */
664             int j;
665
666             for (j = END_SPACE - 1; j >= 0; j--)
667             {
668                 if (E[j] != End[j])
669                 {
670                     break;
671                 }
672             }
673             if (j >= 0 && !E[j])
674             {
675                 /* Ends with '\0', so it's possibly a string */
676                 if (j > 0)
677                 {
678                     while (--j >= 0)
679                     {
680                         if (!E[j])
681                         {
682                             break;
683                         }
684                     }
685                     if (j < 0)
686                     {
687                         fprintf(stderr,
688                                 "\tLooks somewhat like a too long string,\n"
689                                 "\t\tending with \"%s\"\n",
690                                 E);
691                     }
692                 }
693                 else
694                 {
695                     /* Off by one? */
696                     fprintf(stderr,
697                             "\tLooks like string allocated one byte too short\n"
698                             "\t\t(forgetting the nul byte)\n");
699                 }
700             }
701         }
702     }
703
704 #if RM_TEST_DEPTH > 0
705     /* Die LOUD */
706     if (DoAbort)
707     {
708         abort();
709     }
710 #endif
711 }
712
713 #if RM_TEST_DEPTH > 0
714 void Rmalloc_stat(const char *file);
715
716 /* =============================================================================
717            Function:            Exit // local //
718            Author:              Rammi
719            Date:                08/19/1997
720
721            Return:              ---
722            Parameter:           ---
723
724            Purpose:     Function called on exit
725            ============================================================================= */
726 static void Exit(void)
727 {
728 #ifdef SILENT
729     if (!Global.BlockCount)
730     {
731         return;
732     }
733 #endif
734
735     Rmalloc_stat("[atexit]"); /* show statistics */
736 }
737
738 /* =============================================================================
739            Function:            Initialize      // local //
740            Author:              Rammi
741            Date:                11.04.1996
742
743            Return:              ---
744            Parameter:           ---
745
746            Purpose:     Necessary initializations
747
748            ============================================================================= */
749 static void Initialize(void)
750 {
751     int i;
752
753 #ifndef SILENT
754     fprintf(stderr,
755             HEAD "rmalloc -- malloc wrapper V " VERSION "\n"
756                  "\tby Rammi <mailto:rammi@hexco.de>\n"
757                  "\tCompiled with following options:\n"
758 #if RM_TEST_DEPTH == 1
759                  "\t\ttesting:\tonly actual block\n"
760 #elif RM_TEST_DEPTH == 2
761                  "\t\ttesting:\tall allocated blocks\n"
762 #else
763                  "\t\ttesting:\tcomment missing in " __FILE__ ":" INT2STRING(__LINE__) "\n"
764 #endif
765 #ifdef GENERATIONS
766                  "\t\tgenerations:\tON\n"
767 #else
768                  "\t\tgenerations:\tOFF\n"
769 #endif
770 #ifdef ELOQUENT
771                  "\t\teloquence:\tON\n"
772 #else
773                  "\t\teloquence:\tOFF\n"
774 #endif
775 #ifdef ALLOW_REALLOC_NULL
776                  "\t\trealloc(0):\tALLOWED\n"
777 #else
778                  "\t\trealloc(0):\tNOT ALLOWED\n"
779 #endif
780 #ifdef ALLOW_FREE_NULL
781                  "\t\tfree(0):\tALLOWED\n"
782 #else
783                  "\t\tfree(0):\tNOT ALLOWED\n"
784 #endif
785 #ifdef WITH_FLAGS
786                  "\t\tflags:  \tUSED\n"
787 #else
788                  "\t\tflags:  \tUNUSED\n"
789 #endif
790                  "\t\talignment:\t" INT2STRING(ALIGNMENT) "\n"
791                                                           "\t\tpre space:\t%d\n"
792                                                           "\t\tpost space:\t%d\n"
793                                                           "\t\thash tab size:\t" INT2STRING(HASHSIZE) "\n\n",
794             (int)START_SPACE, (int)END_SPACE);
795 #endif /* ndef SILENT */
796
797     /* --- init list heads --- */
798     for (i = 0; i < HASHSIZE; i++)
799     {
800         memcpy(Chain + i, &ChainTempl, sizeof(begin));
801         Chain[i].Next = Chain[i].Prev = Chain + i;
802     }
803
804     /* --- show statistics at exit --- */
805     (void)atexit(Exit);
806
807     Global.isInitialized = 1;
808 }
809
810 /* =============================================================================
811            Function:            TestAll         // local //
812            Author:              Rammi
813            Date:                16.11.1995
814
815            Return:              ---
816            Parameter:           file            file pos where lib function was
817                        called
818
819            Purpose:             Test all allocated blocks for inconsistencies
820            ============================================================================= */
821 static void TestAll(const char *file)
822 {
823     begin *B; /* Block iterator */
824     int i;    /* Hash iterator */
825
826     /* make sure everything is initialized */
827     if (!Global.isInitialized)
828     {
829         Initialize();
830     }
831
832     for (i = 0; i < HASHSIZE; i++)
833     {
834         B = Chain[i].Next;
835
836         /* === Once around the circle === */
837         while (B != &Chain[i])
838         {
839             ControlBlock(B, file);
840             B = B->Next;
841         }
842     }
843 }
844
845 static uint gTestAllCounter;
846
847 static void TestAllConditionally(const char *file)
848 {
849     if (!gTestAllCounter)
850     {
851         TestAll(file);
852         gTestAllCounter = RM_TEST_ALL_FREQUENCY;
853     }
854     gTestAllCounter--;
855 }
856
857 /* =============================================================================
858            Function:            AddBlk          // local //
859            Author:              Rammi
860            Date:                16.11.1995
861
862            Return:              ---
863            Parameter:           Blk     New block (original pos.)
864                  file           called from
865
866            Purpose:     Add new block to the list
867            ============================================================================= */
868 static void AddBlk(begin *Blk, const char *file)
869 {
870     int hash = HASH(Blk); /* hash val */
871     (void)file;
872
873     /* make sure everything is initialized */
874     if (!Global.isInitialized)
875     {
876         Initialize();
877     }
878
879 #if RM_TEST_DEPTH > 1
880     TestAllConditionally(file);
881 #else
882     /* prevent compiler warnings about unused variables */
883     file = NULL;
884 #endif
885     /* --- insert it --- */
886     Blk->Next = Chain[hash].Next;
887     Blk->Prev = &Chain[hash];
888     Chain[hash].Next->Prev = Blk;
889     Chain[hash].Next = Blk;
890
891     Global.BlockCount++;
892 }
893
894 /* =============================================================================
895            Function:            DelBlk          // local //
896            Author:              Rammi
897            Date:                16.11.1995
898
899            Return:              ---
900
901            Parameter:           Blk     block to remove
902                  file           called from
903
904            Purpose:     Remove block from list.
905                  React angry if block is unknown
906            ============================================================================= */
907 static void DelBlk(begin *Blk, const char *file)
908 {
909     begin *B;             /* run var    */
910     int hash = HASH(Blk); /* hash val */
911
912     if (!Global.isInitialized)
913     {
914         fprintf(stderr, HEAD
915                 "Calling free without having allocated block via rmalloc\n"
916                 "in call from %s",
917                 file);
918         abort();
919     }
920
921     /* look if block is known */
922     for (B = Chain[hash].Next; B != &Chain[hash]; B = B->Next)
923     {
924         if ((B->StpA != PREV_STOP) || (B->StpB != PREV_STOP))
925         {
926             ControlBlock(B, file);
927         }
928
929         if (B == Blk)
930         {
931             goto found_actual_block; /* friendly goto */
932         }
933     }
934
935     /* not found */
936     fprintf(stderr, HEAD
937             "Double or false delete\n"
938             "\tHeap adress of block: %p\n"
939             "\tDetected in %s\n",
940             ((char *)Blk) + START_SPACE, file);
941     {
942         void (*old_sigsegv_handler)(int) = SIG_DFL;
943         void (*old_sigbus_handler)(int) = SIG_DFL;
944
945         if (setjmp(errorbuf))
946         {
947             /* uh oh, we got a kick in the ass */
948             signal(SIGSEGV, old_sigsegv_handler);
949             signal(SIGBUS, old_sigbus_handler);
950         }
951         else
952         {
953             /* --- the following is dangerous! So catch signals --- */
954             old_sigsegv_handler = signal(SIGSEGV, FatalSignal);
955             old_sigbus_handler = signal(SIGBUS, FatalSignal);
956
957             if (IsPossibleFilePos(Blk->File, Blk->Size))
958             {
959                 fprintf(stderr,
960                         "\tTrying identification (may be incorrect!):\n"
961                         "\t\tAllocated in %s [%u Bytes]\n",
962                         Blk->File, (unsigned)Blk->Size);
963 #ifdef GENERATIONS
964                 fprintf(stderr, "\t\tGeneration: %u\n", (unsigned)Blk->Generation);
965 #endif
966             }
967             signal(SIGSEGV, old_sigsegv_handler);
968             signal(SIGBUS, old_sigbus_handler);
969         }
970     }
971     abort(); /* die loud */
972
973 found_actual_block:
974 #if RM_TEST_DEPTH > 1
975     /* check everything */
976     TestAllConditionally(file);
977 #else
978     /* test integrity of actual block */
979     ControlBlock(Blk, file);
980 #endif
981
982     /* remove: */
983     Blk->Next->Prev = Blk->Prev;
984     Blk->Prev->Next = Blk->Next;
985
986     Global.BlockCount--;
987
988 #ifdef ELOQUENT
989     fprintf(stderr,
990             HEAD "Delete: %d Bytes allocated in %s (from %s)\n",
991             Blk->Size, Blk->File, file);
992 #ifdef WITH_FLAGS
993     if (Blk->Flags & RM_STRING)
994     {
995         char *c;
996         /* look for eos */
997         for (c = (char *)Blk + START_SPACE;
998              c - (char *)Blk + START_SPACE < Blk->Size;
999              c++)
1000         {
1001             if (!*c)
1002             {
1003                 fprintf(stderr,
1004                         HEAD "\tContains string: \"%s\"\n",
1005                         (char *)Blk + START_SPACE);
1006                 goto found_old_block;
1007             }
1008         }
1009         /* not found */
1010         fprintf(stderr,
1011                 HEAD "\tContains string without null byte\n");
1012     found_old_block:
1013         ;
1014     }
1015 #endif /* WITH_FLAGS */
1016 #endif /* ELOQUENT */
1017
1018 #ifdef WITH_FLAGS
1019     if (Blk->Flags & RM_STATIC)
1020     {
1021         fprintf(stderr,
1022                 HEAD "WARNING: freeing block marked as STATIC (in %s)\n", file);
1023     }
1024 #endif /* WITH_FLAGS */
1025 }
1026
1027 /* =============================================================================
1028            Function:            FindBlk         // local //
1029            Author:              Rammi
1030            Date:                11/30/1996
1031
1032            Return:              0                               not found
1033                           1                             found
1034
1035            Parameter:           P               block (user pos)
1036
1037            Purpose:     look if block is known
1038            ============================================================================= */
1039 static int FindBlk(const unsigned char *P, const char *file)
1040 {
1041     begin *B;
1042     const begin *Blk = (const begin *)(P - START_SPACE);
1043     int hash = HASH(Blk);
1044
1045     /* look if block is known */
1046     for (B = Chain[hash].Next; B != &Chain[hash]; B = B->Next)
1047     {
1048         if ((B->StpA != PREV_STOP) || (B->StpB != PREV_STOP))
1049         {
1050             ControlBlock(B, file);
1051         }
1052
1053         if (B == Blk)
1054         {
1055             return 1;
1056         }
1057     }
1058     return 0;
1059 }
1060 #endif /* RM_TEST_DEPTH > 0 */
1061
1062 #ifdef GENERATIONS
1063 /* =============================================================================
1064            Function:            rmalloc_generation              // local //
1065            Author:              Karl Brace
1066            Date:                04/22/2002
1067
1068            Return:              ---
1069
1070            Parameter:           Blk     pointer to block
1071
1072            Purpose:     Breakpoint for debugger if using Karl's generations
1073                           feature.
1074            ============================================================================= */
1075 void rmalloc_generation(void *Blk)
1076 {
1077     fprintf(stderr, HEAD "Allocating Block with generation %u...\n",
1078             ((begin *)Blk)->Generation);
1079 }
1080
1081 #endif /* GENERATIONS */
1082
1083 /* =============================================================================
1084            Function:            SetBlk          // local //
1085            Author:              Rammi
1086            Date:                11/16/1995
1087
1088            Return:              pointer to block (user pos.)
1089
1090            Parameter:           Blk     pointer to block (original pos.)
1091                  size           size (user)
1092                  file           called from
1093                  flags                  flags (when compiled WITH_FLAGS)
1094
1095            Purpose:     Set our internal information
1096
1097            ============================================================================= */
1098 #ifdef WITH_FLAGS
1099 static void *SetBlk(void *Blk, size_t size, const char *file, unsigned flags)
1100 #else
1101 static void *SetBlk(void *Blk, size_t size, const char *file)
1102 #endif
1103 {
1104
1105     ((begin *)Blk)->StpA = PREV_STOP;
1106 #ifdef GENERATIONS
1107     ((begin *)Blk)->Generation = ++cur_generation;
1108 #endif
1109     ((begin *)Blk)->File = file;
1110     ((begin *)Blk)->Size = size;
1111 #ifdef WITH_FLAGS
1112     ((begin *)Blk)->Flags = flags;
1113 #endif
1114     ((begin *)Blk)->StpB = PREV_STOP;
1115     memcpy(((char *)Blk) + START_SPACE + size, End, END_SPACE);
1116
1117 #if RM_TEST_DEPTH > 0
1118     AddBlk((begin *)Blk, file);
1119 #endif
1120 #ifdef ELOQUENT
1121     fprintf(stderr,
1122             HEAD "Adding: %p, %d Bytes (from %s)\n",
1123             ((char *)Blk) + START_SPACE, size, file);
1124 #endif /* ELOQUENT */
1125 #ifdef GENERATIONS
1126     if (BREAK_GENERATION_COND(cur_generation))
1127     {
1128         rmalloc_generation(Blk);
1129     }
1130 #endif
1131     return ((char *)Blk) + START_SPACE;
1132 }
1133
1134 /* =============================================================================
1135            Function:            Rmalloc // external //
1136            Author:              Rammi
1137            Date:                11/16/1995
1138
1139            Return:              New prepared memory block with size size (user)
1140
1141            Parameter:           size            demanded size
1142                  file           called from where?
1143
1144            Purpose:     wrapper for malloc
1145            ============================================================================= */
1146 void *Rmalloc(size_t size, const char *file)
1147 {
1148     void *ret; /* ret val */
1149
1150     if (size == 0)
1151     {
1152         fprintf(stderr, HEAD "WARNING: malloc() demands 0 Bytes (in %s)\n", file);
1153     }
1154
1155     ret = CALL_MALLOC(size + EXTRA_SPACE); /* get extended block  */
1156
1157     if (ret)
1158     {
1159 /* initialize */
1160 #ifdef WITH_FLAGS
1161         return SetBlk(ret, size, file, 0);
1162 #else
1163         return SetBlk(ret, size, file);
1164 #endif
1165     }
1166     else
1167     {
1168         fprintf(stderr,
1169                 HEAD "WARNING: Out of memory! Returning NULL (in %s)\n", file);
1170         return NULL;
1171     }
1172 }
1173
1174 /* =============================================================================
1175            Function:            Rcalloc         // external //
1176            Author:              Rammi
1177            Date:                11/16/1995
1178
1179            Return:              New (cleared) memory block of size nelem*size
1180
1181            Parameter:           nelem           nr blocks (as stupid as calloc)
1182                  size           size of one block
1183                  file           called from
1184
1185            Purpose:     Wrapper function for calloc
1186            ============================================================================= */
1187 void *Rcalloc(size_t nelem, size_t size, const char *file)
1188 {
1189     void *ret;
1190
1191     /* check for overflow here (patch from Frédéric Culot) */
1192     if (size && nelem > SIZE_MAX / size)
1193     {
1194         fprintf(stderr,
1195                 HEAD "WARNING: calloc() overflow! Returning NULL (in %s)\n", file);
1196         return NULL;
1197     }
1198
1199     /* calculate correct size now */
1200     size *= nelem;
1201
1202     if (size == 0)
1203     {
1204         fprintf(stderr,
1205                 HEAD "WARNING: calloc() demands 0 Bytes (in %s)\n", file);
1206     }
1207
1208     /* Rmalloc makes nearly all the work */
1209     ret = Rmalloc(size, file);
1210
1211     if (ret)
1212     {
1213         /* clear */
1214         memset(ret, 0, size);
1215         return ret;
1216     }
1217     else
1218     {
1219         fprintf(stderr,
1220                 HEAD "WARNING: Out of memory! Returning NULL (in %s)\n", file);
1221         return NULL;
1222     }
1223 }
1224
1225 /* =============================================================================
1226            Function:            Rrealloc        // external //
1227            Author:              Rammi
1228            Date:                11/16/1995
1229
1230            Return:              New block of size size (user pos.)
1231
1232            Parameter:           p               previous pointer
1233                  size           new size
1234                  file           called from
1235
1236            Purpose:     Wrapper function for realloc
1237            ============================================================================= */
1238 void *Rrealloc(void *p, size_t size, const char *file)
1239 {
1240     void *ret;
1241 #ifdef WITH_FLAGS
1242     unsigned flags = 0;
1243 #endif
1244
1245     if (p == NULL)
1246     {
1247 #ifndef ALLOW_REALLOC_NULL
1248         fprintf(stderr, HEAD "Realloc of NULL pointer (in %s)\n", file);
1249         abort();
1250 #else /* ALLOW_REALLOC_NULL */
1251 #ifdef ELOQUENT
1252         fprintf(stderr,
1253                 HEAD "WARNING: realloc of NULL pointer (in %s)\n", file);
1254 #endif
1255         return Rmalloc(size, file);
1256 #endif /* ALLOW_REALLOC_NULL */
1257     }
1258 #ifdef WITH_FLAGS
1259     else
1260     {
1261         /* keep flags */
1262         flags = ((begin *)(((char *)p) - START_SPACE))->Flags;
1263         ((begin *)(((char *)p) - START_SPACE))->Flags &= ~RM_STATIC; /* unset static flag to avoid warning */
1264     }
1265 #endif /* WITH_FLAGS */
1266
1267     if (size == 0)
1268     {
1269         fprintf(stderr,
1270                 HEAD "WARNING: realloc() demands 0 Bytes (in %s)\n", file);
1271     }
1272
1273 #if RM_TEST_DEPTH > 0
1274     /* remove old block from list */
1275     DelBlk((begin *)(((char *)p) - START_SPACE), file);
1276 #endif
1277     /* get everything new */
1278     ret = CALL_REALLOC(((char *)p) - START_SPACE, size + EXTRA_SPACE);
1279
1280     if (ret)
1281     {
1282 /* Initialize new block */
1283 #ifdef WITH_FLAGS
1284         return SetBlk(ret, size, file, flags);
1285 #else
1286         return SetBlk(ret, size, file);
1287 #endif
1288     }
1289     else
1290     {
1291         fprintf(stderr,
1292                 HEAD "WARNING: Out of memory! Returning NULL (in %s)\n", file);
1293         return NULL;
1294     }
1295 }
1296
1297 /* =============================================================================
1298            Function:            Rfree           // external //
1299            Author:              Rammi
1300            Date:                11/16/1995
1301
1302            Return:              ---
1303
1304            Parameter:           p               block to free (user pos.)
1305                  file           called from
1306
1307            Purpose:     Wrapper function for free()
1308
1309            ============================================================================= */
1310 void Rfree(void *p, const char *file)
1311 {
1312 #ifdef ELOQUENT
1313     fprintf(stderr, HEAD "Free: %p (called from: %s)\n", p, file);
1314 #endif /* ELOQUENT */
1315     if (p == NULL)
1316     {
1317 #ifdef ALLOW_FREE_NULL
1318 #ifdef ELOQUENT
1319         fprintf(stderr, HEAD "WARNING: Freeing NULL pointer (in %s)\n", file);
1320 #endif
1321         return;
1322 #else  /* !ALLOW_FREE_NULL */
1323         fprintf(stderr, HEAD "Trying to free NULL pointer (in %s)\n", file);
1324         abort();
1325 #endif /* !ALLOW_FREE_NULL */
1326     }
1327 #if RM_TEST_DEPTH > 0
1328     /* Remove block from list */
1329     DelBlk((begin *)(((char *)p) - START_SPACE), file);
1330 #endif
1331     /* free block */
1332     CALL_FREE(((char *)p) - START_SPACE);
1333 }
1334
1335 size_t Rmalloc_usable_size(void *p, const char *file)
1336 {
1337 #ifdef ELOQUENT
1338     fprintf(stderr, HEAD "UsableSize: %p (called from: %s)\n", p, file);
1339 #endif /* ELOQUENT */
1340
1341     if (!p)
1342         return 0;
1343
1344     begin *info = (begin *)(((char *)p) - START_SPACE);
1345
1346     ControlBlock(info, file);
1347
1348     return info->Size;
1349 }
1350
1351 /* =============================================================================
1352            Function:            Rstrdup         // external //
1353            Author:              Rammi
1354            Date:                11/16/1995
1355
1356            Return:              New memory with copied string.
1357
1358            Parameter:           s               string to copy
1359                  file           called from
1360
1361            Purpose:     Wrapper function for strdup()
1362            ============================================================================= */
1363 char *Rstrdup(const char *s, const char *file)
1364 {
1365     size_t size; /* needed memory */
1366     char *ret;
1367
1368     if (s == NULL)
1369     {
1370         fprintf(stderr, HEAD "Calling strdup(NULL) (in %s)\n", file);
1371         abort();
1372     }
1373     size = strlen(s) + 1;
1374
1375     /* Rmalloc() does nearly all the work */
1376     ret = Rmalloc(size, file);
1377
1378     if (ret)
1379     {
1380         /* copy string */
1381         strcpy(ret, s);
1382 #ifdef WITH_FLAGS
1383         Rmalloc_set_flags(ret, RM_STRING, "<by strdup>");
1384 #endif
1385         return ret;
1386     }
1387     else
1388     {
1389         fprintf(stderr,
1390                 HEAD "WARNING: Out of memory! Returning NULL (in %s)\n", file);
1391         return NULL;
1392     }
1393 }
1394
1395 /* =============================================================================
1396            Function:            Rgetcwd         // external //
1397            Author:              Rammi
1398            Date:                11/13/1998
1399
1400            Return:              New memory with copied string depending on input (if
1401                           buffer == NULL)
1402
1403            Parameter:                   buffer          buffer for write (or NULL)
1404                           size          buffer size
1405                  file           called from
1406
1407            Purpose:     Wrapper function for getcwd() which sometimes returns
1408                  memory from heap.
1409            ============================================================================= */
1410 char *Rgetcwd(char *buffer, size_t size, const char *file)
1411 {
1412     char *ret = getcwd(buffer, size);
1413
1414     if (ret && !buffer)
1415     {
1416         /* create new memory to get internals fixed */
1417         char *newret = Rstrdup(ret, file);
1418         CALL_FREE(ret); /* free old stuff */
1419         ret = newret;   /* this was missing before 1.14 */
1420                         /* thanks to Greg Silverman who discovered it! */
1421     }
1422
1423     return ret;
1424 }
1425
1426 /* =============================================================================
1427            Function:            Rmalloc_test            // external //
1428            Author:              Rammi
1429            Date:                04/11/1995
1430
1431            Return:              ---
1432
1433            Parameter:           file            called from
1434
1435            Purpose:     Explicitely test all blocks for integrity
1436
1437            ============================================================================= */
1438 void Rmalloc_test(const char *file)
1439 {
1440 #if RM_TEST_DEPTH > 0
1441     TestAll(file);
1442 #else
1443     fprintf(stderr, HEAD __FILE__
1444             " not compiled with RM_TEST_DEPTH > 0, call in %s senseless.\n",
1445             file);
1446 #endif
1447 }
1448
1449 /* =============================================================================
1450            Function:            BlockSort               // local //
1451            Author:              Rammi
1452            Date:                04/15/1995
1453
1454            Return:              < 0     A < B
1455                  0              A == B
1456                  > 0    A > B
1457
1458            Parameter:           A, B
1459
1460            Purpose:     sort function for qsort
1461
1462            ============================================================================= */
1463 static int BlockSort(const begin **A, const begin **B)
1464 {
1465     int ret;
1466
1467     /* Sort for adress of string (tricky!) */
1468     if ((ret = (*A)->File - (*B)->File))
1469     {
1470         return ret;
1471     }
1472
1473     /* sort for size */
1474     return ((int)(*A)->Size - (int)(*B)->Size);
1475 }
1476
1477 #ifdef GENERATIONS
1478 /* =============================================================================
1479            Function:            BlockSortGenerations            // local //
1480            Author:              Rammi
1481            Date:                04/22/2002
1482
1483            Return:              < 0     A < B
1484                  0              A == B
1485                  > 0    A > B
1486
1487            Parameter:           A, B
1488
1489            Purpose:     sort function for qsort, using the generations counter
1490                           for sorting, too
1491
1492            ============================================================================= */
1493 static int BlockSortGenerations(const begin **A, const begin **B)
1494 {
1495     int ret = BlockSort(A, B);
1496
1497     if (ret)
1498     {
1499         return ret;
1500     }
1501
1502     /* sort for generation */
1503     return (*A)->Generation - (*B)->Generation;
1504 }
1505
1506 #endif
1507
1508 /* =============================================================================
1509            Function:            Rmalloc_stat            // extern //
1510            Author:              Rammi
1511            Date:                04/15/1995
1512
1513            Return:              ---
1514
1515            Parameter:           file            caled from
1516
1517            Purpose:     Show statistic
1518            ============================================================================= */
1519 void Rmalloc_stat(const char *file)
1520 {
1521 #if RM_TEST_DEPTH > 0
1522     TestAllConditionally(file);
1523
1524 #define STAT_HEAD "<MALLOC_STATS>\t"
1525     fprintf(stderr,
1526             STAT_HEAD "============ STATISTICS (%s) =============\n", file);
1527     if (!Global.BlockCount)
1528     {
1529         fprintf(stderr, STAT_HEAD "Nothing allocated.\n");
1530     }
1531     else
1532     {
1533         const begin **BlockVec;
1534
1535         if ((BlockVec = (const begin **)CALL_MALLOC(Global.BlockCount * sizeof(begin *))) == NULL)
1536         {
1537             fprintf(stderr, STAT_HEAD "Couldn't allocate enough memory for statistics. Going on...\n");
1538         }
1539         else
1540         {
1541             unsigned i = 0;
1542             unsigned j;
1543             begin *B;
1544             unsigned count;
1545             size_t Mem = 0;
1546             unsigned nrBlocks;
1547 #ifdef WITH_FLAGS
1548             size_t StaticMem = 0;
1549 #endif
1550 #ifdef GENERATIONS
1551             unsigned gen;
1552 #endif
1553
1554             /* add all blocks to vector */
1555             for (j = 0; j < HASHSIZE; j++)
1556             {
1557                 for (B = Chain[j].Next; B != &Chain[j]; B = B->Next)
1558                 {
1559 #ifdef WITH_FLAGS
1560                     if (B->Flags & RM_STATIC)
1561                     {
1562                         StaticMem += B->Size;
1563                     }
1564                     else
1565                     {
1566                         BlockVec[i++] = B;
1567                     }
1568 #else
1569                     BlockVec[i++] = B;
1570 #endif
1571                 }
1572             }
1573 #ifdef WITH_FLAGS
1574             assert(i <= Global.BlockCount);
1575 #else
1576             assert(i == Global.BlockCount);
1577 #endif
1578             nrBlocks = i;
1579
1580 /* --- sort --- */
1581 #ifdef GENERATIONS
1582             qsort(BlockVec, nrBlocks,
1583                   sizeof(begin *),
1584                   (int (*)(const void *, const void *))BlockSortGenerations);
1585 #else
1586             qsort(BlockVec, nrBlocks,
1587                   sizeof(begin *),
1588                   (int (*)(const void *, const void *))BlockSort);
1589 #endif
1590
1591             for (i = 0; i < nrBlocks; i = j)
1592             {
1593                 count = 1;
1594                 for (j = i + 1; j < nrBlocks; j++)
1595                 {
1596                     if (BlockSort(BlockVec + i, BlockVec + j) != 0)
1597                     {
1598                         break;
1599                     }
1600                     /* are equal */
1601                     count++;
1602                 }
1603 #ifdef GENERATIONS
1604                 fprintf(stderr,
1605                         STAT_HEAD "%6d x %8u Bytes in %s, generations:",
1606                         count,
1607                         (unsigned)BlockVec[i]->Size,
1608                         BlockVec[i]->File);
1609                 for (gen = 0; gen < count; gen++)
1610                 {
1611                     if (gen == MAX_STAT_GENERATIONS)
1612                     {
1613                         fprintf(stderr, " ...");
1614                         break;
1615                     }
1616                     fprintf(stderr, " %d", BlockVec[gen + i]->Generation);
1617                 }
1618                 fprintf(stderr, "\n");
1619 #else
1620                 fprintf(stderr,
1621                         STAT_HEAD "%6d x %8u Bytes in %s\n",
1622                         count,
1623                         (unsigned)BlockVec[i]->Size,
1624                         BlockVec[i]->File);
1625 #endif
1626                 Mem += count * BlockVec[i]->Size;
1627             }
1628
1629             /* and give free */
1630             CALL_FREE(BlockVec);
1631
1632 #ifdef WITH_FLAGS
1633             fprintf(stderr, STAT_HEAD "*Variable*\t%12u Bytes\n",
1634                     (unsigned)Mem);
1635             fprintf(stderr, STAT_HEAD "*Static*  \t%12u Bytes\n",
1636                     (unsigned)StaticMem);
1637             fprintf(stderr, STAT_HEAD "*Total*   \t%12u Bytes\n",
1638                     (unsigned)(Mem + StaticMem));
1639 #else
1640             fprintf(stderr, STAT_HEAD "*Total*\t%u Bytes\n",
1641                     (unsigned)Mem);
1642 #endif
1643         }
1644     }
1645     fprintf(stderr, STAT_HEAD "============ END OF STATISTICS =============\n");
1646 #else
1647     fprintf(stderr, HEAD __FILE__ " not compiled with RM_TEST_DEPTH > 0, call in %s senseless.\n", file);
1648 #endif
1649 }
1650
1651 /* =============================================================================
1652            Function:            Rmalloc_retag           // external //
1653            Author:              Rammi
1654            Date:                12/12/1997
1655
1656            Return:              the pointer p for possible chaining
1657
1658            Parameter:           p               pointer to allocated block (user)
1659                  file           called from
1660
1661            Purpose:     Change file position in header.
1662            ============================================================================= */
1663 void *Rmalloc_retag(void *p, const char *file)
1664 {
1665     if (p)
1666     {
1667
1668 #if RM_TEST_DEPTH > 0
1669         if (!Global.isInitialized)
1670         {
1671             fprintf(stderr, HEAD
1672                     "Calling RM_RETAG without having allocated block via rmalloc in\n%s",
1673                     file);
1674             abort();
1675         }
1676 #endif
1677
1678         begin *info = (begin *)(((char *)p) - START_SPACE);
1679
1680         /* --- test integrity --- */
1681         ControlBlock(info, file);
1682
1683         /* --- change file pos --- */
1684         info->File = file;
1685     }
1686
1687     return p;
1688 }
1689
1690 /* =============================================================================
1691            Function:            Rmalloc_set_flags               // external //
1692            Author:              Rammi
1693            Date:                12/12/1997
1694
1695            Return:              the pointer p for possible chaining
1696
1697            Parameter:           p                               pointer to allocated block (user)
1698                           flags                 Flags to set
1699                           file          called from
1700
1701            Purpose:             Set flags in header
1702            ============================================================================= */
1703 void *Rmalloc_set_flags(void *p, unsigned flags, const char *file)
1704 {
1705 #ifdef WITH_FLAGS
1706     if (p)
1707     {
1708
1709 #if RM_TEST_DEPTH > 0
1710         if (!Global.isInitialized)
1711         {
1712             fprintf(stderr, HEAD
1713                     "Calling RM_SET without having allocated block via rmalloc in\n%s",
1714                     file);
1715             abort();
1716         }
1717 #endif
1718
1719         begin *info = (begin *)(((char *)p) - START_SPACE);
1720
1721         /* --- test integrity --- */
1722         ControlBlock(info, file);
1723
1724         /* --- change flags --- */
1725         info->Flags |= flags;
1726     }
1727 #endif
1728
1729     return p;
1730 }
1731
1732 /* =============================================================================
1733            Function:            Rmalloc_reinit          // external //
1734            Author:              Rammi
1735            Date:                05/28/1998
1736
1737            Return:              ---
1738
1739            Parameter:           ---
1740
1741            Purpose:             This reinits the lists. This is only for test purposes.
1742                  DON'T USE THIS FUNCTION!
1743            ============================================================================= */
1744 void Rmalloc_reinit(void)
1745 {
1746 #if RM_TEST_DEPTH > 0
1747     int i;
1748     /* --- init list heads (discarding everything!) --- */
1749     for (i = 0; i < HASHSIZE; i++)
1750     {
1751         memcpy(Chain + i, &ChainTempl, sizeof(begin));
1752         Chain[i].Next = Chain[i].Prev = Chain + i;
1753     }
1754 #endif
1755 #ifdef GENERATIONS
1756     cur_generation = 0;
1757 #endif
1758 }
1759
1760 #ifdef __cplusplus
1761 }
1762 #endif