1 /* =====================================================================
4 Date: 11/16/1995 (started)
6 License: (This is the open source ISC license, see
7 http://en.wikipedia.org/wiki/ISC_license
10 Copyright © 2010 Andreas M. Rammelt <rammi@hexco.de>
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.
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.
24 Content: Debug wrapper functions for the malloc library.
25 For more information see rmalloc.h
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
32 History: Revision 1.15 2009/07/06 12:05:48 rammi
33 History: Incremented version.
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.
40 History: Revision 1.13 2008/12/28 13:00:26 rammi
41 History: Fixed Typo in rmalloc.h: unded --> undef
43 History: Revision 1.12 2008/12/27 15:57:40 rammi
44 History: Updated version
46 History: Revision 1.11 2008/12/27 15:56:44 rammi
47 History: Embedded patch from F. Culot to avoid overflow in Rcalloc()
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
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!
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.
58 History: Revision 1.7 2003/01/31 15:04:20 rammi
59 History: Fixed unclosed comment.
61 History: Revision 1.6 2003/01/31 14:51:48 rammi
62 History: Updated version to 1.16
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
67 History: Revision 1.4 2002/04/22 17:39:34 rammi
68 History: Added output of BREAK_GENERATION environment variable when used
70 History: Revision 1.3 2002/04/22 16:28:06 rammi
71 History: Finetuning of generations feature
73 History: Revision 1.2 2002/04/22 15:26:16 rammi
74 History: Added Karl Brace's generations feature.
79 Changed to hashed table for faster access
83 Automatic output of used memory on exit
85 Catching signals in critical situations (partly)
87 Testing memory before outputting statistic
88 Overworked signal handling in IsPossibleFilePos()
89 Made it unnecessary of changing all mallocs etc
92 Little changes to compile on Alpha (64bit) without
97 Allowed compilation without WITH_FLAGS
99 All output goes to stderr.
100 . 1.11beta is released as 1.11!
102 Changed comments to english for public release
103 Added some more signal handling.
105 Added output of (flagged) strings in ELOQUENT mode.
106 Changed all names from my_... to R...
107 This is version 1.12!
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!
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
119 ===================================================================== */
134 #undef MALLOC_DEBUG /* here we need the correct malloc functions */
135 #define RM_NEED_PROTOTYPES /* but we want to compare prototypes */
147 #define VERSION "1.21"
149 /* ================================================================== */
150 /* ============ Switch settings for different behaviours ============ */
151 /* ============ Please set as needed ============ */
152 /* ================================================================== */
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.
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.
165 #ifndef RM_TEST_DEPTH
166 #define RM_TEST_DEPTH 1
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
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.
181 /* BREAK_GENERATION_COND is the condition to find the generation you are
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.
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) */
195 /* The maximum number of generations you'd like to see in the statistics */
196 #define MAX_STAT_GENERATIONS 3
198 #endif /* GENERATIONS */
200 /* Switch on EXTENTED alloc information. (Makes sense if an actual
201 * error is observed such as a multiple free)
203 /* #define ELOQUENT */
205 /* Only show errors. This doesn't output the initializing output and the
206 * end statistic if there was a leak encountered.
210 /* Allows setting of special flags with the RM_SET_FLAGS macro.
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.
219 #define ALLOW_REALLOC_NULL
222 * I still consider this an error in my progs because I use
223 * NULL always as a very special value.
225 #define ALLOW_FREE_NULL
227 /* ================================================================== */
228 /* ======================== Other Defines ========================= */
229 /* ================================================================== */
232 #define ALIGNMENT sizeof(void *) * 2
234 /* Output message header: */
235 #define HEAD "<MALLOC_DEBUG>\t"
237 /* Alignment padding: */
238 //#define ALIGN(s) (((s+ALIGNMENT-1)/ALIGNMENT)*ALIGNMENT)
239 #define ALIGN(s) ((s + ALIGNMENT - 1) & (~(ALIGNMENT - 1)))
241 /* Magic marker for block start: */
242 #define PREV_STOP 0x55555555
244 /* Additional space for block begin to keep alignment: */
245 #define START_SPACE ALIGN(sizeof(begin))
247 /* Additional space at end of block */
248 #define END_SPACE (sizeof(End))
250 /* Overall additional space per block: */
251 #define EXTRA_SPACE (START_SPACE + END_SPACE)
253 /* Hashtable size: */
254 //#define HASHSIZE 257
255 //#define HASHSIZE 65537
256 #define HASHSIZE 98947
259 //#define HASH(p) ((((unsigned long)(p))/ALIGNMENT)%HASHSIZE)
260 static inline unsigned int bitmix32(unsigned int a)
272 #define HASH(p) (bitmix32((((size_t)(p)) / ALIGNMENT)) % HASHSIZE)
274 /* ==========================
275 STRUCTs, TYPEDEFs & ENUMs:
276 ========================== */
278 /* This Information is added to the beginning of every
281 typedef struct _begin
283 unsigned StpA; /* Magic bytes */
284 #if RM_TEST_DEPTH > 0
285 struct _begin *Next, /* for linking in forward */
286 *Prev; /* and backward direction */
288 const char *File; /* Filepos of allocation command */
289 size_t Size; /* Size demanded */
291 unsigned Generation; /* All mallocs are numbered */
294 unsigned Flags; /* Special flags */
296 unsigned StpB; /* Magic bytes */
302 typedef struct _global
304 unsigned isInitialized; /* Flag: already initialized? */
305 unsigned BlockCount; /* Number of allocated blocks */
312 /* Magic block end: */
313 static unsigned char End[] =
315 0xA5, 0xA5, 0xA5, 0xA5, /* odd */
316 0x5B, 0x5B, 0x5B, 0x5B, /* odd */
317 0xAB, 0xAB, 0xAB, 0xAB, /* odd */
318 0xAA, 0x55, 0xAA, 0x55 /* odd */
326 /* The current generation. This is simply incremented which each call. */
327 static unsigned cur_generation = 0;
334 #if RM_TEST_DEPTH > 0
335 /* Stop marker for linked list of allocated blocks: */
336 static begin ChainTempl =
352 /* Hashed linked lists: */
353 static begin Chain[HASHSIZE];
355 /* Global data used: */
356 static global Global =
358 0, /* is initialized? */
359 0 /* number of blocks */
361 #endif /* RM_TEST_DEPTH */
363 /* Internally used longjmp target used if errors occure. */
364 static jmp_buf errorbuf;
370 static int FindBlk(const unsigned char *P, const char *file);
372 /* ===============================================================
374 =============================================================== */
376 static void *DefMalloc(size_t size, void *pUser)
382 static void DefFree(void *p, void *pUser)
388 static void *DefRealloc(void *ptr, size_t size, void *pUser)
391 return realloc(ptr, size);
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;
399 void Rmalloc_set_callbacks(rmalloc_malloc_func_t pMalloc, rmalloc_free_func_t pFree, rmalloc_realloc_func_t pRealloc, void *pUser)
401 g_malloc_func = pMalloc;
403 g_realloc_func = pRealloc;
404 g_pMalloc_user_ptr = pUser;
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)
411 /* =============================================================================
412 Function: FatalSignal // local //
418 Parameter: signum signal number
420 Purpose: Signal handler für fatal signals (SIGBUS, SIGSEGV)
421 ============================================================================= */
422 static void FatalSignal(int signum)
424 /* --- jump to a save place --- */
425 longjmp(errorbuf, signum);
428 /* =============================================================================
429 Function: IsPossibleFilePos // local //
433 Return: != 0 possibly ok
436 Parameter: file possible filename
439 Purpose: Decide whether file could be a filename and
441 ============================================================================= */
442 static int IsPossibleFilePos(const char *file, int size)
444 void (*old_sigsegv_handler)(int) = SIG_DFL;
445 void (*old_sigbus_handler)(int) = SIG_DFL;
449 if (setjmp(errorbuf))
451 /* uh oh, we got a kick in the ass */
452 signal(SIGSEGV, old_sigsegv_handler);
453 signal(SIGBUS, old_sigbus_handler);
457 /* --- the following is dangerous! So catch signals --- */
458 old_sigsegv_handler = signal(SIGSEGV, FatalSignal);
459 old_sigbus_handler = signal(SIGBUS, FatalSignal);
467 dp = strchr(file, ':'); /* file pos needs : */
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);
473 /* --- danger is over! --- */
474 signal(SIGSEGV, old_sigsegv_handler);
475 signal(SIGBUS, old_sigbus_handler);
481 /* =============================================================================
482 Function: GetBreakGenerationEnv // lokal //
486 Return: the content of the BREAK_GENERATION environment variable
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).
495 ============================================================================= */
496 static unsigned GetBreakGenerationEnv(void)
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;
503 if (breakGenerationEnv == (char *)-1)
505 /* 1st call: get the environment variable */
506 breakGenerationEnv = getenv("BREAK_GENERATION");
507 if (breakGenerationEnv != NULL)
510 result = atoi(breakGenerationEnv);
512 HEAD "Using environment variable BREAK_GENERATION=%d\n",
519 #endif /* GENERATIONS */
521 /* =============================================================================
522 Function: ControlBlock // lokal //
527 Parameter: Bkl Pos of allocated block (original)
528 file file pos from where initial lib function
531 Purpose: Control integrity of block
533 ============================================================================= */
534 static void ControlBlock(begin *B, const char *file)
536 unsigned char *p = (((unsigned char *)B) + START_SPACE);
537 #if RM_TEST_DEPTH > 0
540 /* === the very beginning === */
541 if (B->StpA != PREV_STOP)
543 #if RM_TEST_DEPTH > 0
547 "Corrupted block begin (overwritten from elsewhere)\n"
548 "\tshould be: %08x\n"
551 "\tblock was allocated in %s [%u Bytes, generation %u]\n"
553 "\tblock was allocated in %s [%u Bytes]\n"
555 "\terror was detected in %s\n",
566 /* === begin of user data === */
567 if (B->StpB != PREV_STOP)
569 #if RM_TEST_DEPTH > 0
573 "Corrupted block begin (possibly written back)\n"
574 "\tshould be: %08x\n"
577 "\tblock was allocated in %s [%u Bytes, generation %u]\n"
579 "\tblock was allocated in %s [%u Bytes]\n"
581 "\terror was detected in %s\n",
592 /* === end of user data === */
593 if (memcmp(p + B->Size, &End, END_SPACE) != 0)
595 unsigned char *E = (unsigned char *)(p + B->Size);
598 #if RM_TEST_DEPTH > 0
602 "Corrupted block end (possibly written past the end)\n"
604 for (i = 0; i < END_SPACE; i++)
606 fprintf(stderr, i % 4 ? "%02x" : " %02x", End[i]);
611 for (i = 0; i < END_SPACE; i++)
613 fprintf(stderr, i % sizeof(int) ? "%02x" : " %02x", E[i]);
617 "\tblock was allocated in %s [%u Bytes, generation %u]\n"
619 "\tblock was allocated in %s [%u Bytes]\n"
621 "\terror was detected in %s\n",
629 #if RM_TEST_DEPTH > 0
630 if (!((unsigned long)E % sizeof(void *)) &&
631 !(*(unsigned long *)E % sizeof(void *))) /* because of alignment */
633 /* Special service: look if memory was overwritten with pointer */
634 if (FindBlk(*(unsigned char **)E, file))
636 begin *b = (begin *)((*(unsigned char **)E) - START_SPACE);
637 if (IsPossibleFilePos(b->File, b->Size))
640 "\tFirst %d bytes of overwritten memory can be interpreted\n"
641 "\t\tas a pointer to a block "
644 "\t\t%s [%u Bytes, generation %u]\n",
646 "\t\t%s [%u Bytes]\n",
648 (unsigned int)sizeof(void *),
663 /* Look, what we can find... */
666 for (j = END_SPACE - 1; j >= 0; j--)
675 /* Ends with '\0', so it's possibly a string */
688 "\tLooks somewhat like a too long string,\n"
689 "\t\tending with \"%s\"\n",
697 "\tLooks like string allocated one byte too short\n"
698 "\t\t(forgetting the nul byte)\n");
704 #if RM_TEST_DEPTH > 0
713 #if RM_TEST_DEPTH > 0
714 void Rmalloc_stat(const char *file);
716 /* =============================================================================
717 Function: Exit // local //
724 Purpose: Function called on exit
725 ============================================================================= */
726 static void Exit(void)
729 if (!Global.BlockCount)
735 Rmalloc_stat("[atexit]"); /* show statistics */
738 /* =============================================================================
739 Function: Initialize // local //
746 Purpose: Necessary initializations
748 ============================================================================= */
749 static void Initialize(void)
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"
763 "\t\ttesting:\tcomment missing in " __FILE__ ":" INT2STRING(__LINE__) "\n"
766 "\t\tgenerations:\tON\n"
768 "\t\tgenerations:\tOFF\n"
771 "\t\teloquence:\tON\n"
773 "\t\teloquence:\tOFF\n"
775 #ifdef ALLOW_REALLOC_NULL
776 "\t\trealloc(0):\tALLOWED\n"
778 "\t\trealloc(0):\tNOT ALLOWED\n"
780 #ifdef ALLOW_FREE_NULL
781 "\t\tfree(0):\tALLOWED\n"
783 "\t\tfree(0):\tNOT ALLOWED\n"
786 "\t\tflags: \tUSED\n"
788 "\t\tflags: \tUNUSED\n"
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 */
797 /* --- init list heads --- */
798 for (i = 0; i < HASHSIZE; i++)
800 memcpy(Chain + i, &ChainTempl, sizeof(begin));
801 Chain[i].Next = Chain[i].Prev = Chain + i;
804 /* --- show statistics at exit --- */
807 Global.isInitialized = 1;
810 /* =============================================================================
811 Function: TestAll // local //
816 Parameter: file file pos where lib function was
819 Purpose: Test all allocated blocks for inconsistencies
820 ============================================================================= */
821 static void TestAll(const char *file)
823 begin *B; /* Block iterator */
824 int i; /* Hash iterator */
826 /* make sure everything is initialized */
827 if (!Global.isInitialized)
832 for (i = 0; i < HASHSIZE; i++)
836 /* === Once around the circle === */
837 while (B != &Chain[i])
839 ControlBlock(B, file);
845 static uint gTestAllCounter;
847 static void TestAllConditionally(const char *file)
849 if (!gTestAllCounter)
852 gTestAllCounter = RM_TEST_ALL_FREQUENCY;
857 /* =============================================================================
858 Function: AddBlk // local //
863 Parameter: Blk New block (original pos.)
866 Purpose: Add new block to the list
867 ============================================================================= */
868 static void AddBlk(begin *Blk, const char *file)
870 int hash = HASH(Blk); /* hash val */
873 /* make sure everything is initialized */
874 if (!Global.isInitialized)
879 #if RM_TEST_DEPTH > 1
880 TestAllConditionally(file);
882 /* prevent compiler warnings about unused variables */
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;
894 /* =============================================================================
895 Function: DelBlk // local //
901 Parameter: Blk block to remove
904 Purpose: Remove block from list.
905 React angry if block is unknown
906 ============================================================================= */
907 static void DelBlk(begin *Blk, const char *file)
909 begin *B; /* run var */
910 int hash = HASH(Blk); /* hash val */
912 if (!Global.isInitialized)
915 "Calling free without having allocated block via rmalloc\n"
921 /* look if block is known */
922 for (B = Chain[hash].Next; B != &Chain[hash]; B = B->Next)
924 if ((B->StpA != PREV_STOP) || (B->StpB != PREV_STOP))
926 ControlBlock(B, file);
931 goto found_actual_block; /* friendly goto */
937 "Double or false delete\n"
938 "\tHeap adress of block: %p\n"
939 "\tDetected in %s\n",
940 ((char *)Blk) + START_SPACE, file);
942 void (*old_sigsegv_handler)(int) = SIG_DFL;
943 void (*old_sigbus_handler)(int) = SIG_DFL;
945 if (setjmp(errorbuf))
947 /* uh oh, we got a kick in the ass */
948 signal(SIGSEGV, old_sigsegv_handler);
949 signal(SIGBUS, old_sigbus_handler);
953 /* --- the following is dangerous! So catch signals --- */
954 old_sigsegv_handler = signal(SIGSEGV, FatalSignal);
955 old_sigbus_handler = signal(SIGBUS, FatalSignal);
957 if (IsPossibleFilePos(Blk->File, Blk->Size))
960 "\tTrying identification (may be incorrect!):\n"
961 "\t\tAllocated in %s [%u Bytes]\n",
962 Blk->File, (unsigned)Blk->Size);
964 fprintf(stderr, "\t\tGeneration: %u\n", (unsigned)Blk->Generation);
967 signal(SIGSEGV, old_sigsegv_handler);
968 signal(SIGBUS, old_sigbus_handler);
971 abort(); /* die loud */
974 #if RM_TEST_DEPTH > 1
975 /* check everything */
976 TestAllConditionally(file);
978 /* test integrity of actual block */
979 ControlBlock(Blk, file);
983 Blk->Next->Prev = Blk->Prev;
984 Blk->Prev->Next = Blk->Next;
990 HEAD "Delete: %d Bytes allocated in %s (from %s)\n",
991 Blk->Size, Blk->File, file);
993 if (Blk->Flags & RM_STRING)
997 for (c = (char *)Blk + START_SPACE;
998 c - (char *)Blk + START_SPACE < Blk->Size;
1004 HEAD "\tContains string: \"%s\"\n",
1005 (char *)Blk + START_SPACE);
1006 goto found_old_block;
1011 HEAD "\tContains string without null byte\n");
1015 #endif /* WITH_FLAGS */
1016 #endif /* ELOQUENT */
1019 if (Blk->Flags & RM_STATIC)
1022 HEAD "WARNING: freeing block marked as STATIC (in %s)\n", file);
1024 #endif /* WITH_FLAGS */
1027 /* =============================================================================
1028 Function: FindBlk // local //
1035 Parameter: P block (user pos)
1037 Purpose: look if block is known
1038 ============================================================================= */
1039 static int FindBlk(const unsigned char *P, const char *file)
1042 const begin *Blk = (const begin *)(P - START_SPACE);
1043 int hash = HASH(Blk);
1045 /* look if block is known */
1046 for (B = Chain[hash].Next; B != &Chain[hash]; B = B->Next)
1048 if ((B->StpA != PREV_STOP) || (B->StpB != PREV_STOP))
1050 ControlBlock(B, file);
1060 #endif /* RM_TEST_DEPTH > 0 */
1063 /* =============================================================================
1064 Function: rmalloc_generation // local //
1070 Parameter: Blk pointer to block
1072 Purpose: Breakpoint for debugger if using Karl's generations
1074 ============================================================================= */
1075 void rmalloc_generation(void *Blk)
1077 fprintf(stderr, HEAD "Allocating Block with generation %u...\n",
1078 ((begin *)Blk)->Generation);
1081 #endif /* GENERATIONS */
1083 /* =============================================================================
1084 Function: SetBlk // local //
1088 Return: pointer to block (user pos.)
1090 Parameter: Blk pointer to block (original pos.)
1093 flags flags (when compiled WITH_FLAGS)
1095 Purpose: Set our internal information
1097 ============================================================================= */
1099 static void *SetBlk(void *Blk, size_t size, const char *file, unsigned flags)
1101 static void *SetBlk(void *Blk, size_t size, const char *file)
1105 ((begin *)Blk)->StpA = PREV_STOP;
1107 ((begin *)Blk)->Generation = ++cur_generation;
1109 ((begin *)Blk)->File = file;
1110 ((begin *)Blk)->Size = size;
1112 ((begin *)Blk)->Flags = flags;
1114 ((begin *)Blk)->StpB = PREV_STOP;
1115 memcpy(((char *)Blk) + START_SPACE + size, End, END_SPACE);
1117 #if RM_TEST_DEPTH > 0
1118 AddBlk((begin *)Blk, file);
1122 HEAD "Adding: %p, %d Bytes (from %s)\n",
1123 ((char *)Blk) + START_SPACE, size, file);
1124 #endif /* ELOQUENT */
1126 if (BREAK_GENERATION_COND(cur_generation))
1128 rmalloc_generation(Blk);
1131 return ((char *)Blk) + START_SPACE;
1134 /* =============================================================================
1135 Function: Rmalloc // external //
1139 Return: New prepared memory block with size size (user)
1141 Parameter: size demanded size
1142 file called from where?
1144 Purpose: wrapper for malloc
1145 ============================================================================= */
1146 void *Rmalloc(size_t size, const char *file)
1148 void *ret; /* ret val */
1152 fprintf(stderr, HEAD "WARNING: malloc() demands 0 Bytes (in %s)\n", file);
1155 ret = CALL_MALLOC(size + EXTRA_SPACE); /* get extended block */
1161 return SetBlk(ret, size, file, 0);
1163 return SetBlk(ret, size, file);
1169 HEAD "WARNING: Out of memory! Returning NULL (in %s)\n", file);
1174 /* =============================================================================
1175 Function: Rcalloc // external //
1179 Return: New (cleared) memory block of size nelem*size
1181 Parameter: nelem nr blocks (as stupid as calloc)
1182 size size of one block
1185 Purpose: Wrapper function for calloc
1186 ============================================================================= */
1187 void *Rcalloc(size_t nelem, size_t size, const char *file)
1191 /* check for overflow here (patch from Frédéric Culot) */
1192 if (size && nelem > SIZE_MAX / size)
1195 HEAD "WARNING: calloc() overflow! Returning NULL (in %s)\n", file);
1199 /* calculate correct size now */
1205 HEAD "WARNING: calloc() demands 0 Bytes (in %s)\n", file);
1208 /* Rmalloc makes nearly all the work */
1209 ret = Rmalloc(size, file);
1214 memset(ret, 0, size);
1220 HEAD "WARNING: Out of memory! Returning NULL (in %s)\n", file);
1225 /* =============================================================================
1226 Function: Rrealloc // external //
1230 Return: New block of size size (user pos.)
1232 Parameter: p previous pointer
1236 Purpose: Wrapper function for realloc
1237 ============================================================================= */
1238 void *Rrealloc(void *p, size_t size, const char *file)
1247 #ifndef ALLOW_REALLOC_NULL
1248 fprintf(stderr, HEAD "Realloc of NULL pointer (in %s)\n", file);
1250 #else /* ALLOW_REALLOC_NULL */
1253 HEAD "WARNING: realloc of NULL pointer (in %s)\n", file);
1255 return Rmalloc(size, file);
1256 #endif /* ALLOW_REALLOC_NULL */
1262 flags = ((begin *)(((char *)p) - START_SPACE))->Flags;
1263 ((begin *)(((char *)p) - START_SPACE))->Flags &= ~RM_STATIC; /* unset static flag to avoid warning */
1265 #endif /* WITH_FLAGS */
1270 HEAD "WARNING: realloc() demands 0 Bytes (in %s)\n", file);
1273 #if RM_TEST_DEPTH > 0
1274 /* remove old block from list */
1275 DelBlk((begin *)(((char *)p) - START_SPACE), file);
1277 /* get everything new */
1278 ret = CALL_REALLOC(((char *)p) - START_SPACE, size + EXTRA_SPACE);
1282 /* Initialize new block */
1284 return SetBlk(ret, size, file, flags);
1286 return SetBlk(ret, size, file);
1292 HEAD "WARNING: Out of memory! Returning NULL (in %s)\n", file);
1297 /* =============================================================================
1298 Function: Rfree // external //
1304 Parameter: p block to free (user pos.)
1307 Purpose: Wrapper function for free()
1309 ============================================================================= */
1310 void Rfree(void *p, const char *file)
1313 fprintf(stderr, HEAD "Free: %p (called from: %s)\n", p, file);
1314 #endif /* ELOQUENT */
1317 #ifdef ALLOW_FREE_NULL
1319 fprintf(stderr, HEAD "WARNING: Freeing NULL pointer (in %s)\n", file);
1322 #else /* !ALLOW_FREE_NULL */
1323 fprintf(stderr, HEAD "Trying to free NULL pointer (in %s)\n", file);
1325 #endif /* !ALLOW_FREE_NULL */
1327 #if RM_TEST_DEPTH > 0
1328 /* Remove block from list */
1329 DelBlk((begin *)(((char *)p) - START_SPACE), file);
1332 CALL_FREE(((char *)p) - START_SPACE);
1335 size_t Rmalloc_usable_size(void *p, const char *file)
1338 fprintf(stderr, HEAD "UsableSize: %p (called from: %s)\n", p, file);
1339 #endif /* ELOQUENT */
1344 begin *info = (begin *)(((char *)p) - START_SPACE);
1346 ControlBlock(info, file);
1351 /* =============================================================================
1352 Function: Rstrdup // external //
1356 Return: New memory with copied string.
1358 Parameter: s string to copy
1361 Purpose: Wrapper function for strdup()
1362 ============================================================================= */
1363 char *Rstrdup(const char *s, const char *file)
1365 size_t size; /* needed memory */
1370 fprintf(stderr, HEAD "Calling strdup(NULL) (in %s)\n", file);
1373 size = strlen(s) + 1;
1375 /* Rmalloc() does nearly all the work */
1376 ret = Rmalloc(size, file);
1383 Rmalloc_set_flags(ret, RM_STRING, "<by strdup>");
1390 HEAD "WARNING: Out of memory! Returning NULL (in %s)\n", file);
1395 /* =============================================================================
1396 Function: Rgetcwd // external //
1400 Return: New memory with copied string depending on input (if
1403 Parameter: buffer buffer for write (or NULL)
1407 Purpose: Wrapper function for getcwd() which sometimes returns
1409 ============================================================================= */
1410 char *Rgetcwd(char *buffer, size_t size, const char *file)
1412 char *ret = getcwd(buffer, size);
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! */
1426 /* =============================================================================
1427 Function: Rmalloc_test // external //
1433 Parameter: file called from
1435 Purpose: Explicitely test all blocks for integrity
1437 ============================================================================= */
1438 void Rmalloc_test(const char *file)
1440 #if RM_TEST_DEPTH > 0
1443 fprintf(stderr, HEAD __FILE__
1444 " not compiled with RM_TEST_DEPTH > 0, call in %s senseless.\n",
1449 /* =============================================================================
1450 Function: BlockSort // local //
1460 Purpose: sort function for qsort
1462 ============================================================================= */
1463 static int BlockSort(const begin **A, const begin **B)
1467 /* Sort for adress of string (tricky!) */
1468 if ((ret = (*A)->File - (*B)->File))
1474 return ((int)(*A)->Size - (int)(*B)->Size);
1478 /* =============================================================================
1479 Function: BlockSortGenerations // local //
1489 Purpose: sort function for qsort, using the generations counter
1492 ============================================================================= */
1493 static int BlockSortGenerations(const begin **A, const begin **B)
1495 int ret = BlockSort(A, B);
1502 /* sort for generation */
1503 return (*A)->Generation - (*B)->Generation;
1508 /* =============================================================================
1509 Function: Rmalloc_stat // extern //
1515 Parameter: file caled from
1517 Purpose: Show statistic
1518 ============================================================================= */
1519 void Rmalloc_stat(const char *file)
1521 #if RM_TEST_DEPTH > 0
1522 TestAllConditionally(file);
1524 #define STAT_HEAD "<MALLOC_STATS>\t"
1526 STAT_HEAD "============ STATISTICS (%s) =============\n", file);
1527 if (!Global.BlockCount)
1529 fprintf(stderr, STAT_HEAD "Nothing allocated.\n");
1533 const begin **BlockVec;
1535 if ((BlockVec = (const begin **)CALL_MALLOC(Global.BlockCount * sizeof(begin *))) == NULL)
1537 fprintf(stderr, STAT_HEAD "Couldn't allocate enough memory for statistics. Going on...\n");
1548 size_t StaticMem = 0;
1554 /* add all blocks to vector */
1555 for (j = 0; j < HASHSIZE; j++)
1557 for (B = Chain[j].Next; B != &Chain[j]; B = B->Next)
1560 if (B->Flags & RM_STATIC)
1562 StaticMem += B->Size;
1574 assert(i <= Global.BlockCount);
1576 assert(i == Global.BlockCount);
1582 qsort(BlockVec, nrBlocks,
1584 (int (*)(const void *, const void *))BlockSortGenerations);
1586 qsort(BlockVec, nrBlocks,
1588 (int (*)(const void *, const void *))BlockSort);
1591 for (i = 0; i < nrBlocks; i = j)
1594 for (j = i + 1; j < nrBlocks; j++)
1596 if (BlockSort(BlockVec + i, BlockVec + j) != 0)
1605 STAT_HEAD "%6d x %8u Bytes in %s, generations:",
1607 (unsigned)BlockVec[i]->Size,
1609 for (gen = 0; gen < count; gen++)
1611 if (gen == MAX_STAT_GENERATIONS)
1613 fprintf(stderr, " ...");
1616 fprintf(stderr, " %d", BlockVec[gen + i]->Generation);
1618 fprintf(stderr, "\n");
1621 STAT_HEAD "%6d x %8u Bytes in %s\n",
1623 (unsigned)BlockVec[i]->Size,
1626 Mem += count * BlockVec[i]->Size;
1630 CALL_FREE(BlockVec);
1633 fprintf(stderr, STAT_HEAD "*Variable*\t%12u Bytes\n",
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));
1640 fprintf(stderr, STAT_HEAD "*Total*\t%u Bytes\n",
1645 fprintf(stderr, STAT_HEAD "============ END OF STATISTICS =============\n");
1647 fprintf(stderr, HEAD __FILE__ " not compiled with RM_TEST_DEPTH > 0, call in %s senseless.\n", file);
1651 /* =============================================================================
1652 Function: Rmalloc_retag // external //
1656 Return: the pointer p for possible chaining
1658 Parameter: p pointer to allocated block (user)
1661 Purpose: Change file position in header.
1662 ============================================================================= */
1663 void *Rmalloc_retag(void *p, const char *file)
1668 #if RM_TEST_DEPTH > 0
1669 if (!Global.isInitialized)
1671 fprintf(stderr, HEAD
1672 "Calling RM_RETAG without having allocated block via rmalloc in\n%s",
1678 begin *info = (begin *)(((char *)p) - START_SPACE);
1680 /* --- test integrity --- */
1681 ControlBlock(info, file);
1683 /* --- change file pos --- */
1690 /* =============================================================================
1691 Function: Rmalloc_set_flags // external //
1695 Return: the pointer p for possible chaining
1697 Parameter: p pointer to allocated block (user)
1701 Purpose: Set flags in header
1702 ============================================================================= */
1703 void *Rmalloc_set_flags(void *p, unsigned flags, const char *file)
1709 #if RM_TEST_DEPTH > 0
1710 if (!Global.isInitialized)
1712 fprintf(stderr, HEAD
1713 "Calling RM_SET without having allocated block via rmalloc in\n%s",
1719 begin *info = (begin *)(((char *)p) - START_SPACE);
1721 /* --- test integrity --- */
1722 ControlBlock(info, file);
1724 /* --- change flags --- */
1725 info->Flags |= flags;
1732 /* =============================================================================
1733 Function: Rmalloc_reinit // external //
1741 Purpose: This reinits the lists. This is only for test purposes.
1742 DON'T USE THIS FUNCTION!
1743 ============================================================================= */
1744 void Rmalloc_reinit(void)
1746 #if RM_TEST_DEPTH > 0
1748 /* --- init list heads (discarding everything!) --- */
1749 for (i = 0; i < HASHSIZE; i++)
1751 memcpy(Chain + i, &ChainTempl, sizeof(begin));
1752 Chain[i].Next = Chain[i].Prev = Chain + i;