Guardedalloc: Don't use aligned blocks to calculate memory sloppyness
[blender-staging.git] / intern / guardedalloc / intern / mallocn_guarded_impl.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
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.
8  *
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.
13  *
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.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Brecht Van Lommel
24  *                 Campbell Barton
25  *                 Sergey Sharybin
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 /** \file guardedalloc/intern/mallocn.c
31  *  \ingroup MEM
32  *
33  * Guarded memory allocation, and boundary-write detection.
34  */
35
36 #include <stdlib.h>
37 #include <string.h> /* memcpy */
38 #include <stdarg.h>
39 #include <sys/types.h>
40
41 #include "MEM_guardedalloc.h"
42
43 /* to ensure strict conversions */
44 #include "../../source/blender/blenlib/BLI_strict_flags.h"
45
46 #include "mallocn_intern.h"
47 #include "atomic_ops.h"
48
49 /* Only for debugging:
50  * store original buffer's name when doing MEM_dupallocN
51  * helpful to profile issues with non-freed "dup_alloc" buffers,
52  * but this introduces some overhead to memory header and makes
53  * things slower a bit, so better to keep disabled by default
54  */
55 //#define DEBUG_MEMDUPLINAME
56
57 /* Only for debugging:
58  * lets you count the allocations so as to find the allocator of unfreed memory
59  * in situations where the leak is predictable */
60
61 //#define DEBUG_MEMCOUNTER
62
63 /* Only for debugging:
64  * defining DEBUG_THREADS will enable check whether memory manager
65  * is locked with a mutex when allocation is called from non-main
66  * thread.
67  *
68  * This helps troubleshooting memory issues caused by the fact
69  * guarded allocator is not thread-safe, however this check will
70  * fail to check allocations from openmp threads.
71  */
72 //#define DEBUG_THREADS
73
74 /* Only for debugging:
75  * Defining DEBUG_BACKTRACE will store a backtrace from where
76  * memory block was allocated and print this trace for all
77  * unfreed blocks.
78  */
79 //#define DEBUG_BACKTRACE
80
81 #ifdef DEBUG_BACKTRACE
82 #  define BACKTRACE_SIZE 100
83 #endif
84
85 #ifdef DEBUG_MEMCOUNTER
86    /* set this to the value that isn't being freed */
87 #  define DEBUG_MEMCOUNTER_ERROR_VAL 0
88 static int _mallocn_count = 0;
89
90 /* breakpoint here */
91 static void memcount_raise(const char *name)
92 {
93         fprintf(stderr, "%s: memcount-leak, %d\n", name, _mallocn_count);
94 }
95 #endif
96
97 /* --------------------------------------------------------------------- */
98 /* Data definition                                                       */
99 /* --------------------------------------------------------------------- */
100 /* all memory chunks are put in linked lists */
101 typedef struct localLink {
102         struct localLink *next, *prev;
103 } localLink;
104
105 typedef struct localListBase {
106         void *first, *last;
107 } localListBase;
108
109 /* note: keep this struct aligned (e.g., irix/gcc) - Hos */
110 typedef struct MemHead {
111         int tag1;
112         size_t len;
113         struct MemHead *next, *prev;
114         const char *name;
115         const char *nextname;
116         int tag2;
117         short mmap;  /* if true, memory was mmapped */
118         short alignment;  /* if non-zero aligned alloc was used
119                            * and alignment is stored here.
120                            */
121 #ifdef DEBUG_MEMCOUNTER
122         int _count;
123 #endif
124
125 #ifdef DEBUG_MEMDUPLINAME
126         int need_free_name, pad;
127 #endif
128
129 #ifdef DEBUG_BACKTRACE
130         void *backtrace[BACKTRACE_SIZE];
131         int backtrace_size;
132 #endif
133 } MemHead;
134
135 typedef MemHead MemHeadAligned;
136
137 /* for openmp threading asserts, saves time troubleshooting
138  * we may need to extend this if blender code starts using MEM_
139  * functions inside OpenMP correctly with omp_set_lock() */
140
141 #if 0  /* disable for now, only use to debug openmp code which doesn lock threads for malloc */
142 #if defined(_OPENMP) && defined(DEBUG)
143 #  include <assert.h>
144 #  include <omp.h>
145 #  define DEBUG_OMP_MALLOC
146 #endif
147 #endif
148
149 #ifdef DEBUG_THREADS
150 #  include <assert.h>
151 #  include <pthread.h>
152 static pthread_t mainid;
153 #endif
154
155 #ifdef DEBUG_BACKTRACE
156 #  if defined(__linux__) || defined(__APPLE__)
157 #    include <execinfo.h>
158 // Windows is not supported yet.
159 //#  elif defined(_MSV_VER)
160 //#    include <DbgHelp.h>
161 #  endif
162 #endif
163
164 typedef struct MemTail {
165         int tag3, pad;
166 } MemTail;
167
168
169 /* --------------------------------------------------------------------- */
170 /* local functions                                                       */
171 /* --------------------------------------------------------------------- */
172
173 static void addtail(volatile localListBase *listbase, void *vlink);
174 static void remlink(volatile localListBase *listbase, void *vlink);
175 static void rem_memblock(MemHead *memh);
176 static void MemorY_ErroR(const char *block, const char *error);
177 static const char *check_memlist(MemHead *memh);
178
179 /* --------------------------------------------------------------------- */
180 /* locally used defines                                                  */
181 /* --------------------------------------------------------------------- */
182
183 #ifdef __BIG_ENDIAN__
184 #  define MAKE_ID(a, b, c, d) ((int)(a) << 24 | (int)(b) << 16 | (c) << 8 | (d))
185 #else
186 #  define MAKE_ID(a, b, c, d) ((int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a))
187 #endif
188
189 #define MEMTAG1 MAKE_ID('M', 'E', 'M', 'O')
190 #define MEMTAG2 MAKE_ID('R', 'Y', 'B', 'L')
191 #define MEMTAG3 MAKE_ID('O', 'C', 'K', '!')
192 #define MEMFREE MAKE_ID('F', 'R', 'E', 'E')
193
194 #define MEMNEXT(x) \
195         ((MemHead *)(((char *) x) - ((char *) &(((MemHead *)0)->next))))
196
197 /* --------------------------------------------------------------------- */
198 /* vars                                                                  */
199 /* --------------------------------------------------------------------- */
200         
201
202 static unsigned int totblock = 0;
203 static size_t mem_in_use = 0, mmap_in_use = 0, peak_mem = 0;
204
205 static volatile struct localListBase _membase;
206 static volatile struct localListBase *membase = &_membase;
207 static void (*error_callback)(const char *) = NULL;
208 static void (*thread_lock_callback)(void) = NULL;
209 static void (*thread_unlock_callback)(void) = NULL;
210
211 static bool malloc_debug_memset = false;
212
213 #ifdef malloc
214 #undef malloc
215 #endif
216
217 #ifdef calloc
218 #undef calloc
219 #endif
220
221 #ifdef free
222 #undef free
223 #endif
224
225
226 /* --------------------------------------------------------------------- */
227 /* implementation                                                        */
228 /* --------------------------------------------------------------------- */
229
230 #ifdef __GNUC__
231 __attribute__ ((format(printf, 1, 2)))
232 #endif
233 static void print_error(const char *str, ...)
234 {
235         char buf[1024];
236         va_list ap;
237
238         va_start(ap, str);
239         vsnprintf(buf, sizeof(buf), str, ap);
240         va_end(ap);
241         buf[sizeof(buf) - 1] = '\0';
242
243         if (error_callback)
244                 error_callback(buf);
245         else
246                 fputs(buf, stderr);
247 }
248
249 static void mem_lock_thread(void)
250 {
251 #ifdef DEBUG_THREADS
252         static int initialized = 0;
253
254         if (initialized == 0) {
255                 /* assume first allocation happens from main thread */
256                 mainid = pthread_self();
257                 initialized = 1;
258         }
259
260         if (!pthread_equal(pthread_self(), mainid) && thread_lock_callback == NULL) {
261                 assert(!"Memory function is called from non-main thread without lock");
262         }
263 #endif
264
265 #ifdef DEBUG_OMP_MALLOC
266         assert(omp_in_parallel() == 0);
267 #endif
268
269         if (thread_lock_callback)
270                 thread_lock_callback();
271 }
272
273 static void mem_unlock_thread(void)
274 {
275 #ifdef DEBUG_THREADS
276         if (!pthread_equal(pthread_self(), mainid) && thread_lock_callback == NULL) {
277                 assert(!"Thread lock was removed while allocation from thread is in progress");
278         }
279 #endif
280
281         if (thread_unlock_callback)
282                 thread_unlock_callback();
283 }
284
285 bool MEM_guarded_check_memory_integrity(void)
286 {
287         const char *err_val = NULL;
288         MemHead *listend;
289         /* check_memlist starts from the front, and runs until it finds
290          * the requested chunk. For this test, that's the last one. */
291         listend = membase->last;
292         
293         err_val = check_memlist(listend);
294
295         return (err_val != NULL);
296 }
297
298
299 void MEM_guarded_set_error_callback(void (*func)(const char *))
300 {
301         error_callback = func;
302 }
303
304 void MEM_guarded_set_lock_callback(void (*lock)(void), void (*unlock)(void))
305 {
306         thread_lock_callback = lock;
307         thread_unlock_callback = unlock;
308 }
309
310 void MEM_guarded_set_memory_debug(void)
311 {
312         malloc_debug_memset = true;
313 }
314
315 size_t MEM_guarded_allocN_len(const void *vmemh)
316 {
317         if (vmemh) {
318                 const MemHead *memh = vmemh;
319         
320                 memh--;
321                 return memh->len;
322         }
323         else {
324                 return 0;
325         }
326 }
327
328 void *MEM_guarded_dupallocN(const void *vmemh)
329 {
330         void *newp = NULL;
331         
332         if (vmemh) {
333                 const MemHead *memh = vmemh;
334                 memh--;
335
336 #ifndef DEBUG_MEMDUPLINAME
337                 if (UNLIKELY(memh->mmap))
338                         newp = MEM_guarded_mapallocN(memh->len, "dupli_mapalloc");
339                 else if (LIKELY(memh->alignment == 0))
340                         newp = MEM_guarded_mapallocN(memh->len, "dupli_mapalloc");
341                 else
342                         newp = MEM_guarded_mallocN_aligned(memh->len, (size_t) memh->alignment, "dupli_alloc");
343
344                 if (newp == NULL) return NULL;
345 #else
346                 {
347                         MemHead *nmemh;
348                         char *name = malloc(strlen(memh->name) + 24);
349
350                         if (UNLIKELY(memh->mmap)) {
351                                 sprintf(name, "%s %s", "dupli_mapalloc", memh->name);
352                                 newp = MEM_guarded_mapallocN(memh->len, name);
353                         }
354                         else if (LIKELY(memh->alignment == 0)) {
355                                 sprintf(name, "%s %s", "dupli_alloc", memh->name);
356                                 newp = MEM_guarded_mallocN(memh->len, name);
357                         }
358                         else {
359                                 sprintf(name, "%s %s", "dupli_alloc", memh->name);
360                                 newp = MEM_guarded_mallocN_aligned(memh->len, (size_t) memh->alignment, name);
361                         }
362
363                         if (newp == NULL) return NULL;
364
365                         nmemh = newp;
366                         nmemh--;
367
368                         nmemh->need_free_name = 1;
369                 }
370 #endif
371
372                 memcpy(newp, vmemh, memh->len);
373         }
374
375         return newp;
376 }
377
378 void *MEM_guarded_reallocN_id(void *vmemh, size_t len, const char *str)
379 {
380         void *newp = NULL;
381         
382         if (vmemh) {
383                 MemHead *memh = vmemh;
384                 memh--;
385
386                 if (LIKELY(memh->alignment == 0)) {
387                         newp = MEM_guarded_mallocN(len, memh->name);
388                 }
389                 else {
390                         newp = MEM_guarded_mallocN_aligned(len, (size_t) memh->alignment, memh->name);
391                 }
392
393                 if (newp) {
394                         if (len < memh->len) {
395                                 /* shrink */
396                                 memcpy(newp, vmemh, len);
397                         }
398                         else {
399                                 /* grow (or remain same size) */
400                                 memcpy(newp, vmemh, memh->len);
401                         }
402                 }
403
404                 MEM_guarded_freeN(vmemh);
405         }
406         else {
407                 newp = MEM_guarded_mallocN(len, str);
408         }
409
410         return newp;
411 }
412
413 void *MEM_guarded_recallocN_id(void *vmemh, size_t len, const char *str)
414 {
415         void *newp = NULL;
416
417         if (vmemh) {
418                 MemHead *memh = vmemh;
419                 memh--;
420
421                 if (LIKELY(memh->alignment == 0)) {
422                         newp = MEM_guarded_mallocN(len, memh->name);
423                 }
424                 else {
425                         newp = MEM_guarded_mallocN_aligned(len, (size_t) memh->alignment, memh->name);
426                 }
427
428                 if (newp) {
429                         if (len < memh->len) {
430                                 /* shrink */
431                                 memcpy(newp, vmemh, len);
432                         }
433                         else {
434                                 memcpy(newp, vmemh, memh->len);
435
436                                 if (len > memh->len) {
437                                         /* grow */
438                                         /* zero new bytes */
439                                         memset(((char *)newp) + memh->len, 0, len - memh->len);
440                                 }
441                         }
442                 }
443
444                 MEM_guarded_freeN(vmemh);
445         }
446         else {
447                 newp = MEM_guarded_callocN(len, str);
448         }
449
450         return newp;
451 }
452
453 #ifdef DEBUG_BACKTRACE
454 #  if defined(__linux__) || defined(__APPLE__)
455 static void make_memhead_backtrace(MemHead *memh)
456 {
457         memh->backtrace_size = backtrace(memh->backtrace, BACKTRACE_SIZE);
458 }
459
460 static void print_memhead_backtrace(MemHead *memh)
461 {
462         char **strings;
463         int i;
464
465         strings = backtrace_symbols(memh->backtrace, memh->backtrace_size);
466         for (i = 0; i < memh->backtrace_size; i++) {
467                 print_error("  %s\n", strings[i]);
468         }
469
470         free(strings);
471 }
472 #  else
473 static void make_memhead_backtrace(MemHead *memh)
474 {
475         (void) memh;  /* Ignored. */
476 }
477
478 static void print_memhead_backtrace(MemHead *memh)
479 {
480         (void) memh;  /* Ignored. */
481 }
482 #  endif  /* defined(__linux__) || defined(__APPLE__) */
483 #endif  /* DEBUG_BACKTRACE */
484
485 static void make_memhead_header(MemHead *memh, size_t len, const char *str)
486 {
487         MemTail *memt;
488         
489         memh->tag1 = MEMTAG1;
490         memh->name = str;
491         memh->nextname = NULL;
492         memh->len = len;
493         memh->mmap = 0;
494         memh->alignment = 0;
495         memh->tag2 = MEMTAG2;
496
497 #ifdef DEBUG_MEMDUPLINAME
498         memh->need_free_name = 0;
499 #endif
500
501 #ifdef DEBUG_BACKTRACE
502         make_memhead_backtrace(memh);
503 #endif
504
505         memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + len);
506         memt->tag3 = MEMTAG3;
507
508         atomic_add_u(&totblock, 1);
509         atomic_add_z(&mem_in_use, len);
510
511         mem_lock_thread();
512         addtail(membase, &memh->next);
513         if (memh->next) {
514                 memh->nextname = MEMNEXT(memh->next)->name;
515         }
516         peak_mem = mem_in_use > peak_mem ? mem_in_use : peak_mem;
517         mem_unlock_thread();
518 }
519
520 void *MEM_guarded_mallocN(size_t len, const char *str)
521 {
522         MemHead *memh;
523
524         len = SIZET_ALIGN_4(len);
525         
526         memh = (MemHead *)malloc(len + sizeof(MemHead) + sizeof(MemTail));
527
528         if (LIKELY(memh)) {
529                 make_memhead_header(memh, len, str);
530                 if (UNLIKELY(malloc_debug_memset && len))
531                         memset(memh + 1, 255, len);
532
533 #ifdef DEBUG_MEMCOUNTER
534                 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
535                         memcount_raise(__func__);
536                 memh->_count = _mallocn_count++;
537 #endif
538                 return (++memh);
539         }
540         print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
541                     SIZET_ARG(len), str, (unsigned int) mem_in_use);
542         return NULL;
543 }
544
545 void *MEM_guarded_mallocN_aligned(size_t len, size_t alignment, const char *str)
546 {
547         MemHead *memh;
548
549         /* It's possible that MemHead's size is not properly aligned,
550          * do extra padding to deal with this.
551          *
552          * We only support small alignments which fits into short in
553          * order to save some bits in MemHead structure.
554          */
555         size_t extra_padding = MEMHEAD_ALIGN_PADDING(alignment);
556
557         /* Huge alignment values doesn't make sense and they
558          * wouldn't fit into 'short' used in the MemHead.
559          */
560         assert(alignment < 1024);
561
562         /* We only support alignment to a power of two. */
563         assert(IS_POW2(alignment));
564
565         len = SIZET_ALIGN_4(len);
566
567         memh = (MemHead *)aligned_malloc(len + extra_padding + sizeof(MemHead) + sizeof(MemTail), alignment);
568
569         if (LIKELY(memh)) {
570                 /* We keep padding in the beginning of MemHead,
571                  * this way it's always possible to get MemHead
572                  * from the data pointer.
573                  */
574                 memh = (MemHead *)((char *)memh + extra_padding);
575
576                 make_memhead_header(memh, len, str);
577                 memh->alignment = (short) alignment;
578                 if (UNLIKELY(malloc_debug_memset && len))
579                         memset(memh + 1, 255, len);
580
581 #ifdef DEBUG_MEMCOUNTER
582                 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
583                         memcount_raise(__func__);
584                 memh->_count = _mallocn_count++;
585 #endif
586                 return (++memh);
587         }
588         print_error("aligned_malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
589                     SIZET_ARG(len), str, (unsigned int) mem_in_use);
590         return NULL;
591 }
592
593 void *MEM_guarded_callocN(size_t len, const char *str)
594 {
595         MemHead *memh;
596
597         len = SIZET_ALIGN_4(len);
598
599         memh = (MemHead *)calloc(len + sizeof(MemHead) + sizeof(MemTail), 1);
600
601         if (memh) {
602                 make_memhead_header(memh, len, str);
603 #ifdef DEBUG_MEMCOUNTER
604                 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
605                         memcount_raise(__func__);
606                 memh->_count = _mallocn_count++;
607 #endif
608                 return (++memh);
609         }
610         print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
611                     SIZET_ARG(len), str, (unsigned int) mem_in_use);
612         return NULL;
613 }
614
615 /* note; mmap returns zero'd memory */
616 void *MEM_guarded_mapallocN(size_t len, const char *str)
617 {
618         MemHead *memh;
619
620         /* on 64 bit, simply use calloc instead, as mmap does not support
621          * allocating > 4 GB on Windows. the only reason mapalloc exists
622          * is to get around address space limitations in 32 bit OSes. */
623         if (sizeof(void *) >= 8)
624                 return MEM_guarded_callocN(len, str);
625
626         len = SIZET_ALIGN_4(len);
627
628 #if defined(WIN32)
629         /* our windows mmap implementation is not thread safe */
630         mem_lock_thread();
631 #endif
632         memh = mmap(NULL, len + sizeof(MemHead) + sizeof(MemTail),
633                     PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
634 #if defined(WIN32)
635         mem_unlock_thread();
636 #endif
637
638         if (memh != (MemHead *)-1) {
639                 make_memhead_header(memh, len, str);
640                 memh->mmap = 1;
641                 atomic_add_z(&mmap_in_use, len);
642                 mem_lock_thread();
643                 peak_mem = mmap_in_use > peak_mem ? mmap_in_use : peak_mem;
644                 mem_unlock_thread();
645 #ifdef DEBUG_MEMCOUNTER
646                 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
647                         memcount_raise(__func__);
648                 memh->_count = _mallocn_count++;
649 #endif
650                 return (++memh);
651         }
652         else {
653                 print_error("Mapalloc returns null, fallback to regular malloc: "
654                             "len=" SIZET_FORMAT " in %s, total %u\n",
655                             SIZET_ARG(len), str, (unsigned int) mmap_in_use);
656                 return MEM_guarded_callocN(len, str);
657         }
658 }
659
660 /* Memory statistics print */
661 typedef struct MemPrintBlock {
662         const char *name;
663         uintptr_t len;
664         int items;
665 } MemPrintBlock;
666
667 static int compare_name(const void *p1, const void *p2)
668 {
669         const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
670         const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
671
672         return strcmp(pb1->name, pb2->name);
673 }
674
675 static int compare_len(const void *p1, const void *p2)
676 {
677         const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
678         const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
679
680         if (pb1->len < pb2->len)
681                 return 1;
682         else if (pb1->len == pb2->len)
683                 return 0;
684         else
685                 return -1;
686 }
687
688 void MEM_guarded_printmemlist_stats(void)
689 {
690         MemHead *membl;
691         MemPrintBlock *pb, *printblock;
692         unsigned int totpb, a, b;
693         size_t mem_in_use_slop = 0;
694
695         mem_lock_thread();
696
697         /* put memory blocks into array */
698         printblock = malloc(sizeof(MemPrintBlock) * totblock);
699
700         pb = printblock;
701         totpb = 0;
702
703         membl = membase->first;
704         if (membl) membl = MEMNEXT(membl);
705
706         while (membl) {
707                 pb->name = membl->name;
708                 pb->len = membl->len;
709                 pb->items = 1;
710
711                 totpb++;
712                 pb++;
713
714                 if (!membl->mmap && membl->alignment == 0) {
715                         mem_in_use_slop += (sizeof(MemHead) + sizeof(MemTail) +
716                                             malloc_usable_size((void *)membl)) - membl->len;
717                 }
718
719                 if (membl->next)
720                         membl = MEMNEXT(membl->next);
721                 else break;
722         }
723
724         /* sort by name and add together blocks with the same name */
725         qsort(printblock, totpb, sizeof(MemPrintBlock), compare_name);
726         for (a = 0, b = 0; a < totpb; a++) {
727                 if (a == b) {
728                         continue;
729                 }
730                 else if (strcmp(printblock[a].name, printblock[b].name) == 0) {
731                         printblock[b].len += printblock[a].len;
732                         printblock[b].items++;
733                 }
734                 else {
735                         b++;
736                         memcpy(&printblock[b], &printblock[a], sizeof(MemPrintBlock));
737                 }
738         }
739         totpb = b + 1;
740
741         /* sort by length and print */
742         qsort(printblock, totpb, sizeof(MemPrintBlock), compare_len);
743         printf("\ntotal memory len: %.3f MB\n",
744                (double)mem_in_use / (double)(1024 * 1024));
745         printf("peak memory len: %.3f MB\n",
746                (double)peak_mem / (double)(1024 * 1024));
747         printf("slop memory len: %.3f MB\n",
748                (double)mem_in_use_slop / (double)(1024 * 1024));
749         printf(" ITEMS TOTAL-MiB AVERAGE-KiB TYPE\n");
750         for (a = 0, pb = printblock; a < totpb; a++, pb++) {
751                 printf("%6d (%8.3f  %8.3f) %s\n",
752                        pb->items, (double)pb->len / (double)(1024 * 1024),
753                        (double)pb->len / 1024.0 / (double)pb->items, pb->name);
754         }
755         free(printblock);
756         
757         mem_unlock_thread();
758
759 #ifdef HAVE_MALLOC_STATS
760         printf("System Statistics:\n");
761         malloc_stats();
762 #endif
763 }
764
765 static const char mem_printmemlist_pydict_script[] =
766 "mb_userinfo = {}\n"
767 "totmem = 0\n"
768 "for mb_item in membase:\n"
769 "    mb_item_user_size = mb_userinfo.setdefault(mb_item['name'], [0,0])\n"
770 "    mb_item_user_size[0] += 1 # Add a user\n"
771 "    mb_item_user_size[1] += mb_item['len'] # Increment the size\n"
772 "    totmem += mb_item['len']\n"
773 "print('(membase) items:', len(membase), '| unique-names:',\n"
774 "      len(mb_userinfo), '| total-mem:', totmem)\n"
775 "mb_userinfo_sort = list(mb_userinfo.items())\n"
776 "for sort_name, sort_func in (('size', lambda a: -a[1][1]),\n"
777 "                             ('users', lambda a: -a[1][0]),\n"
778 "                             ('name', lambda a: a[0])):\n"
779 "    print('\\nSorting by:', sort_name)\n"
780 "    mb_userinfo_sort.sort(key = sort_func)\n"
781 "    for item in mb_userinfo_sort:\n"
782 "        print('name:%%s, users:%%i, len:%%i' %%\n"
783 "              (item[0], item[1][0], item[1][1]))\n";
784
785 /* Prints in python syntax for easy */
786 static void MEM_guarded_printmemlist_internal(int pydict)
787 {
788         MemHead *membl;
789
790         mem_lock_thread();
791
792         membl = membase->first;
793         if (membl) membl = MEMNEXT(membl);
794         
795         if (pydict) {
796                 print_error("# membase_debug.py\n");
797                 print_error("membase = [\n");
798         }
799         while (membl) {
800                 if (pydict) {
801                         print_error("    {'len':" SIZET_FORMAT ", "
802                                     "'name':'''%s''', "
803                                     "'pointer':'%p'},\n",
804                                     SIZET_ARG(membl->len), membl->name, (void *)(membl + 1));
805                 }
806                 else {
807 #ifdef DEBUG_MEMCOUNTER
808                         print_error("%s len: " SIZET_FORMAT " %p, count: %d\n",
809                                     membl->name, SIZET_ARG(membl->len), membl + 1,
810                                     membl->_count);
811 #else
812                         print_error("%s len: " SIZET_FORMAT " %p\n",
813                                     membl->name, SIZET_ARG(membl->len), (void *)(membl + 1));
814 #endif
815 #ifdef DEBUG_BACKTRACE
816                         print_memhead_backtrace(membl);
817 #endif
818                 }
819                 if (membl->next)
820                         membl = MEMNEXT(membl->next);
821                 else break;
822         }
823         if (pydict) {
824                 print_error("]\n\n");
825                 print_error(mem_printmemlist_pydict_script);
826         }
827         
828         mem_unlock_thread();
829 }
830
831 void MEM_guarded_callbackmemlist(void (*func)(void *))
832 {
833         MemHead *membl;
834
835         mem_lock_thread();
836
837         membl = membase->first;
838         if (membl) membl = MEMNEXT(membl);
839
840         while (membl) {
841                 func(membl + 1);
842                 if (membl->next)
843                         membl = MEMNEXT(membl->next);
844                 else break;
845         }
846
847         mem_unlock_thread();
848 }
849
850 #if 0
851 short MEM_guarded_testN(void *vmemh)
852 {
853         MemHead *membl;
854
855         mem_lock_thread();
856
857         membl = membase->first;
858         if (membl) membl = MEMNEXT(membl);
859
860         while (membl) {
861                 if (vmemh == membl + 1) {
862                         mem_unlock_thread();
863                         return 1;
864                 }
865
866                 if (membl->next)
867                         membl = MEMNEXT(membl->next);
868                 else break;
869         }
870
871         mem_unlock_thread();
872
873         print_error("Memoryblock %p: pointer not in memlist\n", vmemh);
874         return 0;
875 }
876 #endif
877
878 void MEM_guarded_printmemlist(void)
879 {
880         MEM_guarded_printmemlist_internal(0);
881 }
882 void MEM_guarded_printmemlist_pydict(void)
883 {
884         MEM_guarded_printmemlist_internal(1);
885 }
886
887 void MEM_guarded_freeN(void *vmemh)
888 {
889         MemTail *memt;
890         MemHead *memh = vmemh;
891         const char *name;
892
893         if (memh == NULL) {
894                 MemorY_ErroR("free", "attempt to free NULL pointer");
895                 /* print_error(err_stream, "%d\n", (memh+4000)->tag1); */
896                 return;
897         }
898
899         if (sizeof(intptr_t) == 8) {
900                 if (((intptr_t) memh) & 0x7) {
901                         MemorY_ErroR("free", "attempt to free illegal pointer");
902                         return;
903                 }
904         }
905         else {
906                 if (((intptr_t) memh) & 0x3) {
907                         MemorY_ErroR("free", "attempt to free illegal pointer");
908                         return;
909                 }
910         }
911         
912         memh--;
913         if (memh->tag1 == MEMFREE && memh->tag2 == MEMFREE) {
914                 MemorY_ErroR(memh->name, "double free");
915                 return;
916         }
917
918         if ((memh->tag1 == MEMTAG1) &&
919             (memh->tag2 == MEMTAG2) &&
920             ((memh->len & 0x3) == 0))
921         {
922                 memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + memh->len);
923                 if (memt->tag3 == MEMTAG3) {
924                         
925                         memh->tag1 = MEMFREE;
926                         memh->tag2 = MEMFREE;
927                         memt->tag3 = MEMFREE;
928                         /* after tags !!! */
929                         rem_memblock(memh);
930
931                         return;
932                 }
933                 MemorY_ErroR(memh->name, "end corrupt");
934                 name = check_memlist(memh);
935                 if (name != NULL) {
936                         if (name != memh->name) MemorY_ErroR(name, "is also corrupt");
937                 }
938         }
939         else {
940                 mem_lock_thread();
941                 name = check_memlist(memh);
942                 mem_unlock_thread();
943                 if (name == NULL)
944                         MemorY_ErroR("free", "pointer not in memlist");
945                 else
946                         MemorY_ErroR(name, "error in header");
947         }
948
949         totblock--;
950         /* here a DUMP should happen */
951
952         return;
953 }
954
955 /* --------------------------------------------------------------------- */
956 /* local functions                                                       */
957 /* --------------------------------------------------------------------- */
958
959 static void addtail(volatile localListBase *listbase, void *vlink)
960 {
961         struct localLink *link = vlink;
962
963         /* for a generic API error checks here is fine but
964          * the limited use here they will never be NULL */
965 #if 0
966         if (link == NULL) return;
967         if (listbase == NULL) return;
968 #endif
969
970         link->next = NULL;
971         link->prev = listbase->last;
972
973         if (listbase->last) ((struct localLink *)listbase->last)->next = link;
974         if (listbase->first == NULL) listbase->first = link;
975         listbase->last = link;
976 }
977
978 static void remlink(volatile localListBase *listbase, void *vlink)
979 {
980         struct localLink *link = vlink;
981
982         /* for a generic API error checks here is fine but
983          * the limited use here they will never be NULL */
984 #if 0
985         if (link == NULL) return;
986         if (listbase == NULL) return;
987 #endif
988
989         if (link->next) link->next->prev = link->prev;
990         if (link->prev) link->prev->next = link->next;
991
992         if (listbase->last == link) listbase->last = link->prev;
993         if (listbase->first == link) listbase->first = link->next;
994 }
995
996 static void rem_memblock(MemHead *memh)
997 {
998         mem_lock_thread();
999         remlink(membase, &memh->next);
1000         if (memh->prev) {
1001                 if (memh->next)
1002                         MEMNEXT(memh->prev)->nextname = MEMNEXT(memh->next)->name;
1003                 else
1004                         MEMNEXT(memh->prev)->nextname = NULL;
1005         }
1006         mem_unlock_thread();
1007
1008         atomic_sub_u(&totblock, 1);
1009         atomic_sub_z(&mem_in_use, memh->len);
1010
1011 #ifdef DEBUG_MEMDUPLINAME
1012         if (memh->need_free_name)
1013                 free((char *) memh->name);
1014 #endif
1015
1016         if (memh->mmap) {
1017                 atomic_sub_z(&mmap_in_use, memh->len);
1018 #if defined(WIN32)
1019                 /* our windows mmap implementation is not thread safe */
1020                 mem_lock_thread();
1021 #endif
1022                 if (munmap(memh, memh->len + sizeof(MemHead) + sizeof(MemTail)))
1023                         printf("Couldn't unmap memory %s\n", memh->name);
1024 #if defined(WIN32)
1025                 mem_unlock_thread();
1026 #endif
1027         }
1028         else {
1029                 if (UNLIKELY(malloc_debug_memset && memh->len))
1030                         memset(memh + 1, 255, memh->len);
1031                 if (LIKELY(memh->alignment == 0)) {
1032                         free(memh);
1033                 }
1034                 else {
1035                         aligned_free(MEMHEAD_REAL_PTR(memh));
1036                 }
1037         }
1038 }
1039
1040 static void MemorY_ErroR(const char *block, const char *error)
1041 {
1042         print_error("Memoryblock %s: %s\n", block, error);
1043
1044 #ifdef WITH_ASSERT_ABORT
1045         abort();
1046 #endif
1047 }
1048
1049 static const char *check_memlist(MemHead *memh)
1050 {
1051         MemHead *forw, *back, *forwok, *backok;
1052         const char *name;
1053
1054         forw = membase->first;
1055         if (forw) forw = MEMNEXT(forw);
1056         forwok = NULL;
1057         while (forw) {
1058                 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
1059                 forwok = forw;
1060                 if (forw->next) forw = MEMNEXT(forw->next);
1061                 else forw = NULL;
1062         }
1063
1064         back = (MemHead *) membase->last;
1065         if (back) back = MEMNEXT(back);
1066         backok = NULL;
1067         while (back) {
1068                 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
1069                 backok = back;
1070                 if (back->prev) back = MEMNEXT(back->prev);
1071                 else back = NULL;
1072         }
1073
1074         if (forw != back) return ("MORE THAN 1 MEMORYBLOCK CORRUPT");
1075
1076         if (forw == NULL && back == NULL) {
1077                 /* no wrong headers found then but in search of memblock */
1078
1079                 forw = membase->first;
1080                 if (forw) forw = MEMNEXT(forw);
1081                 forwok = NULL;
1082                 while (forw) {
1083                         if (forw == memh) break;
1084                         if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
1085                         forwok = forw;
1086                         if (forw->next) forw = MEMNEXT(forw->next);
1087                         else forw = NULL;
1088                 }
1089                 if (forw == NULL) return NULL;
1090
1091                 back = (MemHead *) membase->last;
1092                 if (back) back = MEMNEXT(back);
1093                 backok = NULL;
1094                 while (back) {
1095                         if (back == memh) break;
1096                         if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
1097                         backok = back;
1098                         if (back->prev) back = MEMNEXT(back->prev);
1099                         else back = NULL;
1100                 }
1101         }
1102
1103         if (forwok) name = forwok->nextname;
1104         else name = "No name found";
1105
1106         if (forw == memh) {
1107                 /* to be sure but this block is removed from the list */
1108                 if (forwok) {
1109                         if (backok) {
1110                                 forwok->next = (MemHead *)&backok->next;
1111                                 backok->prev = (MemHead *)&forwok->next;
1112                                 forwok->nextname = backok->name;
1113                         }
1114                         else {
1115                                 forwok->next = NULL;
1116                                 membase->last = (struct localLink *) &forwok->next;
1117                         }
1118                 }
1119                 else {
1120                         if (backok) {
1121                                 backok->prev = NULL;
1122                                 membase->first = &backok->next;
1123                         }
1124                         else {
1125                                 membase->first = membase->last = NULL;
1126                         }
1127                 }
1128         }
1129         else {
1130                 MemorY_ErroR(name, "Additional error in header");
1131                 return("Additional error in header");
1132         }
1133
1134         return(name);
1135 }
1136
1137 size_t MEM_guarded_get_peak_memory(void)
1138 {
1139         size_t _peak_mem;
1140
1141         mem_lock_thread();
1142         _peak_mem = peak_mem;
1143         mem_unlock_thread();
1144
1145         return _peak_mem;
1146 }
1147
1148 void MEM_guarded_reset_peak_memory(void)
1149 {
1150         mem_lock_thread();
1151         peak_mem = mem_in_use;
1152         mem_unlock_thread();
1153 }
1154
1155 size_t MEM_guarded_get_memory_in_use(void)
1156 {
1157         size_t _mem_in_use;
1158
1159         mem_lock_thread();
1160         _mem_in_use = mem_in_use;
1161         mem_unlock_thread();
1162
1163         return _mem_in_use;
1164 }
1165
1166 size_t MEM_guarded_get_mapped_memory_in_use(void)
1167 {
1168         size_t _mmap_in_use;
1169
1170         mem_lock_thread();
1171         _mmap_in_use = mmap_in_use;
1172         mem_unlock_thread();
1173
1174         return _mmap_in_use;
1175 }
1176
1177 unsigned int MEM_guarded_get_memory_blocks_in_use(void)
1178 {
1179         unsigned int _totblock;
1180
1181         mem_lock_thread();
1182         _totblock = totblock;
1183         mem_unlock_thread();
1184
1185         return _totblock;
1186 }
1187
1188 #ifndef NDEBUG
1189 const char *MEM_guarded_name_ptr(void *vmemh)
1190 {
1191         if (vmemh) {
1192                 MemHead *memh = vmemh;
1193                 memh--;
1194                 return memh->name;
1195         }
1196         else {
1197                 return "MEM_guarded_name_ptr(NULL)";
1198         }
1199 }
1200 #endif  /* NDEBUG */