Blender V4.5
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#ifdef __BIG_ENDIAN__
178# define MAKE_ID(a, b, c, d) (int(a) << 24 | int(b) << 16 | (c) << 8 | (d))
179#else
180# define MAKE_ID(a, b, c, d) (int(d) << 24 | int(c) << 16 | (b) << 8 | (a))
181#endif
182
183#define MEMTAG1 MAKE_ID('M', 'E', 'M', 'O')
184#define MEMTAG2 MAKE_ID('R', 'Y', 'B', 'L')
185#define MEMTAG3 MAKE_ID('O', 'C', 'K', '!')
186#define MEMFREE MAKE_ID('F', 'R', 'E', 'E')
187
188#define MEMNEXT(x) ((MemHead *)(((char *)x) - offsetof(MemHead, next)))
189
190/* --------------------------------------------------------------------- */
191/* vars */
192/* --------------------------------------------------------------------- */
193
194static uint totblock = 0;
195static size_t mem_in_use = 0, peak_mem = 0;
196
197static volatile localListBase _membase;
198static volatile localListBase *membase = &_membase;
199static void (*error_callback)(const char *) = nullptr;
200
201static bool malloc_debug_memset = false;
202
203#ifdef malloc
204# undef malloc
205#endif
206
207#ifdef calloc
208# undef calloc
209#endif
210
211#ifdef free
212# undef free
213#endif
214
215/* --------------------------------------------------------------------- */
216/* implementation */
217/* --------------------------------------------------------------------- */
218
219#ifdef __GNUC__
221#endif
222static void
223print_error(const char *message, va_list str_format_args)
224{
225 char buf[512];
226 vsnprintf(buf, sizeof(buf), message, str_format_args);
227 buf[sizeof(buf) - 1] = '\0';
228
229 if (error_callback) {
230 error_callback(buf);
231 }
232 else {
233 fputs(buf, stderr);
234 }
235}
236
237#ifdef __GNUC__
239#endif
240static void
241print_error(const char *message, ...)
242{
243 va_list str_format_args;
244 va_start(str_format_args, message);
245 print_error(message, str_format_args);
246 va_end(str_format_args);
247}
248
249#ifdef __GNUC__
251#endif
252static void
253report_error_on_address(const void *vmemh, const char *message, ...)
254{
255 va_list str_format_args;
256
257 va_start(str_format_args, message);
258 print_error(message, str_format_args);
259 va_end(str_format_args);
260
261 if (vmemh == nullptr) {
263 return;
264 }
265
266 const MemHead *memh = static_cast<const MemHead *>(vmemh);
267 memh--;
268 size_t len = memh->len;
269
270 const void *address = memh;
271 size_t size = len + sizeof(*memh) + sizeof(MemTail);
272 if (UNLIKELY(memh->alignment > 0)) {
273 const MemHeadAligned *memh_aligned = memh;
274 address = MEMHEAD_REAL_PTR(memh_aligned);
275 size = len + sizeof(*memh_aligned) + MEMHEAD_ALIGN_PADDING(memh_aligned->alignment) +
276 sizeof(MemTail);
277 }
279}
280
281static pthread_mutex_t thread_lock = PTHREAD_MUTEX_INITIALIZER;
282
283static void mem_lock_thread()
284{
285 pthread_mutex_lock(&thread_lock);
286}
287
288static void mem_unlock_thread()
289{
290 pthread_mutex_unlock(&thread_lock);
291}
292
294{
295 const char *err_val = nullptr;
296 const MemHead *listend;
297 /* check_memlist starts from the front, and runs until it finds
298 * the requested chunk. For this test, that's the last one. */
299 listend = static_cast<MemHead *>(membase->last);
300
301 err_val = check_memlist(listend);
302
303 return (err_val == nullptr);
304}
305
306void MEM_guarded_set_error_callback(void (*func)(const char *))
307{
308 error_callback = func;
309}
310
315
316size_t MEM_guarded_allocN_len(const void *vmemh)
317{
318 if (vmemh) {
319 const MemHead *memh = static_cast<const MemHead *>(vmemh);
320
321 memh--;
322 return memh->len;
323 }
324
325 return 0;
326}
327
328void *MEM_guarded_dupallocN(const void *vmemh)
329{
330 void *newp = nullptr;
331
332 if (vmemh) {
333 const MemHead *memh = static_cast<const MemHead *>(vmemh);
334 memh--;
335
336 if ((memh->flag & MEMHEAD_FLAG_FROM_CPP_NEW) != 0) {
338 "Attempt to use C-style MEM_dupallocN on a pointer created with "
339 "CPP-style MEM_new or new\n");
340 }
341
342#ifndef DEBUG_MEMDUPLINAME
343 if (LIKELY(memh->alignment == 0)) {
344 newp = MEM_guarded_mallocN(memh->len, "dupli_alloc");
345 }
346 else {
348 memh->len, size_t(memh->alignment), "dupli_alloc", AllocationType::ALLOC_FREE);
349 }
350
351 if (newp == nullptr) {
352 return nullptr;
353 }
354#else
355 {
356 MemHead *nmemh;
357 const char name_prefix[] = "dupli_alloc ";
358 const size_t name_prefix_len = sizeof(name_prefix) - 1;
359 const size_t name_size = strlen(memh->name) + 1;
360 char *name = malloc(name_prefix_len + name_size);
361 memcpy(name, name_prefix, sizeof(name_prefix));
362 memcpy(name + name_prefix_len, memh->name, name_size);
363
364 if (LIKELY(memh->alignment == 0)) {
365 newp = MEM_guarded_mallocN(memh->len, name);
366 }
367 else {
369 memh->len, (size_t)memh->alignment, name, AllocationType::ALLOC_FREE);
370 }
371
372 if (newp == nullptr)
373 return nullptr;
374
375 nmemh = newp;
376 nmemh--;
377
378 nmemh->need_free_name = 1;
379 }
380#endif
381
382 memcpy(newp, vmemh, memh->len);
383 }
384
385 return newp;
386}
387
388void *MEM_guarded_reallocN_id(void *vmemh, size_t len, const char *str)
389{
390 void *newp = nullptr;
391
392 if (vmemh) {
393 MemHead *memh = static_cast<MemHead *>(vmemh);
394 memh--;
395
396 if ((memh->flag & MEMHEAD_FLAG_FROM_CPP_NEW) != 0) {
398 "Attempt to use C-style MEM_reallocN on a pointer created with "
399 "CPP-style MEM_new or new\n");
400 }
401
402 if (LIKELY(memh->alignment == 0)) {
403 newp = MEM_guarded_mallocN(len, memh->name);
404 }
405 else {
407 len, size_t(memh->alignment), memh->name, AllocationType::ALLOC_FREE);
408 }
409
410 if (newp) {
411 if (len < memh->len) {
412 /* shrink */
413 memcpy(newp, vmemh, len);
414 }
415 else {
416 /* grow (or remain same size) */
417 memcpy(newp, vmemh, memh->len);
418 }
419 }
420
422 }
423 else {
424 newp = MEM_guarded_mallocN(len, str);
425 }
426
427 return newp;
428}
429
430void *MEM_guarded_recallocN_id(void *vmemh, size_t len, const char *str)
431{
432 void *newp = nullptr;
433
434 if (vmemh) {
435 MemHead *memh = static_cast<MemHead *>(vmemh);
436 memh--;
437
438 if ((memh->flag & MEMHEAD_FLAG_FROM_CPP_NEW) != 0) {
440 "Attempt to use C-style MEM_recallocN on a pointer created with "
441 "CPP-style MEM_new or new\n");
442 }
443
444 if (LIKELY(memh->alignment == 0)) {
445 newp = MEM_guarded_mallocN(len, memh->name);
446 }
447 else {
449 len, size_t(memh->alignment), memh->name, AllocationType::ALLOC_FREE);
450 }
451
452 if (newp) {
453 if (len < memh->len) {
454 /* shrink */
455 memcpy(newp, vmemh, len);
456 }
457 else {
458 memcpy(newp, vmemh, memh->len);
459
460 if (len > memh->len) {
461 /* grow */
462 /* zero new bytes */
463 memset(((char *)newp) + memh->len, 0, len - memh->len);
464 }
465 }
466 }
467
469 }
470 else {
471 newp = MEM_guarded_callocN(len, str);
472 }
473
474 return newp;
475}
476
477#ifdef DEBUG_BACKTRACE_EXECINFO
478static void make_memhead_backtrace(MemHead *memh)
479{
480 memh->backtrace_size = backtrace(memh->backtrace, BACKTRACE_SIZE);
481}
482
483static void print_memhead_backtrace(MemHead *memh)
484{
485 char **strings;
486 int i;
487
488 strings = backtrace_symbols(memh->backtrace, memh->backtrace_size);
489 for (i = 0; i < memh->backtrace_size; i++) {
490 print_error(" %s\n", strings[i]);
491 }
492
493 free(strings);
494}
495#endif /* DEBUG_BACKTRACE_EXECINFO */
496
497static void make_memhead_header(MemHead *memh,
498 size_t len,
499 const char *str,
500 const AllocationType allocation_type)
501{
502 MemTail *memt;
503
504 memh->tag1 = MEMTAG1;
505 memh->name = str;
506 memh->nextname = nullptr;
507 memh->len = len;
508 memh->flag = (allocation_type == AllocationType::NEW_DELETE ? MEMHEAD_FLAG_FROM_CPP_NEW : 0);
509 memh->alignment = 0;
510 memh->tag2 = MEMTAG2;
511
512#ifdef DEBUG_MEMDUPLINAME
513 memh->need_free_name = 0;
514#endif
515
516#ifdef DEBUG_BACKTRACE_EXECINFO
517 make_memhead_backtrace(memh);
518#endif
519
520 memt = (MemTail *)(((char *)memh) + sizeof(MemHead) + len);
521 memt->tag3 = MEMTAG3;
522
525
527 addtail(membase, &memh->next);
528 if (memh->next) {
529 memh->nextname = MEMNEXT(memh->next)->name;
530 }
533}
534
535void *MEM_guarded_mallocN(size_t len, const char *str)
536{
537 MemHead *memh;
538
539#ifdef WITH_MEM_VALGRIND
540 const size_t len_unaligned = len;
541#endif
543
544 memh = (MemHead *)malloc(len + sizeof(MemHead) + sizeof(MemTail));
545
546 if (LIKELY(memh)) {
548
549 if (LIKELY(len)) {
551 memset(memh + 1, 255, len);
552 }
553#ifdef WITH_MEM_VALGRIND
555 VALGRIND_MAKE_MEM_UNDEFINED(memh + 1, len_unaligned);
556 }
557 else {
558 VALGRIND_MAKE_MEM_DEFINED((const char *)(memh + 1) + len_unaligned, len - len_unaligned);
559 }
560#endif /* WITH_MEM_VALGRIND */
561 }
562
563#ifdef DEBUG_MEMCOUNTER
564 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
565 memcount_raise(__func__);
566 memh->_count = _mallocn_count++;
567#endif
568 return (++memh);
569 }
570 print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
571 SIZET_ARG(len),
572 str,
573 mem_in_use);
574 return nullptr;
575}
576
577void *MEM_guarded_malloc_arrayN(size_t len, size_t size, const char *str)
578{
579 size_t total_size;
580 if (UNLIKELY(!MEM_size_safe_multiply(len, size, &total_size))) {
582 "Malloc array aborted due to integer overflow: "
583 "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
584 SIZET_ARG(len),
586 str,
587 mem_in_use);
588 abort();
589 return nullptr;
590 }
591
592 return MEM_guarded_mallocN(total_size, str);
593}
594
596 size_t alignment,
597 const char *str,
598 const AllocationType allocation_type)
599{
600 /* Huge alignment values doesn't make sense and they wouldn't fit into 'short' used in the
601 * MemHead. */
602 assert(alignment < 1024);
603
604 /* We only support alignments that are a power of two. */
605 assert(IS_POW2(alignment));
606
607 /* Some OS specific aligned allocators require a certain minimal alignment. */
608 /* And #MEM_guarded_freeN also checks that it is freeing a pointer aligned with `sizeof(void *)`.
609 */
610 if (alignment < ALIGNED_MALLOC_MINIMUM_ALIGNMENT) {
612 }
613
614 /* It's possible that MemHead's size is not properly aligned,
615 * do extra padding to deal with this.
616 *
617 * We only support small alignments which fits into short in
618 * order to save some bits in MemHead structure.
619 */
620 size_t extra_padding = MEMHEAD_ALIGN_PADDING(alignment);
621
622#ifdef WITH_MEM_VALGRIND
623 const size_t len_unaligned = len;
624#endif
626
627 MemHead *memh = (MemHead *)aligned_malloc(
628 len + extra_padding + sizeof(MemHead) + sizeof(MemTail), alignment);
629
630 if (LIKELY(memh)) {
631 /* We keep padding in the beginning of MemHead,
632 * this way it's always possible to get MemHead
633 * from the data pointer.
634 */
635 memh = (MemHead *)((char *)memh + extra_padding);
636
637 make_memhead_header(memh, len, str, allocation_type);
638 memh->alignment = short(alignment);
639 if (LIKELY(len)) {
641 memset(memh + 1, 255, len);
642 }
643#ifdef WITH_MEM_VALGRIND
645 VALGRIND_MAKE_MEM_UNDEFINED(memh + 1, len_unaligned);
646 }
647 else {
648 VALGRIND_MAKE_MEM_DEFINED((const char *)(memh + 1) + len_unaligned, len - len_unaligned);
649 }
650#endif /* WITH_MEM_VALGRIND */
651 }
652
653#ifdef DEBUG_MEMCOUNTER
654 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
655 memcount_raise(__func__);
656 memh->_count = _mallocn_count++;
657#endif
658 return (++memh);
659 }
660 print_error("aligned_malloc returns null: len=" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
661 SIZET_ARG(len),
662 str,
663 mem_in_use);
664 return nullptr;
665}
666
667void *MEM_guarded_callocN(size_t len, const char *str)
668{
669 MemHead *memh;
670
672
673 memh = (MemHead *)calloc(len + sizeof(MemHead) + sizeof(MemTail), 1);
674
675 if (memh) {
677#ifdef DEBUG_MEMCOUNTER
678 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
679 memcount_raise(__func__);
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 while (membl) {
1007 if (vmemh == membl + 1) {
1009 return 1;
1010 }
1011
1012 if (membl->next)
1013 membl = MEMNEXT(membl->next);
1014 else
1015 break;
1016 }
1017
1019
1020 print_error("Memoryblock %p: pointer not in memlist\n", vmemh);
1021 return 0;
1022}
1023#endif
1024
1034{
1035 membase->first = membase->last = nullptr;
1036}
1037
1038void MEM_guarded_freeN(void *vmemh, const AllocationType allocation_type)
1039{
1040 MemTail *memt;
1041 MemHead *memh = static_cast<MemHead *>(vmemh);
1042 const char *name;
1043
1044 if (memh == nullptr) {
1045 MemorY_ErroR("free", "attempt to free nullptr pointer");
1046 // print_error(err_stream, "%d\n", (memh+4000)->tag1);
1047 return;
1048 }
1049
1050 if (sizeof(intptr_t) == 8) {
1051 if (intptr_t(memh) & 0x7) {
1052 MemorY_ErroR("free", "attempt to free illegal pointer");
1053 return;
1054 }
1055 }
1056 else {
1057 if (intptr_t(memh) & 0x3) {
1058 MemorY_ErroR("free", "attempt to free illegal pointer");
1059 return;
1060 }
1061 }
1062
1063 memh--;
1064
1065 if (allocation_type != AllocationType::NEW_DELETE &&
1066 (memh->flag & MEMHEAD_FLAG_FROM_CPP_NEW) != 0)
1067 {
1069 vmemh,
1070 "Attempt to use C-style MEM_freeN on a pointer created with CPP-style MEM_new or new\n");
1071 }
1072
1073 if (memh->tag1 == MEMFREE && memh->tag2 == MEMFREE) {
1074 MemorY_ErroR(memh->name, "double free");
1075 return;
1076 }
1077
1078 if ((memh->tag1 == MEMTAG1) && (memh->tag2 == MEMTAG2) && ((memh->len & 0x3) == 0)) {
1079 memt = (MemTail *)(((char *)memh) + sizeof(MemHead) + memh->len);
1080 if (memt->tag3 == MEMTAG3) {
1081
1084 }
1085
1086 memh->tag1 = MEMFREE;
1087 memh->tag2 = MEMFREE;
1088 memt->tag3 = MEMFREE;
1089 /* after tags !!! */
1090 rem_memblock(memh);
1091
1092 return;
1093 }
1094 MemorY_ErroR(memh->name, "end corrupt");
1095 name = check_memlist(memh);
1096 if (name != nullptr) {
1097 if (name != memh->name) {
1098 MemorY_ErroR(name, "is also corrupt");
1099 }
1100 }
1101 }
1102 else {
1104 name = check_memlist(memh);
1106 if (name == nullptr) {
1107 MemorY_ErroR("free", "pointer not in memlist");
1108 }
1109 else {
1110 MemorY_ErroR(name, "error in header");
1111 }
1112 }
1113
1114 totblock--;
1115 /* here a DUMP should happen */
1116}
1117
1118/* --------------------------------------------------------------------- */
1119/* local functions */
1120/* --------------------------------------------------------------------- */
1121
1122static void addtail(volatile localListBase *listbase, void *vlink)
1123{
1124 localLink *link = static_cast<localLink *>(vlink);
1125
1126 /* for a generic API error checks here is fine but
1127 * the limited use here they will never be nullptr */
1128#if 0
1129 if (link == nullptr)
1130 return;
1131 if (listbase == nullptr)
1132 return;
1133#endif
1134
1135 link->next = nullptr;
1136 link->prev = static_cast<localLink *>(listbase->last);
1137
1138 if (listbase->last) {
1139 ((localLink *)listbase->last)->next = link;
1140 }
1141 if (listbase->first == nullptr) {
1142 listbase->first = link;
1143 }
1144 listbase->last = link;
1145}
1146
1147static void remlink(volatile localListBase *listbase, void *vlink)
1148{
1149 localLink *link = static_cast<localLink *>(vlink);
1150
1151 /* for a generic API error checks here is fine but
1152 * the limited use here they will never be nullptr */
1153#if 0
1154 if (link == nullptr)
1155 return;
1156 if (listbase == nullptr)
1157 return;
1158#endif
1159
1160 if (link->next) {
1161 link->next->prev = link->prev;
1162 }
1163 if (link->prev) {
1164 link->prev->next = link->next;
1165 }
1166
1167 if (listbase->last == link) {
1168 listbase->last = link->prev;
1169 }
1170 if (listbase->first == link) {
1171 listbase->first = link->next;
1172 }
1173}
1174
1175static void rem_memblock(MemHead *memh)
1176{
1178 remlink(membase, &memh->next);
1179 if (memh->prev) {
1180 if (memh->next) {
1181 MEMNEXT(memh->prev)->nextname = MEMNEXT(memh->next)->name;
1182 }
1183 else {
1184 MEMNEXT(memh->prev)->nextname = nullptr;
1185 }
1186 }
1188
1191
1192#ifdef DEBUG_MEMDUPLINAME
1193 if (memh->need_free_name)
1194 free((char *)memh->name);
1195#endif
1196
1197 if (UNLIKELY(malloc_debug_memset && memh->len)) {
1198 memset(memh + 1, 255, memh->len);
1199 }
1200 if (LIKELY(memh->alignment == 0)) {
1201 free(memh);
1202 }
1203 else {
1205 }
1206}
1207
1208static void MemorY_ErroR(const char *block, const char *error)
1209{
1210 print_error("Memoryblock %s: %s\n", block, error);
1211
1212#ifdef WITH_ASSERT_ABORT
1213 abort();
1214#endif
1215}
1216
1217static const char *check_memlist(const MemHead *memh)
1218{
1219 MemHead *forw, *back, *forwok, *backok;
1220 const char *name;
1221
1222 forw = static_cast<MemHead *>(membase->first);
1223 if (forw) {
1224 forw = MEMNEXT(forw);
1225 }
1226 forwok = nullptr;
1227 while (forw) {
1228 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) {
1229 break;
1230 }
1231 forwok = forw;
1232 if (forw->next) {
1233 forw = MEMNEXT(forw->next);
1234 }
1235 else {
1236 forw = nullptr;
1237 }
1238 }
1239
1240 back = (MemHead *)membase->last;
1241 if (back) {
1242 back = MEMNEXT(back);
1243 }
1244 backok = nullptr;
1245 while (back) {
1246 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) {
1247 break;
1248 }
1249 backok = back;
1250 if (back->prev) {
1251 back = MEMNEXT(back->prev);
1252 }
1253 else {
1254 back = nullptr;
1255 }
1256 }
1257
1258 if (forw != back) {
1259 return ("MORE THAN 1 MEMORYBLOCK CORRUPT");
1260 }
1261
1262 if (forw == nullptr && back == nullptr) {
1263 /* no wrong headers found then but in search of memblock */
1264
1265 forw = static_cast<MemHead *>(membase->first);
1266 if (forw) {
1267 forw = MEMNEXT(forw);
1268 }
1269 forwok = nullptr;
1270 while (forw) {
1271 if (forw == memh) {
1272 break;
1273 }
1274 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) {
1275 break;
1276 }
1277 forwok = forw;
1278 if (forw->next) {
1279 forw = MEMNEXT(forw->next);
1280 }
1281 else {
1282 forw = nullptr;
1283 }
1284 }
1285 if (forw == nullptr) {
1286 return nullptr;
1287 }
1288
1289 back = (MemHead *)membase->last;
1290 if (back) {
1291 back = MEMNEXT(back);
1292 }
1293 backok = nullptr;
1294 while (back) {
1295 if (back == memh) {
1296 break;
1297 }
1298 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) {
1299 break;
1300 }
1301 backok = back;
1302 if (back->prev) {
1303 back = MEMNEXT(back->prev);
1304 }
1305 else {
1306 back = nullptr;
1307 }
1308 }
1309 }
1310
1311 if (forwok) {
1312 name = forwok->nextname;
1313 }
1314 else {
1315 name = "No name found";
1316 }
1317
1318 if (forw == memh) {
1319 /* to be sure but this block is removed from the list */
1320 if (forwok) {
1321 if (backok) {
1322 forwok->next = (MemHead *)&backok->next;
1323 backok->prev = (MemHead *)&forwok->next;
1324 forwok->nextname = backok->name;
1325 }
1326 else {
1327 forwok->next = nullptr;
1328 membase->last = (localLink *)&forwok->next;
1329 }
1330 }
1331 else {
1332 if (backok) {
1333 backok->prev = nullptr;
1334 membase->first = &backok->next;
1335 }
1336 else {
1337 membase->first = membase->last = nullptr;
1338 }
1339 }
1340 }
1341 else {
1342 MemorY_ErroR(name, "Additional error in header");
1343 return ("Additional error in header");
1344 }
1345
1346 return name;
1347}
1348
1350{
1351 size_t _peak_mem;
1352
1354 _peak_mem = peak_mem;
1356
1357 return _peak_mem;
1358}
1359
1366
1368{
1369 size_t _mem_in_use;
1370
1372 _mem_in_use = mem_in_use;
1374
1375 return _mem_in_use;
1376}
1377
1379{
1380 uint _totblock;
1381
1383 _totblock = totblock;
1385
1386 return _totblock;
1387}
1388
1389#ifndef NDEBUG
1390const char *MEM_guarded_name_ptr(void *vmemh)
1391{
1392 if (vmemh) {
1393 MemHead *memh = static_cast<MemHead *>(vmemh);
1394 memh--;
1395 return memh->name;
1396 }
1397
1398 return "MEM_guarded_name_ptr(nullptr)";
1399}
1400
1401void MEM_guarded_name_ptr_set(void *vmemh, const char *str)
1402{
1403 if (!vmemh) {
1404 return;
1405 }
1406
1407 MemHead *memh = static_cast<MemHead *>(vmemh);
1408 memh--;
1409 memh->name = str;
1410 if (memh->prev) {
1411 MEMNEXT(memh->prev)->nextname = str;
1412 }
1413}
1414#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)
Definition filelist.cc:476
#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
i
Definition text_draw.cc:230
uint len
PointerRNA * ptr
Definition wm_files.cc:4227