2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19 * All rights reserved.
21 * The Original Code is: all of this file.
23 * Contributor(s): none yet.
25 * ***** END GPL LICENSE BLOCK *****
28 /** \file guardedalloc/intern/mallocn.c
31 * Guarded memory allocation, and boundary-write detection.
35 #include <string.h> /* memcpy */
37 #include <sys/types.h>
38 /* Blame Microsoft for LLP64 and no inttypes.h, quick workaround needed: */
40 # define SIZET_FORMAT "%I64u"
41 # define SIZET_ARG(a) ((unsigned long long)(a))
43 # define SIZET_FORMAT "%lu"
44 # define SIZET_ARG(a) ((unsigned long)(a))
49 # include "mmap_win.h"
51 # include <sys/mman.h>
54 #include "MEM_guardedalloc.h"
56 /* Only for debugging:
57 * lets you count the allocations so as to find the allocator of unfreed memory
58 * in situations where the leak is predictable */
60 //#define DEBUG_MEMCOUNTER
62 #ifdef DEBUG_MEMCOUNTER
63 /* set this to the value that isn't being freed */
64 # define DEBUG_MEMCOUNTER_ERROR_VAL 0
65 static int _mallocn_count = 0;
68 static void memcount_raise(const char *name)
70 fprintf(stderr, "%s: memcount-leak, %d\n", name, _mallocn_count);
74 /* --------------------------------------------------------------------- */
76 /* --------------------------------------------------------------------- */
77 /* all memory chunks are put in linked lists */
78 typedef struct localLink {
79 struct localLink *next, *prev;
82 typedef struct localListBase {
86 /* note: keep this struct aligned (e.g., irix/gcc) - Hos */
87 typedef struct MemHead {
90 struct MemHead *next, *prev;
94 int mmap; /* if true, memory was mmapped */
95 #ifdef DEBUG_MEMCOUNTER
100 typedef struct MemTail {
105 /* --------------------------------------------------------------------- */
106 /* local functions */
107 /* --------------------------------------------------------------------- */
109 static void addtail(volatile localListBase *listbase, void *vlink);
110 static void remlink(volatile localListBase *listbase, void *vlink);
111 static void rem_memblock(MemHead *memh);
112 static void MemorY_ErroR(const char *block, const char *error);
113 static const char *check_memlist(MemHead *memh);
115 /* --------------------------------------------------------------------- */
116 /* locally used defines */
117 /* --------------------------------------------------------------------- */
119 #ifdef __BIG_ENDIAN__
120 # define MAKE_ID(a, b, c, d) ((int)(a) << 24 | (int)(b) << 16 | (c) << 8 | (d))
122 # define MAKE_ID(a, b, c, d) ((int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a))
125 #define MEMTAG1 MAKE_ID('M', 'E', 'M', 'O')
126 #define MEMTAG2 MAKE_ID('R', 'Y', 'B', 'L')
127 #define MEMTAG3 MAKE_ID('O', 'C', 'K', '!')
128 #define MEMFREE MAKE_ID('F', 'R', 'E', 'E')
131 ((MemHead *)(((char *) x) - ((char *) &(((MemHead *)0)->next))))
133 /* --------------------------------------------------------------------- */
135 /* --------------------------------------------------------------------- */
138 static volatile int totblock = 0;
139 static volatile uintptr_t mem_in_use = 0, mmap_in_use = 0, peak_mem = 0;
141 static volatile struct localListBase _membase;
142 static volatile struct localListBase *membase = &_membase;
143 static void (*error_callback)(const char *) = NULL;
144 static void (*thread_lock_callback)(void) = NULL;
145 static void (*thread_unlock_callback)(void) = NULL;
147 static int malloc_debug_memset = 0;
162 /* --------------------------------------------------------------------- */
164 /* --------------------------------------------------------------------- */
166 static void print_error(const char *str, ...)
172 vsnprintf(buf, sizeof(buf), str, ap);
174 buf[sizeof(buf) - 1] = '\0';
176 if (error_callback) error_callback(buf);
179 static void mem_lock_thread(void)
181 if (thread_lock_callback)
182 thread_lock_callback();
185 static void mem_unlock_thread(void)
187 if (thread_unlock_callback)
188 thread_unlock_callback();
191 int MEM_check_memory_integrity(void)
193 const char *err_val = NULL;
195 /* check_memlist starts from the front, and runs until it finds
196 * the requested chunk. For this test, that's the last one. */
197 listend = membase->last;
199 err_val = check_memlist(listend);
201 if (err_val == NULL) return 0;
206 void MEM_set_error_callback(void (*func)(const char *))
208 error_callback = func;
211 void MEM_set_lock_callback(void (*lock)(void), void (*unlock)(void))
213 thread_lock_callback = lock;
214 thread_unlock_callback = unlock;
217 void MEM_set_memory_debug(void)
219 malloc_debug_memset = 1;
222 size_t MEM_allocN_len(void *vmemh)
225 MemHead *memh = vmemh;
235 void *MEM_dupallocN(void *vmemh)
240 MemHead *memh = vmemh;
244 newp = MEM_mapallocN(memh->len, "dupli_mapalloc");
246 newp = MEM_mallocN(memh->len, "dupli_alloc");
248 if (newp == NULL) return NULL;
250 memcpy(newp, vmemh, memh->len);
256 void *MEM_reallocN(void *vmemh, size_t len)
261 MemHead *memh = vmemh;
264 newp = MEM_mallocN(len, memh->name);
267 memcpy(newp, vmemh, len);
269 memcpy(newp, vmemh, memh->len);
278 static void make_memhead_header(MemHead *memh, size_t len, const char *str)
282 memh->tag1 = MEMTAG1;
284 memh->nextname = NULL;
287 memh->tag2 = MEMTAG2;
289 memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + len);
290 memt->tag3 = MEMTAG3;
292 addtail(membase, &memh->next);
294 memh->nextname = MEMNEXT(memh->next)->name;
300 peak_mem = mem_in_use > peak_mem ? mem_in_use : peak_mem;
303 void *MEM_mallocN(size_t len, const char *str)
309 len = (len + 3) & ~3; /* allocate in units of 4 */
311 memh = (MemHead *)malloc(len + sizeof(MemHead) + sizeof(MemTail));
314 make_memhead_header(memh, len, str);
316 if (malloc_debug_memset && len)
317 memset(memh + 1, 255, len);
319 #ifdef DEBUG_MEMCOUNTER
320 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
321 memcount_raise(__func__);
322 memh->_count = _mallocn_count++;
327 print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
328 SIZET_ARG(len), str, mem_in_use);
332 void *MEM_callocN(size_t len, const char *str)
338 len = (len + 3) & ~3; /* allocate in units of 4 */
340 memh = (MemHead *)calloc(len + sizeof(MemHead) + sizeof(MemTail), 1);
343 make_memhead_header(memh, len, str);
345 #ifdef DEBUG_MEMCOUNTER
346 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
347 memcount_raise(__func__);
348 memh->_count = _mallocn_count++;
353 print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
354 SIZET_ARG(len), str, mem_in_use);
358 /* note; mmap returns zero'd memory */
359 void *MEM_mapallocN(size_t len, const char *str)
365 len = (len + 3) & ~3; /* allocate in units of 4 */
367 memh = mmap(NULL, len + sizeof(MemHead) + sizeof(MemTail),
368 PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
370 if (memh != (MemHead *)-1) {
371 make_memhead_header(memh, len, str);
374 peak_mem = mmap_in_use > peak_mem ? mmap_in_use : peak_mem;
376 #ifdef DEBUG_MEMCOUNTER
377 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
378 memcount_raise(__func__);
379 memh->_count = _mallocn_count++;
385 print_error("Mapalloc returns null, fallback to regular malloc: "
386 "len=" SIZET_FORMAT " in %s, total %u\n",
387 SIZET_ARG(len), str, mmap_in_use);
388 return MEM_callocN(len, str);
392 /* Memory statistics print */
393 typedef struct MemPrintBlock {
399 static int compare_name(const void *p1, const void *p2)
401 const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
402 const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
404 return strcmp(pb1->name, pb2->name);
407 static int compare_len(const void *p1, const void *p2)
409 const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
410 const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
412 if (pb1->len < pb2->len)
414 else if (pb1->len == pb2->len)
420 void MEM_printmemlist_stats(void)
423 MemPrintBlock *pb, *printblock;
428 /* put memory blocks into array */
429 printblock = malloc(sizeof(MemPrintBlock) * totblock);
434 membl = membase->first;
435 if (membl) membl = MEMNEXT(membl);
438 pb->name = membl->name;
439 pb->len = membl->len;
446 membl = MEMNEXT(membl->next);
450 /* sort by name and add together blocks with the same name */
451 qsort(printblock, totpb, sizeof(MemPrintBlock), compare_name);
452 for (a = 0, b = 0; a < totpb; a++) {
456 else if (strcmp(printblock[a].name, printblock[b].name) == 0) {
457 printblock[b].len += printblock[a].len;
458 printblock[b].items++;
462 memcpy(&printblock[b], &printblock[a], sizeof(MemPrintBlock));
467 /* sort by length and print */
468 qsort(printblock, totpb, sizeof(MemPrintBlock), compare_len);
469 printf("\ntotal memory len: %.3f MB\n",
470 (double)mem_in_use / (double)(1024 * 1024));
471 printf(" ITEMS TOTAL-MiB AVERAGE-KiB TYPE\n");
472 for (a = 0, pb = printblock; a < totpb; a++, pb++) {
473 printf("%6d (%8.3f %8.3f) %s\n",
474 pb->items, (double)pb->len / (double)(1024 * 1024),
475 (double)pb->len / 1024.0 / (double)pb->items, pb->name);
481 #if 0 /* GLIBC only */
486 static const char mem_printmemlist_pydict_script[] =
489 "for mb_item in membase:\n"
490 " mb_item_user_size = mb_userinfo.setdefault(mb_item['name'], [0,0])\n"
491 " mb_item_user_size[0] += 1 # Add a user\n"
492 " mb_item_user_size[1] += mb_item['len'] # Increment the size\n"
493 " totmem += mb_item['len']\n"
494 "print('(membase) items:', len(membase), '| unique-names:',\n"
495 " len(mb_userinfo), '| total-mem:', totmem)\n"
496 "mb_userinfo_sort = list(mb_userinfo.items())\n"
497 "for sort_name, sort_func in (('size', lambda a: -a[1][1]),\n"
498 " ('users', lambda a: -a[1][0]),\n"
499 " ('name', lambda a: a[0])):\n"
500 " print('\\nSorting by:', sort_name)\n"
501 " mb_userinfo_sort.sort(key = sort_func)\n"
502 " for item in mb_userinfo_sort:\n"
503 " print('name:%%s, users:%%i, len:%%i' %%\n"
504 " (item[0], item[1][0], item[1][1]))\n";
506 /* Prints in python syntax for easy */
507 static void MEM_printmemlist_internal(int pydict)
513 membl = membase->first;
514 if (membl) membl = MEMNEXT(membl);
517 print_error("# membase_debug.py\n");
518 print_error("membase = [\n");
523 " {'len':" SIZET_FORMAT ", "
525 "'pointer':'%p'},\n",
526 SIZET_ARG(membl->len), membl->name, (void *)(membl + 1));
529 #ifdef DEBUG_MEMCOUNTER
530 print_error("%s len: " SIZET_FORMAT " %p, count: %d\n",
531 membl->name, SIZET_ARG(membl->len), membl + 1,
534 print_error("%s len: " SIZET_FORMAT " %p\n",
535 membl->name, SIZET_ARG(membl->len), membl + 1);
539 membl = MEMNEXT(membl->next);
543 fprintf(stderr, "]\n\n");
544 fprintf(stderr, mem_printmemlist_pydict_script);
550 void MEM_callbackmemlist(void (*func)(void *))
556 membl = membase->first;
557 if (membl) membl = MEMNEXT(membl);
562 membl = MEMNEXT(membl->next);
569 short MEM_testN(void *vmemh)
575 membl = membase->first;
576 if (membl) membl = MEMNEXT(membl);
579 if (vmemh == membl + 1) {
585 membl = MEMNEXT(membl->next);
591 print_error("Memoryblock %p: pointer not in memlist\n", vmemh);
595 void MEM_printmemlist(void)
597 MEM_printmemlist_internal(0);
599 void MEM_printmemlist_pydict(void)
601 MEM_printmemlist_internal(1);
604 short MEM_freeN(void *vmemh) /* anders compileertie niet meer */
608 MemHead *memh = vmemh;
612 MemorY_ErroR("free", "attempt to free NULL pointer");
613 /* print_error(err_stream, "%d\n", (memh+4000)->tag1); */
617 if (sizeof(intptr_t) == 8) {
618 if (((intptr_t) memh) & 0x7) {
619 MemorY_ErroR("free", "attempt to free illegal pointer");
624 if (((intptr_t) memh) & 0x3) {
625 MemorY_ErroR("free", "attempt to free illegal pointer");
631 if (memh->tag1 == MEMFREE && memh->tag2 == MEMFREE) {
632 MemorY_ErroR(memh->name, "double free");
637 if ((memh->tag1 == MEMTAG1) &&
638 (memh->tag2 == MEMTAG2) &&
639 ((memh->len & 0x3) == 0))
641 memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + memh->len);
642 if (memt->tag3 == MEMTAG3) {
644 memh->tag1 = MEMFREE;
645 memh->tag2 = MEMFREE;
646 memt->tag3 = MEMFREE;
655 MemorY_ErroR(memh->name, "end corrupt");
656 name = check_memlist(memh);
658 if (name != memh->name) MemorY_ErroR(name, "is also corrupt");
663 name = check_memlist(memh);
665 MemorY_ErroR("free", "pointer not in memlist");
667 MemorY_ErroR(name, "error in header");
671 /* here a DUMP should happen */
678 /* --------------------------------------------------------------------- */
679 /* local functions */
680 /* --------------------------------------------------------------------- */
682 static void addtail(volatile localListBase *listbase, void *vlink)
684 struct localLink *link = vlink;
686 /* for a generic API error checks here is fine but
687 * the limited use here they will never be NULL */
689 if (link == NULL) return;
690 if (listbase == NULL) return;
694 link->prev = listbase->last;
696 if (listbase->last) ((struct localLink *)listbase->last)->next = link;
697 if (listbase->first == NULL) listbase->first = link;
698 listbase->last = link;
701 static void remlink(volatile localListBase *listbase, void *vlink)
703 struct localLink *link = vlink;
705 /* for a generic API error checks here is fine but
706 * the limited use here they will never be NULL */
708 if (link == NULL) return;
709 if (listbase == NULL) return;
712 if (link->next) link->next->prev = link->prev;
713 if (link->prev) link->prev->next = link->next;
715 if (listbase->last == link) listbase->last = link->prev;
716 if (listbase->first == link) listbase->first = link->next;
719 static void rem_memblock(MemHead *memh)
721 remlink(membase, &memh->next);
724 MEMNEXT(memh->prev)->nextname = MEMNEXT(memh->next)->name;
726 MEMNEXT(memh->prev)->nextname = NULL;
730 mem_in_use -= memh->len;
733 mmap_in_use -= memh->len;
734 if (munmap(memh, memh->len + sizeof(MemHead) + sizeof(MemTail)))
735 printf("Couldn't unmap memory %s\n", memh->name);
738 if (malloc_debug_memset && memh->len)
739 memset(memh + 1, 255, memh->len);
744 static void MemorY_ErroR(const char *block, const char *error)
746 print_error("Memoryblock %s: %s\n", block, error);
748 #ifdef WITH_ASSERT_ABORT
753 static const char *check_memlist(MemHead *memh)
755 MemHead *forw, *back, *forwok, *backok;
758 forw = membase->first;
759 if (forw) forw = MEMNEXT(forw);
762 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
764 if (forw->next) forw = MEMNEXT(forw->next);
768 back = (MemHead *) membase->last;
769 if (back) back = MEMNEXT(back);
772 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
774 if (back->prev) back = MEMNEXT(back->prev);
778 if (forw != back) return ("MORE THAN 1 MEMORYBLOCK CORRUPT");
780 if (forw == NULL && back == NULL) {
781 /* no wrong headers found then but in search of memblock */
783 forw = membase->first;
784 if (forw) forw = MEMNEXT(forw);
787 if (forw == memh) break;
788 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
790 if (forw->next) forw = MEMNEXT(forw->next);
793 if (forw == NULL) return NULL;
795 back = (MemHead *) membase->last;
796 if (back) back = MEMNEXT(back);
799 if (back == memh) break;
800 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
802 if (back->prev) back = MEMNEXT(back->prev);
807 if (forwok) name = forwok->nextname;
808 else name = "No name found";
811 /* to be sure but this block is removed from the list */
814 forwok->next = (MemHead *)&backok->next;
815 backok->prev = (MemHead *)&forwok->next;
816 forwok->nextname = backok->name;
820 membase->last = (struct localLink *) &forwok->next;
826 membase->first = &backok->next;
829 membase->first = membase->last = NULL;
834 MemorY_ErroR(name, "Additional error in header");
835 return("Additional error in header");
841 uintptr_t MEM_get_peak_memory(void)
846 _peak_mem = peak_mem;
852 void MEM_reset_peak_memory(void)
859 uintptr_t MEM_get_memory_in_use(void)
861 uintptr_t _mem_in_use;
864 _mem_in_use = mem_in_use;
870 uintptr_t MEM_get_mapped_memory_in_use(void)
872 uintptr_t _mmap_in_use;
875 _mmap_in_use = mmap_in_use;
881 int MEM_get_memory_blocks_in_use(void)
886 _totblock = totblock;
893 const char *MEM_name_ptr(void *vmemh)
896 MemHead *memh = vmemh;
901 return "MEM_name_ptr(NULL)";