Blender V5.0
mallocn_guarded_impl.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include <stdarg.h>
12#include <stddef.h> /* offsetof */
13#include <stdio.h> /* printf */
14#include <stdlib.h>
15#include <string.h> /* memcpy */
16#include <sys/types.h>
17
18#include <pthread.h>
19
20#include "MEM_guardedalloc.h"
21
22/* Quiet warnings when dealing with allocated data written into the blend file.
23 * This also rounds up and causes warnings which we don't consider bugs in practice. */
24#ifdef WITH_MEM_VALGRIND
25# include "valgrind/memcheck.h"
26#endif
27
28/* to ensure strict conversions */
29#include "../../source/blender/blenlib/BLI_strict_flags.h"
30
31#include "atomic_ops.h"
32#include "mallocn_intern.hh"
34
35using namespace mem_guarded::internal;
36
37/* Only for debugging:
38 * store original buffer's name when doing MEM_dupallocN
39 * helpful to profile issues with non-freed "dup_alloc" buffers,
40 * but this introduces some overhead to memory header and makes
41 * things slower a bit, so better to keep disabled by default
42 */
43// #define DEBUG_MEMDUPLINAME
44
45/* Only for debugging:
46 * lets you count the allocations so as to find the allocator of unfreed memory
47 * in situations where the leak is predictable */
48
49// #define DEBUG_MEMCOUNTER
50
51/* Only for debugging:
52 * Defining DEBUG_BACKTRACE will display a back-trace from where memory block was allocated and
53 * print this trace for all unfreed blocks. This will only work for ASAN enabled builds. This
54 * option will be on by default for MSVC as it currently does not have LSAN which would normally
55 * report these leaks, off by default on all other platforms because it would report the leaks
56 * twice, once here, and once by LSAN.
57 */
58#if defined(_MSC_VER)
59# ifdef WITH_ASAN
60# define DEBUG_BACKTRACE
61# endif
62#else
63/* Un-comment to report back-traces with leaks, uses ASAN when enabled.
64 * NOTE: The default linking options cause the stack traces only to include addresses.
65 * Use `addr2line` to expand into file, line & function identifiers,
66 * see: `tools/utils/addr2line_backtrace.py` convenience utility. */
67// # define DEBUG_BACKTRACE
68#endif
69
70#ifdef DEBUG_BACKTRACE
71# ifdef WITH_ASAN
72/* Rely on address sanitizer. */
73# else
74# if defined(__linux__) || defined(__APPLE__)
75# define DEBUG_BACKTRACE_EXECINFO
76# else
77# error "DEBUG_BACKTRACE: not supported for this platform!"
78# endif
79# endif
80#endif
81
82#ifdef DEBUG_BACKTRACE_EXECINFO
83# define BACKTRACE_SIZE 100
84#endif
85
86#ifdef DEBUG_MEMCOUNTER
87/* set this to the value that isn't being freed */
88# define DEBUG_MEMCOUNTER_ERROR_VAL 0
89static int _mallocn_count = 0;
90
91/* Break-point here. */
92static void memcount_raise(const char *name)
93{
94 fprintf(stderr, "%s: memcount-leak, %d\n", name, _mallocn_count);
95}
96#endif
97
98/* --------------------------------------------------------------------- */
99/* Data definition */
100/* --------------------------------------------------------------------- */
101/* all memory chunks are put in linked lists */
105
106typedef struct localListBase {
107 void *first, *last;
109
110namespace {
111
112/* NOTE(@hos): keep this struct aligned (e.g., IRIX/GCC). */
113typedef struct MemHead {
114 int tag1;
115 size_t len;
116 MemHead *next, *prev;
117 const char *name;
118 const char *nextname;
119 int tag2;
120 uint16_t flag;
121 /* if non-zero aligned allocation was used and alignment is stored here. */
122 short alignment;
123#ifdef DEBUG_MEMCOUNTER
124 int _count;
125#endif
126
127#ifdef DEBUG_MEMDUPLINAME
128 int need_free_name, pad;
129#endif
130
131#ifdef DEBUG_BACKTRACE_EXECINFO
132 void *backtrace[BACKTRACE_SIZE];
133 int backtrace_size;
134#endif
135
136} MemHead;
137static_assert(MEM_MIN_CPP_ALIGNMENT <= alignof(MemHead), "Bad alignment of MemHead");
138static_assert(MEM_MIN_CPP_ALIGNMENT <= sizeof(MemHead), "Bad size of MemHead");
139
140typedef MemHead MemHeadAligned;
141
142} // namespace
143
144/* #MemHead::flag. */
154
155typedef struct MemTail {
156 int tag3, pad;
158
159#ifdef DEBUG_BACKTRACE_EXECINFO
160# include <execinfo.h>
161#endif
162
163/* --------------------------------------------------------------------- */
164/* local functions */
165/* --------------------------------------------------------------------- */
166
167static void addtail(volatile localListBase *listbase, void *vlink);
168static void remlink(volatile localListBase *listbase, void *vlink);
169static void rem_memblock(MemHead *memh);
170static void MemorY_ErroR(const char *block, const char *error);
171static const char *check_memlist(const MemHead *memh);
172
173/* --------------------------------------------------------------------- */
174/* locally used defines */
175/* --------------------------------------------------------------------- */
176
177/* NOTE: this is endianness-sensitive. */
178#define MAKE_ID(a, b, c, d) (int(d) << 24 | int(c) << 16 | (b) << 8 | (a))
179
180#define MEMTAG1 MAKE_ID('M', 'E', 'M', 'O')
181#define MEMTAG2 MAKE_ID('R', 'Y', 'B', 'L')
182#define MEMTAG3 MAKE_ID('O', 'C', 'K', '!')
183#define MEMFREE MAKE_ID('F', 'R', 'E', 'E')
184
185#define MEMNEXT(x) ((MemHead *)(((char *)x) - offsetof(MemHead, next)))
186
187/* --------------------------------------------------------------------- */
188/* vars */
189/* --------------------------------------------------------------------- */
190
191static uint totblock = 0;
192static size_t mem_in_use = 0, peak_mem = 0;
193
194static volatile localListBase _membase;
195static volatile localListBase *membase = &_membase;
196static void (*error_callback)(const char *) = nullptr;
197
198static bool malloc_debug_memset = false;
199
200#ifdef malloc
201# undef malloc
202#endif
203
204#ifdef calloc
205# undef calloc
206#endif
207
208#ifdef free
209# undef free
210#endif
211
212/* --------------------------------------------------------------------- */
213/* implementation */
214/* --------------------------------------------------------------------- */
215
216#ifdef __GNUC__
218#endif
219static void
220print_error(const char *message, va_list str_format_args)
221{
222 char buf[512];
223 vsnprintf(buf, sizeof(buf), message, str_format_args);
224 buf[sizeof(buf) - 1] = '\0';
225
226 if (error_callback) {
227 error_callback(buf);
228 }
229 else {
230 fputs(buf, stderr);
231 }
232}
233
234#ifdef __GNUC__
236#endif
237static void
238print_error(const char *message, ...)
239{
240 va_list str_format_args;
241 va_start(str_format_args, message);
242 print_error(message, str_format_args);
243 va_end(str_format_args);
244}
245
246#ifdef __GNUC__
248#endif
249static void
250report_error_on_address(const void *vmemh, const char *message, ...)
251{
252 va_list str_format_args;
253
254 va_start(str_format_args, message);
255 print_error(message, str_format_args);
256 va_end(str_format_args);
257
258 if (vmemh == nullptr) {
260 return;
261 }
262
263 const MemHead *memh = static_cast<const MemHead *>(vmemh);
264 memh--;
265 size_t len = memh->len;
266
267 const void *address = memh;
268 size_t size = len + sizeof(*memh) + sizeof(MemTail);
269 if (UNLIKELY(memh->alignment > 0)) {
270 const MemHeadAligned *memh_aligned = memh;
271 address = MEMHEAD_REAL_PTR(memh_aligned);
272 size = len + sizeof(*memh_aligned) + MEMHEAD_ALIGN_PADDING(memh_aligned->alignment) +
273 sizeof(MemTail);
274 }
276}
277
278static pthread_mutex_t thread_lock = PTHREAD_MUTEX_INITIALIZER;
279
280static void mem_lock_thread()
281{
282 pthread_mutex_lock(&thread_lock);
283}
284
285static void mem_unlock_thread()
286{
287 pthread_mutex_unlock(&thread_lock);
288}
289
291{
292 const char *err_val = nullptr;
293 const MemHead *listend;
294 /* check_memlist starts from the front, and runs until it finds
295 * the requested chunk. For this test, that's the last one. */
296 listend = static_cast<MemHead *>(membase->last);
297
298 err_val = check_memlist(listend);
299
300 return (err_val == nullptr);
301}
302
303void MEM_guarded_set_error_callback(void (*func)(const char *))
304{
305 error_callback = func;
306}
307
312
313size_t MEM_guarded_allocN_len(const void *vmemh)
314{
315 if (vmemh) {
316 const MemHead *memh = static_cast<const MemHead *>(vmemh);
317
318 memh--;
319 return memh->len;
320 }
321
322 return 0;
323}
324
325void *MEM_guarded_dupallocN(const void *vmemh)
326{
327 void *newp = nullptr;
328
329 if (vmemh) {
330 const MemHead *memh = static_cast<const MemHead *>(vmemh);
331 memh--;
332
333 if ((memh->flag & MEMHEAD_FLAG_FROM_CPP_NEW) != 0) {
335 "Attempt to use C-style MEM_dupallocN on a pointer created with "
336 "CPP-style MEM_new or new\n");
337 }
338
339#ifndef DEBUG_MEMDUPLINAME
340 if (LIKELY(memh->alignment == 0)) {
341 newp = MEM_guarded_mallocN(memh->len, "dupli_alloc");
342 }
343 else {
345 memh->len, size_t(memh->alignment), "dupli_alloc", AllocationType::ALLOC_FREE);
346 }
347
348 if (newp == nullptr) {
349 return nullptr;
350 }
351#else
352 {
353 MemHead *nmemh;
354 const char name_prefix[] = "dupli_alloc ";
355 const size_t name_prefix_len = sizeof(name_prefix) - 1;
356 const size_t name_size = strlen(memh->name) + 1;
357 char *name = malloc(name_prefix_len + name_size);
358 memcpy(name, name_prefix, sizeof(name_prefix));
359 memcpy(name + name_prefix_len, memh->name, name_size);
360
361 if (LIKELY(memh->alignment == 0)) {
362 newp = MEM_guarded_mallocN(memh->len, name);
363 }
364 else {
366 memh->len, size_t(memh->alignment), name, AllocationType::ALLOC_FREE);
367 }
368
369 if (newp == nullptr)
370 return nullptr;
371
372 nmemh = newp;
373 nmemh--;
374
375 nmemh->need_free_name = 1;
376 }
377#endif
378
379 memcpy(newp, vmemh, memh->len);
380 }
381
382 return newp;
383}
384
385void *MEM_guarded_reallocN_id(void *vmemh, size_t len, const char *str)
386{
387 void *newp = nullptr;
388
389 if (vmemh) {
390 MemHead *memh = static_cast<MemHead *>(vmemh);
391 memh--;
392
393 if ((memh->flag & MEMHEAD_FLAG_FROM_CPP_NEW) != 0) {
395 "Attempt to use C-style MEM_reallocN on a pointer created with "
396 "CPP-style MEM_new or new\n");
397 }
398
399 if (LIKELY(memh->alignment == 0)) {
400 newp = MEM_guarded_mallocN(len, memh->name);
401 }
402 else {
404 len, size_t(memh->alignment), memh->name, AllocationType::ALLOC_FREE);
405 }
406
407 if (newp) {
408 if (len < memh->len) {
409 /* shrink */
410 memcpy(newp, vmemh, len);
411 }
412 else {
413 /* grow (or remain same size) */
414 memcpy(newp, vmemh, memh->len);
415 }
416 }
417
419 }
420 else {
421 newp = MEM_guarded_mallocN(len, str);
422 }
423
424 return newp;
425}
426
427void *MEM_guarded_recallocN_id(void *vmemh, size_t len, const char *str)
428{
429 void *newp = nullptr;
430
431 if (vmemh) {
432 MemHead *memh = static_cast<MemHead *>(vmemh);
433 memh--;
434
435 if ((memh->flag & MEMHEAD_FLAG_FROM_CPP_NEW) != 0) {
437 "Attempt to use C-style MEM_recallocN on a pointer created with "
438 "CPP-style MEM_new or new\n");
439 }
440
441 if (LIKELY(memh->alignment == 0)) {
442 newp = MEM_guarded_mallocN(len, memh->name);
443 }
444 else {
446 len, size_t(memh->alignment), memh->name, AllocationType::ALLOC_FREE);
447 }
448
449 if (newp) {
450 if (len < memh->len) {
451 /* shrink */
452 memcpy(newp, vmemh, len);
453 }
454 else {
455 memcpy(newp, vmemh, memh->len);
456
457 if (len > memh->len) {
458 /* grow */
459 /* zero new bytes */
460 memset(((char *)newp) + memh->len, 0, len - memh->len);
461 }
462 }
463 }
464
466 }
467 else {
468 newp = MEM_guarded_callocN(len, str);
469 }
470
471 return newp;
472}
473
474#ifdef DEBUG_BACKTRACE_EXECINFO
475static void make_memhead_backtrace(MemHead *memh)
476{
477 memh->backtrace_size = backtrace(memh->backtrace, BACKTRACE_SIZE);
478}
479
480static void print_memhead_backtrace(MemHead *memh)
481{
482 char **strings;
483 int i;
484
485 strings = backtrace_symbols(memh->backtrace, memh->backtrace_size);
486 for (i = 0; i < memh->backtrace_size; i++) {
487 print_error(" %s\n", strings[i]);
488 }
489
490 free(strings);
491}
492#endif /* DEBUG_BACKTRACE_EXECINFO */
493
494static void make_memhead_header(MemHead *memh,
495 size_t len,
496 const char *str,
497 const AllocationType allocation_type)
498{
499 MemTail *memt;
500
501 memh->tag1 = MEMTAG1;
502 memh->name = str;
503 memh->nextname = nullptr;
504 memh->len = len;
505 memh->flag = (allocation_type == AllocationType::NEW_DELETE ? MEMHEAD_FLAG_FROM_CPP_NEW : 0);
506 memh->alignment = 0;
507 memh->tag2 = MEMTAG2;
508
509#ifdef DEBUG_MEMDUPLINAME
510 memh->need_free_name = 0;
511#endif
512
513#ifdef DEBUG_BACKTRACE_EXECINFO
514 make_memhead_backtrace(memh);
515#endif
516
517 memt = (MemTail *)(((char *)memh) + sizeof(MemHead) + len);
518 memt->tag3 = MEMTAG3;
519
522
524 addtail(membase, &memh->next);
525 if (memh->next) {
526 memh->nextname = MEMNEXT(memh->next)->name;
527 }
530}
531
532void *MEM_guarded_mallocN(size_t len, const char *str)
533{
534 MemHead *memh;
535
536#ifdef WITH_MEM_VALGRIND
537 const size_t len_unaligned = len;
538#endif
540
541 memh = (MemHead *)malloc(len + sizeof(MemHead) + sizeof(MemTail));
542
543 if (LIKELY(memh)) {
545
546 if (LIKELY(len)) {
548 memset(memh + 1, 255, len);
549 }
550#ifdef WITH_MEM_VALGRIND
552 VALGRIND_MAKE_MEM_UNDEFINED(memh + 1, len_unaligned);
553 }
554 else {
555 VALGRIND_MAKE_MEM_DEFINED((const char *)(memh + 1) + len_unaligned, len - len_unaligned);
556 }
557#endif /* WITH_MEM_VALGRIND */
558 }
559
560#ifdef DEBUG_MEMCOUNTER
561 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL) {
562 memcount_raise(__func__);
563 }
564 memh->_count = _mallocn_count++;
565#endif
566 return (++memh);
567 }
568 print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
569 SIZET_ARG(len),
570 str,
571 mem_in_use);
572 return nullptr;
573}
574
575void *MEM_guarded_malloc_arrayN(size_t len, size_t size, const char *str)
576{
577 size_t total_size;
578 if (UNLIKELY(!MEM_size_safe_multiply(len, size, &total_size))) {
580 "Malloc array aborted due to integer overflow: "
581 "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
582 SIZET_ARG(len),
584 str,
585 mem_in_use);
586 abort();
587 return nullptr;
588 }
589
590 return MEM_guarded_mallocN(total_size, str);
591}
592
594 size_t alignment,
595 const char *str,
596 const AllocationType allocation_type)
597{
598 /* Huge alignment values doesn't make sense and they wouldn't fit into 'short' used in the
599 * MemHead. */
600 assert(alignment < 1024);
601
602 /* We only support alignments that are a power of two. */
603 assert(IS_POW2(alignment));
604
605 /* Some OS specific aligned allocators require a certain minimal alignment. */
606 /* And #MEM_guarded_freeN also checks that it is freeing a pointer aligned with `sizeof(void *)`.
607 */
608 if (alignment < ALIGNED_MALLOC_MINIMUM_ALIGNMENT) {
610 }
611
612 /* It's possible that MemHead's size is not properly aligned,
613 * do extra padding to deal with this.
614 *
615 * We only support small alignments which fits into short in
616 * order to save some bits in MemHead structure.
617 */
618 size_t extra_padding = MEMHEAD_ALIGN_PADDING(alignment);
619
620#ifdef WITH_MEM_VALGRIND
621 const size_t len_unaligned = len;
622#endif
624
625 MemHead *memh = (MemHead *)aligned_malloc(
626 len + extra_padding + sizeof(MemHead) + sizeof(MemTail), alignment);
627
628 if (LIKELY(memh)) {
629 /* We keep padding in the beginning of MemHead,
630 * this way it's always possible to get MemHead
631 * from the data pointer.
632 */
633 memh = (MemHead *)((char *)memh + extra_padding);
634
635 make_memhead_header(memh, len, str, allocation_type);
636 memh->alignment = short(alignment);
637 if (LIKELY(len)) {
639 memset(memh + 1, 255, len);
640 }
641#ifdef WITH_MEM_VALGRIND
643 VALGRIND_MAKE_MEM_UNDEFINED(memh + 1, len_unaligned);
644 }
645 else {
646 VALGRIND_MAKE_MEM_DEFINED((const char *)(memh + 1) + len_unaligned, len - len_unaligned);
647 }
648#endif /* WITH_MEM_VALGRIND */
649 }
650
651#ifdef DEBUG_MEMCOUNTER
652 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL) {
653 memcount_raise(__func__);
654 }
655 memh->_count = _mallocn_count++;
656#endif
657 return (++memh);
658 }
659 print_error("aligned_malloc returns null: len=" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
660 SIZET_ARG(len),
661 str,
662 mem_in_use);
663 return nullptr;
664}
665
666void *MEM_guarded_callocN(size_t len, const char *str)
667{
668 MemHead *memh;
669
671
672 memh = (MemHead *)calloc(len + sizeof(MemHead) + sizeof(MemTail), 1);
673
674 if (memh) {
676#ifdef DEBUG_MEMCOUNTER
677 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL) {
678 memcount_raise(__func__);
679 }
680 memh->_count = _mallocn_count++;
681#endif
682 return (++memh);
683 }
684 print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
685 SIZET_ARG(len),
686 str,
687 mem_in_use);
688 return nullptr;
689}
690
691void *MEM_guarded_calloc_arrayN(size_t len, size_t size, const char *str)
692{
693 size_t total_size;
694 if (UNLIKELY(!MEM_size_safe_multiply(len, size, &total_size))) {
696 "Calloc array aborted due to integer overflow: "
697 "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
698 SIZET_ARG(len),
700 str,
701 mem_in_use);
702 abort();
703 return nullptr;
704 }
705
706 return MEM_guarded_callocN(total_size, str);
707}
708
709static void *mem_guarded_malloc_arrayN_aligned(const size_t len,
710 const size_t size,
711 const size_t alignment,
712 const char *str,
713 size_t &r_bytes_num)
714{
715 if (UNLIKELY(!MEM_size_safe_multiply(len, size, &r_bytes_num))) {
717 "Calloc array aborted due to integer overflow: "
718 "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
719 SIZET_ARG(len),
721 str,
722 mem_in_use);
723 abort();
724 return nullptr;
725 }
726 if (alignment <= MEM_MIN_CPP_ALIGNMENT) {
727 return mem_mallocN(r_bytes_num, str);
728 }
729 return MEM_mallocN_aligned(r_bytes_num, alignment, str);
730}
731
733 const size_t size,
734 const size_t alignment,
735 const char *str)
736{
737 size_t bytes_num;
738 return mem_guarded_malloc_arrayN_aligned(len, size, alignment, str, bytes_num);
739}
740
742 const size_t size,
743 const size_t alignment,
744 const char *str)
745{
746 size_t bytes_num;
747 /* There is no lower level #calloc with an alignment parameter, so we have to fall back to using
748 * #memset unfortunately. */
749 void *ptr = mem_guarded_malloc_arrayN_aligned(len, size, alignment, str, bytes_num);
750 if (!ptr) {
751 return nullptr;
752 }
753 memset(ptr, 0, bytes_num);
754 return ptr;
755}
756
757/* Memory statistics print */
758typedef struct MemPrintBlock {
759 const char *name;
760 uintptr_t len;
761 int items;
763
764static int compare_name(const void *p1, const void *p2)
765{
766 const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
767 const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
768
769 return strcmp(pb1->name, pb2->name);
770}
771
772static int compare_len(const void *p1, const void *p2)
773{
774 const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
775 const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
776
777 if (pb1->len < pb2->len) {
778 return 1;
779 }
780 if (pb1->len == pb2->len) {
781 return 0;
782 }
783
784 return -1;
785}
786
788{
789 MemHead *membl;
790 MemPrintBlock *pb, *printblock;
791 uint totpb, a, b;
792 size_t mem_in_use_slop = 0;
793
795
796 if (totblock != 0) {
797 /* put memory blocks into array */
798 printblock = static_cast<MemPrintBlock *>(malloc(sizeof(MemPrintBlock) * totblock));
799
800 if (UNLIKELY(!printblock)) {
802 print_error("malloc returned null while generating stats");
803 return;
804 }
805 }
806 else {
807 printblock = nullptr;
808 }
809
810 pb = printblock;
811 totpb = 0;
812
813 membl = static_cast<MemHead *>(membase->first);
814 if (membl) {
815 membl = MEMNEXT(membl);
816 }
817
818 while (membl && pb) {
819 pb->name = membl->name;
820 pb->len = membl->len;
821 pb->items = 1;
822
823 totpb++;
824 pb++;
825
826#ifdef USE_MALLOC_USABLE_SIZE
827 if (membl->alignment == 0) {
828 mem_in_use_slop += (sizeof(MemHead) + sizeof(MemTail) + malloc_usable_size((void *)membl)) -
829 membl->len;
830 }
831#endif
832
833 if (membl->next) {
834 membl = MEMNEXT(membl->next);
835 }
836 else {
837 break;
838 }
839 }
840
841 /* sort by name and add together blocks with the same name */
842 if (totpb > 1) {
843 qsort(printblock, totpb, sizeof(MemPrintBlock), compare_name);
844 }
845
846 for (a = 0, b = 0; a < totpb; a++) {
847 if (a == b) {
848 continue;
849 }
850 if (strcmp(printblock[a].name, printblock[b].name) == 0) {
851 printblock[b].len += printblock[a].len;
852 printblock[b].items++;
853 }
854 else {
855 b++;
856 memcpy(&printblock[b], &printblock[a], sizeof(MemPrintBlock));
857 }
858 }
859 totpb = b + 1;
860
861 /* sort by length and print */
862 if (totpb > 1) {
863 qsort(printblock, totpb, sizeof(MemPrintBlock), compare_len);
864 }
865
866 printf("\ntotal memory len: %.3f MB\n", double(mem_in_use) / double(1024 * 1024));
867 printf("peak memory len: %.3f MB\n", double(peak_mem) / double(1024 * 1024));
868 printf("slop memory len: %.3f MB\n", double(mem_in_use_slop) / double(1024 * 1024));
869 printf(" ITEMS TOTAL-MiB AVERAGE-KiB TYPE\n");
870 for (a = 0, pb = printblock; a < totpb; a++, pb++) {
871 printf("%6d (%8.3f %8.3f) %s\n",
872 pb->items,
873 double(pb->len) / double(1024 * 1024),
874 double(pb->len) / 1024.0 / double(pb->items),
875 pb->name);
876 }
877
878 if (printblock != nullptr) {
879 free(printblock);
880 }
881
883
884#ifdef HAVE_MALLOC_STATS
885 printf("System Statistics:\n");
886 malloc_stats();
887#endif
888}
889
891 "mb_userinfo = {}\n"
892 "totmem = 0\n"
893 "for mb_item in membase:\n"
894 " mb_item_user_size = mb_userinfo.setdefault(mb_item['name'], [0,0])\n"
895 " mb_item_user_size[0] += 1 # Add a user\n"
896 " mb_item_user_size[1] += mb_item['len'] # Increment the size\n"
897 " totmem += mb_item['len']\n"
898 "print('(membase) items:', len(membase), '| unique-names:',\n"
899 " len(mb_userinfo), '| total-mem:', totmem)\n"
900 "mb_userinfo_sort = list(mb_userinfo.items())\n"
901 "for sort_name, sort_func in (('size', lambda a: -a[1][1]),\n"
902 " ('users', lambda a: -a[1][0]),\n"
903 " ('name', lambda a: a[0])):\n"
904 " print('\\nSorting by:', sort_name)\n"
905 " mb_userinfo_sort.sort(key = sort_func)\n"
906 " for item in mb_userinfo_sort:\n"
907 " print('name:%%s, users:%%i, len:%%i' %%\n"
908 " (item[0], item[1][0], item[1][1]))\n";
909
910/* Prints in python syntax for easy */
912{
913 MemHead *membl;
914
916
917 membl = static_cast<MemHead *>(membase->first);
918 if (membl) {
919 membl = MEMNEXT(membl);
920 }
921
922 if (pydict) {
923 print_error("# membase_debug.py\n");
924 print_error("membase = [\n");
925 }
926 while (membl) {
927 if (pydict) {
928 print_error(" {'len':" SIZET_FORMAT
929 ", "
930 "'name':'''%s''', "
931 "'pointer':'%p'},\n",
932 SIZET_ARG(membl->len),
933 membl->name,
934 (void *)(membl + 1));
935 }
936 else {
937#ifdef DEBUG_MEMCOUNTER
938 print_error("%s len: " SIZET_FORMAT " %p, count: %d\n",
939 membl->name,
940 SIZET_ARG(membl->len),
941 membl + 1,
942 membl->_count);
943#else
944 print_error("%s len: " SIZET_FORMAT " %p\n",
945 membl->name,
946 SIZET_ARG(membl->len),
947 (void *)(membl + 1));
948#endif
949
950#ifdef DEBUG_BACKTRACE_EXECINFO
951 print_memhead_backtrace(membl);
952#elif defined(DEBUG_BACKTRACE) && defined(WITH_ASAN)
953 __asan_describe_address(membl);
954#endif
955 }
956 if (membl->next) {
957 membl = MEMNEXT(membl->next);
958 }
959 else {
960 break;
961 }
962 }
963 if (pydict) {
964 print_error("]\n\n");
966 }
967
969}
970
971void MEM_guarded_callbackmemlist(void (*func)(void *))
972{
973 MemHead *membl;
974
976
977 membl = static_cast<MemHead *>(membase->first);
978 if (membl) {
979 membl = MEMNEXT(membl);
980 }
981
982 while (membl) {
983 func(membl + 1);
984 if (membl->next) {
985 membl = MEMNEXT(membl->next);
986 }
987 else {
988 break;
989 }
990 }
991
993}
994
995#if 0
996short MEM_guarded_testN(void *vmemh)
997{
998 MemHead *membl;
999
1001
1002 membl = membase->first;
1003 if (membl) {
1004 membl = MEMNEXT(membl);
1005 }
1006
1007 while (membl) {
1008 if (vmemh == membl + 1) {
1010 return 1;
1011 }
1012
1013 if (membl->next)
1014 membl = MEMNEXT(membl->next);
1015 else
1016 break;
1017 }
1018
1020
1021 print_error("Memoryblock %p: pointer not in memlist\n", vmemh);
1022 return 0;
1023}
1024#endif
1025
1035{
1036 membase->first = membase->last = nullptr;
1037}
1038
1039void MEM_guarded_freeN(void *vmemh, const AllocationType allocation_type)
1040{
1041 MemTail *memt;
1042 MemHead *memh = static_cast<MemHead *>(vmemh);
1043 const char *name;
1044
1045 if (memh == nullptr) {
1046 MemorY_ErroR("free", "attempt to free nullptr pointer");
1047 // print_error(err_stream, "%d\n", (memh+4000)->tag1);
1048 return;
1049 }
1050
1051 if (sizeof(intptr_t) == 8) {
1052 if (intptr_t(memh) & 0x7) {
1053 MemorY_ErroR("free", "attempt to free illegal pointer");
1054 return;
1055 }
1056 }
1057 else {
1058 if (intptr_t(memh) & 0x3) {
1059 MemorY_ErroR("free", "attempt to free illegal pointer");
1060 return;
1061 }
1062 }
1063
1064 memh--;
1065
1066 if (allocation_type != AllocationType::NEW_DELETE &&
1067 (memh->flag & MEMHEAD_FLAG_FROM_CPP_NEW) != 0)
1068 {
1070 vmemh,
1071 "Attempt to use C-style MEM_freeN on a pointer created with CPP-style MEM_new or new\n");
1072 }
1073
1074 if (memh->tag1 == MEMFREE && memh->tag2 == MEMFREE) {
1075 MemorY_ErroR(memh->name, "double free");
1076 return;
1077 }
1078
1079 if ((memh->tag1 == MEMTAG1) && (memh->tag2 == MEMTAG2) && ((memh->len & 0x3) == 0)) {
1080 memt = (MemTail *)(((char *)memh) + sizeof(MemHead) + memh->len);
1081 if (memt->tag3 == MEMTAG3) {
1082
1085 }
1086
1087 memh->tag1 = MEMFREE;
1088 memh->tag2 = MEMFREE;
1089 memt->tag3 = MEMFREE;
1090 /* after tags !!! */
1091 rem_memblock(memh);
1092
1093 return;
1094 }
1095 MemorY_ErroR(memh->name, "end corrupt");
1096 name = check_memlist(memh);
1097 if (name != nullptr) {
1098 if (name != memh->name) {
1099 MemorY_ErroR(name, "is also corrupt");
1100 }
1101 }
1102 }
1103 else {
1105 name = check_memlist(memh);
1107 if (name == nullptr) {
1108 MemorY_ErroR("free", "pointer not in memlist");
1109 }
1110 else {
1111 MemorY_ErroR(name, "error in header");
1112 }
1113 }
1114
1115 totblock--;
1116 /* here a DUMP should happen */
1117}
1118
1119/* --------------------------------------------------------------------- */
1120/* local functions */
1121/* --------------------------------------------------------------------- */
1122
1123static void addtail(volatile localListBase *listbase, void *vlink)
1124{
1125 localLink *link = static_cast<localLink *>(vlink);
1126
1127 /* for a generic API error checks here is fine but
1128 * the limited use here they will never be nullptr */
1129#if 0
1130 if (link == nullptr) {
1131 return;
1132 }
1133 if (listbase == nullptr) {
1134 return;
1135 }
1136#endif
1137
1138 link->next = nullptr;
1139 link->prev = static_cast<localLink *>(listbase->last);
1140
1141 if (listbase->last) {
1142 ((localLink *)listbase->last)->next = link;
1143 }
1144 if (listbase->first == nullptr) {
1145 listbase->first = link;
1146 }
1147 listbase->last = link;
1148}
1149
1150static void remlink(volatile localListBase *listbase, void *vlink)
1151{
1152 localLink *link = static_cast<localLink *>(vlink);
1153
1154 /* for a generic API error checks here is fine but
1155 * the limited use here they will never be nullptr */
1156#if 0
1157 if (link == nullptr) {
1158 return;
1159 }
1160 if (listbase == nullptr) {
1161 return;
1162 }
1163#endif
1164
1165 if (link->next) {
1166 link->next->prev = link->prev;
1167 }
1168 if (link->prev) {
1169 link->prev->next = link->next;
1170 }
1171
1172 if (listbase->last == link) {
1173 listbase->last = link->prev;
1174 }
1175 if (listbase->first == link) {
1176 listbase->first = link->next;
1177 }
1178}
1179
1180static void rem_memblock(MemHead *memh)
1181{
1183 remlink(membase, &memh->next);
1184 if (memh->prev) {
1185 if (memh->next) {
1186 MEMNEXT(memh->prev)->nextname = MEMNEXT(memh->next)->name;
1187 }
1188 else {
1189 MEMNEXT(memh->prev)->nextname = nullptr;
1190 }
1191 }
1193
1196
1197#ifdef DEBUG_MEMDUPLINAME
1198 if (memh->need_free_name) {
1199 free((char *)memh->name);
1200 }
1201#endif
1202
1203 if (UNLIKELY(malloc_debug_memset && memh->len)) {
1204 memset(memh + 1, 255, memh->len);
1205 }
1206 if (LIKELY(memh->alignment == 0)) {
1207 free(memh);
1208 }
1209 else {
1211 }
1212}
1213
1214static void MemorY_ErroR(const char *block, const char *error)
1215{
1216 print_error("Memoryblock %s: %s\n", block, error);
1217
1218#ifdef WITH_ASSERT_ABORT
1219 abort();
1220#endif
1221}
1222
1223static const char *check_memlist(const MemHead *memh)
1224{
1225 MemHead *forw, *back, *forwok, *backok;
1226 const char *name;
1227
1228 forw = static_cast<MemHead *>(membase->first);
1229 if (forw) {
1230 forw = MEMNEXT(forw);
1231 }
1232 forwok = nullptr;
1233 while (forw) {
1234 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) {
1235 break;
1236 }
1237 forwok = forw;
1238 if (forw->next) {
1239 forw = MEMNEXT(forw->next);
1240 }
1241 else {
1242 forw = nullptr;
1243 }
1244 }
1245
1246 back = (MemHead *)membase->last;
1247 if (back) {
1248 back = MEMNEXT(back);
1249 }
1250 backok = nullptr;
1251 while (back) {
1252 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) {
1253 break;
1254 }
1255 backok = back;
1256 if (back->prev) {
1257 back = MEMNEXT(back->prev);
1258 }
1259 else {
1260 back = nullptr;
1261 }
1262 }
1263
1264 if (forw != back) {
1265 return ("MORE THAN 1 MEMORYBLOCK CORRUPT");
1266 }
1267
1268 if (forw == nullptr && back == nullptr) {
1269 /* no wrong headers found then but in search of memblock */
1270
1271 forw = static_cast<MemHead *>(membase->first);
1272 if (forw) {
1273 forw = MEMNEXT(forw);
1274 }
1275 forwok = nullptr;
1276 while (forw) {
1277 if (forw == memh) {
1278 break;
1279 }
1280 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) {
1281 break;
1282 }
1283 forwok = forw;
1284 if (forw->next) {
1285 forw = MEMNEXT(forw->next);
1286 }
1287 else {
1288 forw = nullptr;
1289 }
1290 }
1291 if (forw == nullptr) {
1292 return nullptr;
1293 }
1294
1295 back = (MemHead *)membase->last;
1296 if (back) {
1297 back = MEMNEXT(back);
1298 }
1299 backok = nullptr;
1300 while (back) {
1301 if (back == memh) {
1302 break;
1303 }
1304 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) {
1305 break;
1306 }
1307 backok = back;
1308 if (back->prev) {
1309 back = MEMNEXT(back->prev);
1310 }
1311 else {
1312 back = nullptr;
1313 }
1314 }
1315 }
1316
1317 if (forwok) {
1318 name = forwok->nextname;
1319 }
1320 else {
1321 name = "No name found";
1322 }
1323
1324 if (forw == memh) {
1325 /* to be sure but this block is removed from the list */
1326 if (forwok) {
1327 if (backok) {
1328 forwok->next = (MemHead *)&backok->next;
1329 backok->prev = (MemHead *)&forwok->next;
1330 forwok->nextname = backok->name;
1331 }
1332 else {
1333 forwok->next = nullptr;
1334 membase->last = (localLink *)&forwok->next;
1335 }
1336 }
1337 else {
1338 if (backok) {
1339 backok->prev = nullptr;
1340 membase->first = &backok->next;
1341 }
1342 else {
1343 membase->first = membase->last = nullptr;
1344 }
1345 }
1346 }
1347 else {
1348 MemorY_ErroR(name, "Additional error in header");
1349 return ("Additional error in header");
1350 }
1351
1352 return name;
1353}
1354
1356{
1357 size_t _peak_mem;
1358
1360 _peak_mem = peak_mem;
1362
1363 return _peak_mem;
1364}
1365
1372
1374{
1375 size_t _mem_in_use;
1376
1378 _mem_in_use = mem_in_use;
1380
1381 return _mem_in_use;
1382}
1383
1385{
1386 uint _totblock;
1387
1389 _totblock = totblock;
1391
1392 return _totblock;
1393}
1394
1395#ifndef NDEBUG
1396const char *MEM_guarded_name_ptr(void *vmemh)
1397{
1398 if (vmemh) {
1399 MemHead *memh = static_cast<MemHead *>(vmemh);
1400 memh--;
1401 return memh->name;
1402 }
1403
1404 return "MEM_guarded_name_ptr(nullptr)";
1405}
1406
1407void MEM_guarded_name_ptr_set(void *vmemh, const char *str)
1408{
1409 if (!vmemh) {
1410 return;
1411 }
1412
1413 MemHead *memh = static_cast<MemHead *>(vmemh);
1414 memh--;
1415 memh->name = str;
1416 if (memh->prev) {
1417 MEMNEXT(memh->prev)->nextname = str;
1418 }
1419}
1420#endif /* !NDEBUG */
void BLI_kdtree_nd_ free(KDTree *tree)
unsigned int uint
#define UNLIKELY(x)
#define LIKELY(x)
Read Guarded memory(de)allocation.
Provides wrapper around system-specific atomic primitives, and some extensions (faked-atomic operatio...
ATOMIC_INLINE size_t atomic_add_and_fetch_z(size_t *p, size_t x)
ATOMIC_INLINE size_t atomic_sub_and_fetch_z(size_t *p, size_t x)
ATOMIC_INLINE unsigned int atomic_add_and_fetch_u(unsigned int *p, unsigned int x)
ATOMIC_INLINE unsigned int atomic_sub_and_fetch_u(unsigned int *p, unsigned int x)
int pad[32 - sizeof(int)]
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static __attribute__((constructor)) void cpu_check()
Definition cpu_check.cc:94
#define str(s)
static int compare_name(void *user_data, const void *a1, const void *a2)
#define assert(assertion)
#define printf(...)
bool leak_detector_has_run
char free_after_leak_detection_message[]
format
void aligned_free(void *ptr)
Definition mallocn.cc:104
void * MEM_mallocN_aligned(size_t len, size_t alignment, const char *str)
Definition mallocn.cc:138
void * aligned_malloc(size_t size, size_t alignment)
Definition mallocn.cc:82
static pthread_mutex_t thread_lock
void * MEM_guarded_mallocN_aligned(size_t len, size_t alignment, const char *str, const AllocationType allocation_type)
static void MEM_guarded_printmemlist_internal(int pydict)
void * MEM_guarded_calloc_arrayN_aligned(const size_t len, const size_t size, const size_t alignment, const char *str)
static void rem_memblock(MemHead *memh)
static const char mem_printmemlist_pydict_script[]
size_t MEM_guarded_allocN_len(const void *vmemh)
static size_t mem_in_use
static int compare_len(const void *p1, const void *p2)
void * MEM_guarded_mallocN(size_t len, const char *str)
size_t MEM_guarded_get_peak_memory()
static int compare_name(const void *p1, const void *p2)
static void remlink(volatile localListBase *listbase, void *vlink)
static size_t peak_mem
void * MEM_guarded_calloc_arrayN(size_t len, size_t size, const char *str)
static void make_memhead_header(MemHead *memh, size_t len, const char *str, const AllocationType allocation_type)
void MEM_guarded_reset_peak_memory()
void MEM_guarded_freeN(void *vmemh, const AllocationType allocation_type)
void MEM_guarded_set_error_callback(void(*func)(const char *))
#define MEMNEXT(x)
bool MEM_guarded_consistency_check()
static bool malloc_debug_memset
#define MEMTAG2
void MEM_guarded_printmemlist_pydict()
void MEM_guarded_callbackmemlist(void(*func)(void *))
void MEM_guarded_printmemlist()
static void(* error_callback)(const char *)
uint MEM_guarded_get_memory_blocks_in_use()
static void MemorY_ErroR(const char *block, const char *error)
static void mem_unlock_thread()
void * MEM_guarded_callocN(size_t len, const char *str)
void * MEM_guarded_reallocN_id(void *vmemh, size_t len, const char *str)
static void print_error(const char *message, va_list str_format_args)
static void report_error_on_address(const void *vmemh, const char *message,...)
#define MEMFREE
void MEM_guarded_printmemlist_stats()
static const char * check_memlist(const MemHead *memh)
static void * mem_guarded_malloc_arrayN_aligned(const size_t len, const size_t size, const size_t alignment, const char *str, size_t &r_bytes_num)
void * MEM_guarded_malloc_arrayN(size_t len, size_t size, const char *str)
void * MEM_guarded_dupallocN(const void *vmemh)
size_t MEM_guarded_get_memory_in_use()
void * MEM_guarded_recallocN_id(void *vmemh, size_t len, const char *str)
#define MEMTAG3
static void addtail(volatile localListBase *listbase, void *vlink)
static uint totblock
static volatile localListBase _membase
void MEM_guarded_name_ptr_set(void *vmemh, const char *str)
@ MEMHEAD_FLAG_FROM_CPP_NEW
static volatile localListBase * membase
void MEM_guarded_set_memory_debug()
const char * MEM_guarded_name_ptr(void *vmemh)
void mem_guarded_clearmemlist()
static void mem_lock_thread()
void * MEM_guarded_malloc_arrayN_aligned(const size_t len, const size_t size, const size_t alignment, const char *str)
#define MEMTAG1
MEM_INLINE bool MEM_size_safe_multiply(size_t a, size_t b, size_t *result)
MEM_INLINE void MEM_trigger_error_on_memory_block(const void *, const size_t)
#define MEMHEAD_REAL_PTR(memh)
#define IS_POW2(a)
#define MEMHEAD_ALIGN_PADDING(alignment)
#define SIZET_ARG(a)
#define SIZET_ALIGN_4(len)
#define SIZET_FORMAT
#define ALIGNED_MALLOC_MINIMUM_ALIGNMENT
static void error(const char *str)
void *(* mem_mallocN)(size_t len, const char *str) ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2)
Definition mallocn.cc:46
const char * name
i
Definition text_draw.cc:230
uint len
PointerRNA * ptr
Definition wm_files.cc:4238