Blender V4.3
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
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
110/* NOTE(@hos): keep this struct aligned (e.g., IRIX/GCC). */
111typedef struct MemHead {
112 int tag1;
113 size_t len;
115 const char *name;
116 const char *nextname;
117 int tag2;
119 /* if non-zero aligned allocation was used and alignment is stored here. */
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_EXECINFO
130 void *backtrace[BACKTRACE_SIZE];
131 int backtrace_size;
132#endif
133
135static_assert(MEM_MIN_CPP_ALIGNMENT <= alignof(MemHead), "Bad alignment of MemHead");
136static_assert(MEM_MIN_CPP_ALIGNMENT <= sizeof(MemHead), "Bad size of MemHead");
137
139
140/* #MemHead::flag. */
150
151typedef struct MemTail {
152 int tag3, pad;
154
155#ifdef DEBUG_BACKTRACE_EXECINFO
156# include <execinfo.h>
157#endif
158
159/* --------------------------------------------------------------------- */
160/* local functions */
161/* --------------------------------------------------------------------- */
162
163static void addtail(volatile localListBase *listbase, void *vlink);
164static void remlink(volatile localListBase *listbase, void *vlink);
165static void rem_memblock(MemHead *memh);
166static void MemorY_ErroR(const char *block, const char *error);
167static const char *check_memlist(const MemHead *memh);
168
169/* --------------------------------------------------------------------- */
170/* locally used defines */
171/* --------------------------------------------------------------------- */
172
173#ifdef __BIG_ENDIAN__
174# define MAKE_ID(a, b, c, d) (int(a) << 24 | int(b) << 16 | (c) << 8 | (d))
175#else
176# define MAKE_ID(a, b, c, d) (int(d) << 24 | int(c) << 16 | (b) << 8 | (a))
177#endif
178
179#define MEMTAG1 MAKE_ID('M', 'E', 'M', 'O')
180#define MEMTAG2 MAKE_ID('R', 'Y', 'B', 'L')
181#define MEMTAG3 MAKE_ID('O', 'C', 'K', '!')
182#define MEMFREE MAKE_ID('F', 'R', 'E', 'E')
183
184#define MEMNEXT(x) ((MemHead *)(((char *)x) - offsetof(MemHead, next)))
185
186/* --------------------------------------------------------------------- */
187/* vars */
188/* --------------------------------------------------------------------- */
189
190static uint totblock = 0;
191static size_t mem_in_use = 0, peak_mem = 0;
192
193static volatile localListBase _membase;
194static volatile localListBase *membase = &_membase;
195static void (*error_callback)(const char *) = nullptr;
196
197static bool malloc_debug_memset = false;
198
199#ifdef malloc
200# undef malloc
201#endif
202
203#ifdef calloc
204# undef calloc
205#endif
206
207#ifdef free
208# undef free
209#endif
210
211/* --------------------------------------------------------------------- */
212/* implementation */
213/* --------------------------------------------------------------------- */
214
215#ifdef __GNUC__
217#endif
218static void
219print_error(const char *message, va_list str_format_args)
220{
221 char buf[512];
222 vsnprintf(buf, sizeof(buf), message, str_format_args);
223 buf[sizeof(buf) - 1] = '\0';
224
225 if (error_callback) {
226 error_callback(buf);
227 }
228 else {
229 fputs(buf, stderr);
230 }
231}
232
233#ifdef __GNUC__
235#endif
236static void
237print_error(const char *message, ...)
238{
239 va_list str_format_args;
240 va_start(str_format_args, message);
241 print_error(message, str_format_args);
242 va_end(str_format_args);
243}
244
245#ifdef __GNUC__
247#endif
248static void
249report_error_on_address(const void *vmemh, const char *message, ...)
250{
251 va_list str_format_args;
252
253 va_start(str_format_args, message);
254 print_error(message, str_format_args);
255 va_end(str_format_args);
256
257 if (vmemh == nullptr) {
259 return;
260 }
261
262 const MemHead *memh = static_cast<const MemHead *>(vmemh);
263 memh--;
264 size_t len = memh->len;
265
266 const void *address = memh;
267 size_t size = len + sizeof(*memh) + sizeof(MemTail);
268 if (UNLIKELY(memh->alignment > 0)) {
269 const MemHeadAligned *memh_aligned = memh;
270 address = MEMHEAD_REAL_PTR(memh_aligned);
271 size = len + sizeof(*memh_aligned) + MEMHEAD_ALIGN_PADDING(memh_aligned->alignment) +
272 sizeof(MemTail);
273 }
275}
276
277static pthread_mutex_t thread_lock = PTHREAD_MUTEX_INITIALIZER;
278
279static void mem_lock_thread()
280{
281 pthread_mutex_lock(&thread_lock);
282}
283
284static void mem_unlock_thread()
285{
286 pthread_mutex_unlock(&thread_lock);
287}
288
290{
291 const char *err_val = nullptr;
292 const MemHead *listend;
293 /* check_memlist starts from the front, and runs until it finds
294 * the requested chunk. For this test, that's the last one. */
295 listend = static_cast<MemHead *>(membase->last);
296
297 err_val = check_memlist(listend);
298
299 return (err_val == nullptr);
300}
301
302void MEM_guarded_set_error_callback(void (*func)(const char *))
303{
304 error_callback = func;
305}
306
311
312size_t MEM_guarded_allocN_len(const void *vmemh)
313{
314 if (vmemh) {
315 const MemHead *memh = static_cast<const MemHead *>(vmemh);
316
317 memh--;
318 return memh->len;
319 }
320
321 return 0;
322}
323
324void *MEM_guarded_dupallocN(const void *vmemh)
325{
326 void *newp = nullptr;
327
328 if (vmemh) {
329 const MemHead *memh = static_cast<const MemHead *>(vmemh);
330 memh--;
331
332 if ((memh->flag & MEMHEAD_FLAG_FROM_CPP_NEW) != 0) {
334 "Attempt to use C-style MEM_dupallocN on a pointer created with "
335 "CPP-style MEM_new or new\n");
336 }
337
338#ifndef DEBUG_MEMDUPLINAME
339 if (LIKELY(memh->alignment == 0)) {
340 newp = MEM_guarded_mallocN(memh->len, "dupli_alloc");
341 }
342 else {
344 memh->len, size_t(memh->alignment), "dupli_alloc", AllocationType::ALLOC_FREE);
345 }
346
347 if (newp == nullptr) {
348 return nullptr;
349 }
350#else
351 {
352 MemHead *nmemh;
353 const char name_prefix[] = "dupli_alloc ";
354 const size_t name_prefix_len = sizeof(name_prefix) - 1;
355 const size_t name_size = strlen(memh->name) + 1;
356 char *name = malloc(name_prefix_len + name_size);
357 memcpy(name, name_prefix, sizeof(name_prefix));
358 memcpy(name + name_prefix_len, memh->name, name_size);
359
360 if (LIKELY(memh->alignment == 0)) {
361 newp = MEM_guarded_mallocN(memh->len, name);
362 }
363 else {
365 memh->len, (size_t)memh->alignment, name, AllocationType::ALLOC_FREE);
366 }
367
368 if (newp == nullptr)
369 return nullptr;
370
371 nmemh = newp;
372 nmemh--;
373
374 nmemh->need_free_name = 1;
375 }
376#endif
377
378 memcpy(newp, vmemh, memh->len);
379 }
380
381 return newp;
382}
383
384void *MEM_guarded_reallocN_id(void *vmemh, size_t len, const char *str)
385{
386 void *newp = nullptr;
387
388 if (vmemh) {
389 MemHead *memh = static_cast<MemHead *>(vmemh);
390 memh--;
391
392 if ((memh->flag & MEMHEAD_FLAG_FROM_CPP_NEW) != 0) {
394 "Attempt to use C-style MEM_reallocN on a pointer created with "
395 "CPP-style MEM_new or new\n");
396 }
397
398 if (LIKELY(memh->alignment == 0)) {
399 newp = MEM_guarded_mallocN(len, memh->name);
400 }
401 else {
403 len, size_t(memh->alignment), memh->name, AllocationType::ALLOC_FREE);
404 }
405
406 if (newp) {
407 if (len < memh->len) {
408 /* shrink */
409 memcpy(newp, vmemh, len);
410 }
411 else {
412 /* grow (or remain same size) */
413 memcpy(newp, vmemh, memh->len);
414 }
415 }
416
417 MEM_guarded_freeN(vmemh, AllocationType::ALLOC_FREE);
418 }
419 else {
420 newp = MEM_guarded_mallocN(len, str);
421 }
422
423 return newp;
424}
425
426void *MEM_guarded_recallocN_id(void *vmemh, size_t len, const char *str)
427{
428 void *newp = nullptr;
429
430 if (vmemh) {
431 MemHead *memh = static_cast<MemHead *>(vmemh);
432 memh--;
433
434 if ((memh->flag & MEMHEAD_FLAG_FROM_CPP_NEW) != 0) {
436 "Attempt to use C-style MEM_recallocN on a pointer created with "
437 "CPP-style MEM_new or new\n");
438 }
439
440 if (LIKELY(memh->alignment == 0)) {
441 newp = MEM_guarded_mallocN(len, memh->name);
442 }
443 else {
445 len, size_t(memh->alignment), memh->name, AllocationType::ALLOC_FREE);
446 }
447
448 if (newp) {
449 if (len < memh->len) {
450 /* shrink */
451 memcpy(newp, vmemh, len);
452 }
453 else {
454 memcpy(newp, vmemh, memh->len);
455
456 if (len > memh->len) {
457 /* grow */
458 /* zero new bytes */
459 memset(((char *)newp) + memh->len, 0, len - memh->len);
460 }
461 }
462 }
463
464 MEM_guarded_freeN(vmemh, AllocationType::ALLOC_FREE);
465 }
466 else {
467 newp = MEM_guarded_callocN(len, str);
468 }
469
470 return newp;
471}
472
473#ifdef DEBUG_BACKTRACE_EXECINFO
474static void make_memhead_backtrace(MemHead *memh)
475{
476 memh->backtrace_size = backtrace(memh->backtrace, BACKTRACE_SIZE);
477}
478
479static void print_memhead_backtrace(MemHead *memh)
480{
481 char **strings;
482 int i;
483
484 strings = backtrace_symbols(memh->backtrace, memh->backtrace_size);
485 for (i = 0; i < memh->backtrace_size; i++) {
486 print_error(" %s\n", strings[i]);
487 }
488
489 free(strings);
490}
491#endif /* DEBUG_BACKTRACE_EXECINFO */
492
493static void make_memhead_header(MemHead *memh,
494 size_t len,
495 const char *str,
496 const AllocationType allocation_type)
497{
498 MemTail *memt;
499
500 memh->tag1 = MEMTAG1;
501 memh->name = str;
502 memh->nextname = nullptr;
503 memh->len = len;
504 memh->flag = (allocation_type == AllocationType::NEW_DELETE ? MEMHEAD_FLAG_FROM_CPP_NEW : 0);
505 memh->alignment = 0;
506 memh->tag2 = MEMTAG2;
507
508#ifdef DEBUG_MEMDUPLINAME
509 memh->need_free_name = 0;
510#endif
511
512#ifdef DEBUG_BACKTRACE_EXECINFO
513 make_memhead_backtrace(memh);
514#endif
515
516 memt = (MemTail *)(((char *)memh) + sizeof(MemHead) + len);
517 memt->tag3 = MEMTAG3;
518
521
523 addtail(membase, &memh->next);
524 if (memh->next) {
525 memh->nextname = MEMNEXT(memh->next)->name;
526 }
529}
530
531void *MEM_guarded_mallocN(size_t len, const char *str)
532{
533 MemHead *memh;
534
535#ifdef WITH_MEM_VALGRIND
536 const size_t len_unaligned = len;
537#endif
539
540 memh = (MemHead *)malloc(len + sizeof(MemHead) + sizeof(MemTail));
541
542 if (LIKELY(memh)) {
543 make_memhead_header(memh, len, str, AllocationType::ALLOC_FREE);
544
545 if (LIKELY(len)) {
547 memset(memh + 1, 255, len);
548 }
549#ifdef WITH_MEM_VALGRIND
551 VALGRIND_MAKE_MEM_UNDEFINED(memh + 1, len_unaligned);
552 }
553 else {
554 VALGRIND_MAKE_MEM_DEFINED((const char *)(memh + 1) + len_unaligned, len - len_unaligned);
555 }
556#endif /* WITH_MEM_VALGRIND */
557 }
558
559#ifdef DEBUG_MEMCOUNTER
560 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
561 memcount_raise(__func__);
562 memh->_count = _mallocn_count++;
563#endif
564 return (++memh);
565 }
566 print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
567 SIZET_ARG(len),
568 str,
569 mem_in_use);
570 return nullptr;
571}
572
573void *MEM_guarded_malloc_arrayN(size_t len, size_t size, const char *str)
574{
575 size_t total_size;
576 if (UNLIKELY(!MEM_size_safe_multiply(len, size, &total_size))) {
578 "Malloc array aborted due to integer overflow: "
579 "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
580 SIZET_ARG(len),
581 SIZET_ARG(size),
582 str,
583 mem_in_use);
584 abort();
585 return nullptr;
586 }
587
588 return MEM_guarded_mallocN(total_size, str);
589}
590
592 size_t alignment,
593 const char *str,
594 const AllocationType allocation_type)
595{
596 /* Huge alignment values doesn't make sense and they wouldn't fit into 'short' used in the
597 * MemHead. */
598 assert(alignment < 1024);
599
600 /* We only support alignments that are a power of two. */
601 assert(IS_POW2(alignment));
602
603 /* Some OS specific aligned allocators require a certain minimal alignment. */
604 /* And #MEM_guarded_freeN also checks that it is freeing a pointer aligned with `sizeof(void *)`.
605 */
606 if (alignment < ALIGNED_MALLOC_MINIMUM_ALIGNMENT) {
608 }
609
610 /* It's possible that MemHead's size is not properly aligned,
611 * do extra padding to deal with this.
612 *
613 * We only support small alignments which fits into short in
614 * order to save some bits in MemHead structure.
615 */
616 size_t extra_padding = MEMHEAD_ALIGN_PADDING(alignment);
617
618#ifdef WITH_MEM_VALGRIND
619 const size_t len_unaligned = len;
620#endif
622
623 MemHead *memh = (MemHead *)aligned_malloc(
624 len + extra_padding + sizeof(MemHead) + sizeof(MemTail), alignment);
625
626 if (LIKELY(memh)) {
627 /* We keep padding in the beginning of MemHead,
628 * this way it's always possible to get MemHead
629 * from the data pointer.
630 */
631 memh = (MemHead *)((char *)memh + extra_padding);
632
633 make_memhead_header(memh, len, str, allocation_type);
634 memh->alignment = short(alignment);
635 if (LIKELY(len)) {
637 memset(memh + 1, 255, len);
638 }
639#ifdef WITH_MEM_VALGRIND
641 VALGRIND_MAKE_MEM_UNDEFINED(memh + 1, len_unaligned);
642 }
643 else {
644 VALGRIND_MAKE_MEM_DEFINED((const char *)(memh + 1) + len_unaligned, len - len_unaligned);
645 }
646#endif /* WITH_MEM_VALGRIND */
647 }
648
649#ifdef DEBUG_MEMCOUNTER
650 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
651 memcount_raise(__func__);
652 memh->_count = _mallocn_count++;
653#endif
654 return (++memh);
655 }
656 print_error("aligned_malloc returns null: len=" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
657 SIZET_ARG(len),
658 str,
659 mem_in_use);
660 return nullptr;
661}
662
663void *MEM_guarded_callocN(size_t len, const char *str)
664{
665 MemHead *memh;
666
668
669 memh = (MemHead *)calloc(len + sizeof(MemHead) + sizeof(MemTail), 1);
670
671 if (memh) {
672 make_memhead_header(memh, len, str, AllocationType::ALLOC_FREE);
673#ifdef DEBUG_MEMCOUNTER
674 if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
675 memcount_raise(__func__);
676 memh->_count = _mallocn_count++;
677#endif
678 return (++memh);
679 }
680 print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
681 SIZET_ARG(len),
682 str,
683 mem_in_use);
684 return nullptr;
685}
686
687void *MEM_guarded_calloc_arrayN(size_t len, size_t size, const char *str)
688{
689 size_t total_size;
690 if (UNLIKELY(!MEM_size_safe_multiply(len, size, &total_size))) {
692 "Calloc array aborted due to integer overflow: "
693 "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
694 SIZET_ARG(len),
695 SIZET_ARG(size),
696 str,
697 mem_in_use);
698 abort();
699 return nullptr;
700 }
701
702 return MEM_guarded_callocN(total_size, str);
703}
704
706 const size_t size,
707 const size_t alignment,
708 const char *str)
709{
710 size_t bytes_num;
711 if (UNLIKELY(!MEM_size_safe_multiply(len, size, &bytes_num))) {
713 "Calloc array aborted due to integer overflow: "
714 "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
715 SIZET_ARG(len),
716 SIZET_ARG(size),
717 str,
718 mem_in_use);
719 abort();
720 return nullptr;
721 }
722 if (alignment <= MEM_MIN_CPP_ALIGNMENT) {
723 return MEM_callocN(bytes_num, str);
724 }
725 /* There is no lower level #calloc with an alignment parameter, so we have to fallback to using
726 * #memset unfortunately. */
727 void *ptr = MEM_mallocN_aligned(bytes_num, alignment, str);
728 if (!ptr) {
729 return nullptr;
730 }
731 memset(ptr, 0, bytes_num);
732 return ptr;
733}
734
735/* Memory statistics print */
741
742static int compare_name(const void *p1, const void *p2)
743{
744 const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
745 const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
746
747 return strcmp(pb1->name, pb2->name);
748}
749
750static int compare_len(const void *p1, const void *p2)
751{
752 const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
753 const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
754
755 if (pb1->len < pb2->len) {
756 return 1;
757 }
758 if (pb1->len == pb2->len) {
759 return 0;
760 }
761
762 return -1;
763}
764
766{
767 MemHead *membl;
768 MemPrintBlock *pb, *printblock;
769 uint totpb, a, b;
770 size_t mem_in_use_slop = 0;
771
773
774 if (totblock != 0) {
775 /* put memory blocks into array */
776 printblock = static_cast<MemPrintBlock *>(malloc(sizeof(MemPrintBlock) * totblock));
777
778 if (UNLIKELY(!printblock)) {
780 print_error("malloc returned null while generating stats");
781 return;
782 }
783 }
784 else {
785 printblock = nullptr;
786 }
787
788 pb = printblock;
789 totpb = 0;
790
791 membl = static_cast<MemHead *>(membase->first);
792 if (membl) {
793 membl = MEMNEXT(membl);
794 }
795
796 while (membl && pb) {
797 pb->name = membl->name;
798 pb->len = membl->len;
799 pb->items = 1;
800
801 totpb++;
802 pb++;
803
804#ifdef USE_MALLOC_USABLE_SIZE
805 if (membl->alignment == 0) {
806 mem_in_use_slop += (sizeof(MemHead) + sizeof(MemTail) + malloc_usable_size((void *)membl)) -
807 membl->len;
808 }
809#endif
810
811 if (membl->next) {
812 membl = MEMNEXT(membl->next);
813 }
814 else {
815 break;
816 }
817 }
818
819 /* sort by name and add together blocks with the same name */
820 if (totpb > 1) {
821 qsort(printblock, totpb, sizeof(MemPrintBlock), compare_name);
822 }
823
824 for (a = 0, b = 0; a < totpb; a++) {
825 if (a == b) {
826 continue;
827 }
828 if (strcmp(printblock[a].name, printblock[b].name) == 0) {
829 printblock[b].len += printblock[a].len;
830 printblock[b].items++;
831 }
832 else {
833 b++;
834 memcpy(&printblock[b], &printblock[a], sizeof(MemPrintBlock));
835 }
836 }
837 totpb = b + 1;
838
839 /* sort by length and print */
840 if (totpb > 1) {
841 qsort(printblock, totpb, sizeof(MemPrintBlock), compare_len);
842 }
843
844 printf("\ntotal memory len: %.3f MB\n", double(mem_in_use) / double(1024 * 1024));
845 printf("peak memory len: %.3f MB\n", double(peak_mem) / double(1024 * 1024));
846 printf("slop memory len: %.3f MB\n", double(mem_in_use_slop) / double(1024 * 1024));
847 printf(" ITEMS TOTAL-MiB AVERAGE-KiB TYPE\n");
848 for (a = 0, pb = printblock; a < totpb; a++, pb++) {
849 printf("%6d (%8.3f %8.3f) %s\n",
850 pb->items,
851 double(pb->len) / double(1024 * 1024),
852 double(pb->len) / 1024.0 / double(pb->items),
853 pb->name);
854 }
855
856 if (printblock != nullptr) {
857 free(printblock);
858 }
859
861
862#ifdef HAVE_MALLOC_STATS
863 printf("System Statistics:\n");
864 malloc_stats();
865#endif
866}
867
869 "mb_userinfo = {}\n"
870 "totmem = 0\n"
871 "for mb_item in membase:\n"
872 " mb_item_user_size = mb_userinfo.setdefault(mb_item['name'], [0,0])\n"
873 " mb_item_user_size[0] += 1 # Add a user\n"
874 " mb_item_user_size[1] += mb_item['len'] # Increment the size\n"
875 " totmem += mb_item['len']\n"
876 "print('(membase) items:', len(membase), '| unique-names:',\n"
877 " len(mb_userinfo), '| total-mem:', totmem)\n"
878 "mb_userinfo_sort = list(mb_userinfo.items())\n"
879 "for sort_name, sort_func in (('size', lambda a: -a[1][1]),\n"
880 " ('users', lambda a: -a[1][0]),\n"
881 " ('name', lambda a: a[0])):\n"
882 " print('\\nSorting by:', sort_name)\n"
883 " mb_userinfo_sort.sort(key = sort_func)\n"
884 " for item in mb_userinfo_sort:\n"
885 " print('name:%%s, users:%%i, len:%%i' %%\n"
886 " (item[0], item[1][0], item[1][1]))\n";
887
888/* Prints in python syntax for easy */
890{
891 MemHead *membl;
892
894
895 membl = static_cast<MemHead *>(membase->first);
896 if (membl) {
897 membl = MEMNEXT(membl);
898 }
899
900 if (pydict) {
901 print_error("# membase_debug.py\n");
902 print_error("membase = [\n");
903 }
904 while (membl) {
905 if (pydict) {
906 print_error(" {'len':" SIZET_FORMAT
907 ", "
908 "'name':'''%s''', "
909 "'pointer':'%p'},\n",
910 SIZET_ARG(membl->len),
911 membl->name,
912 (void *)(membl + 1));
913 }
914 else {
915#ifdef DEBUG_MEMCOUNTER
916 print_error("%s len: " SIZET_FORMAT " %p, count: %d\n",
917 membl->name,
918 SIZET_ARG(membl->len),
919 membl + 1,
920 membl->_count);
921#else
922 print_error("%s len: " SIZET_FORMAT " %p\n",
923 membl->name,
924 SIZET_ARG(membl->len),
925 (void *)(membl + 1));
926#endif
927
928#ifdef DEBUG_BACKTRACE_EXECINFO
929 print_memhead_backtrace(membl);
930#elif defined(DEBUG_BACKTRACE) && defined(WITH_ASAN)
931 __asan_describe_address(membl);
932#endif
933 }
934 if (membl->next) {
935 membl = MEMNEXT(membl->next);
936 }
937 else {
938 break;
939 }
940 }
941 if (pydict) {
942 print_error("]\n\n");
944 }
945
947}
948
949void MEM_guarded_callbackmemlist(void (*func)(void *))
950{
951 MemHead *membl;
952
954
955 membl = static_cast<MemHead *>(membase->first);
956 if (membl) {
957 membl = MEMNEXT(membl);
958 }
959
960 while (membl) {
961 func(membl + 1);
962 if (membl->next) {
963 membl = MEMNEXT(membl->next);
964 }
965 else {
966 break;
967 }
968 }
969
971}
972
973#if 0
974short MEM_guarded_testN(void *vmemh)
975{
976 MemHead *membl;
977
979
980 membl = membase->first;
981 if (membl)
982 membl = MEMNEXT(membl);
983
984 while (membl) {
985 if (vmemh == membl + 1) {
987 return 1;
988 }
989
990 if (membl->next)
991 membl = MEMNEXT(membl->next);
992 else
993 break;
994 }
995
997
998 print_error("Memoryblock %p: pointer not in memlist\n", vmemh);
999 return 0;
1000}
1001#endif
1002
1012{
1013 membase->first = membase->last = nullptr;
1014}
1015
1016void MEM_guarded_freeN(void *vmemh, const AllocationType allocation_type)
1017{
1018 MemTail *memt;
1019 MemHead *memh = static_cast<MemHead *>(vmemh);
1020 const char *name;
1021
1022 if (memh == nullptr) {
1023 MemorY_ErroR("free", "attempt to free nullptr pointer");
1024 // print_error(err_stream, "%d\n", (memh+4000)->tag1);
1025 return;
1026 }
1027
1028 if (sizeof(intptr_t) == 8) {
1029 if (intptr_t(memh) & 0x7) {
1030 MemorY_ErroR("free", "attempt to free illegal pointer");
1031 return;
1032 }
1033 }
1034 else {
1035 if (intptr_t(memh) & 0x3) {
1036 MemorY_ErroR("free", "attempt to free illegal pointer");
1037 return;
1038 }
1039 }
1040
1041 memh--;
1042
1043 if (allocation_type != AllocationType::NEW_DELETE &&
1044 (memh->flag & MEMHEAD_FLAG_FROM_CPP_NEW) != 0)
1045 {
1047 vmemh,
1048 "Attempt to use C-style MEM_freeN on a pointer created with CPP-style MEM_new or new\n");
1049 }
1050
1051 if (memh->tag1 == MEMFREE && memh->tag2 == MEMFREE) {
1052 MemorY_ErroR(memh->name, "double free");
1053 return;
1054 }
1055
1056 if ((memh->tag1 == MEMTAG1) && (memh->tag2 == MEMTAG2) && ((memh->len & 0x3) == 0)) {
1057 memt = (MemTail *)(((char *)memh) + sizeof(MemHead) + memh->len);
1058 if (memt->tag3 == MEMTAG3) {
1059
1062 }
1063
1064 memh->tag1 = MEMFREE;
1065 memh->tag2 = MEMFREE;
1066 memt->tag3 = MEMFREE;
1067 /* after tags !!! */
1068 rem_memblock(memh);
1069
1070 return;
1071 }
1072 MemorY_ErroR(memh->name, "end corrupt");
1073 name = check_memlist(memh);
1074 if (name != nullptr) {
1075 if (name != memh->name) {
1076 MemorY_ErroR(name, "is also corrupt");
1077 }
1078 }
1079 }
1080 else {
1082 name = check_memlist(memh);
1084 if (name == nullptr) {
1085 MemorY_ErroR("free", "pointer not in memlist");
1086 }
1087 else {
1088 MemorY_ErroR(name, "error in header");
1089 }
1090 }
1091
1092 totblock--;
1093 /* here a DUMP should happen */
1094}
1095
1096/* --------------------------------------------------------------------- */
1097/* local functions */
1098/* --------------------------------------------------------------------- */
1099
1100static void addtail(volatile localListBase *listbase, void *vlink)
1101{
1102 localLink *link = static_cast<localLink *>(vlink);
1103
1104 /* for a generic API error checks here is fine but
1105 * the limited use here they will never be nullptr */
1106#if 0
1107 if (link == nullptr)
1108 return;
1109 if (listbase == nullptr)
1110 return;
1111#endif
1112
1113 link->next = nullptr;
1114 link->prev = static_cast<localLink *>(listbase->last);
1115
1116 if (listbase->last) {
1117 ((localLink *)listbase->last)->next = link;
1118 }
1119 if (listbase->first == nullptr) {
1120 listbase->first = link;
1121 }
1122 listbase->last = link;
1123}
1124
1125static void remlink(volatile localListBase *listbase, void *vlink)
1126{
1127 localLink *link = static_cast<localLink *>(vlink);
1128
1129 /* for a generic API error checks here is fine but
1130 * the limited use here they will never be nullptr */
1131#if 0
1132 if (link == nullptr)
1133 return;
1134 if (listbase == nullptr)
1135 return;
1136#endif
1137
1138 if (link->next) {
1139 link->next->prev = link->prev;
1140 }
1141 if (link->prev) {
1142 link->prev->next = link->next;
1143 }
1144
1145 if (listbase->last == link) {
1146 listbase->last = link->prev;
1147 }
1148 if (listbase->first == link) {
1149 listbase->first = link->next;
1150 }
1151}
1152
1153static void rem_memblock(MemHead *memh)
1154{
1156 remlink(membase, &memh->next);
1157 if (memh->prev) {
1158 if (memh->next) {
1159 MEMNEXT(memh->prev)->nextname = MEMNEXT(memh->next)->name;
1160 }
1161 else {
1162 MEMNEXT(memh->prev)->nextname = nullptr;
1163 }
1164 }
1166
1169
1170#ifdef DEBUG_MEMDUPLINAME
1171 if (memh->need_free_name)
1172 free((char *)memh->name);
1173#endif
1174
1175 if (UNLIKELY(malloc_debug_memset && memh->len)) {
1176 memset(memh + 1, 255, memh->len);
1177 }
1178 if (LIKELY(memh->alignment == 0)) {
1179 free(memh);
1180 }
1181 else {
1183 }
1184}
1185
1186static void MemorY_ErroR(const char *block, const char *error)
1187{
1188 print_error("Memoryblock %s: %s\n", block, error);
1189
1190#ifdef WITH_ASSERT_ABORT
1191 abort();
1192#endif
1193}
1194
1195static const char *check_memlist(const MemHead *memh)
1196{
1197 MemHead *forw, *back, *forwok, *backok;
1198 const char *name;
1199
1200 forw = static_cast<MemHead *>(membase->first);
1201 if (forw) {
1202 forw = MEMNEXT(forw);
1203 }
1204 forwok = nullptr;
1205 while (forw) {
1206 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) {
1207 break;
1208 }
1209 forwok = forw;
1210 if (forw->next) {
1211 forw = MEMNEXT(forw->next);
1212 }
1213 else {
1214 forw = nullptr;
1215 }
1216 }
1217
1218 back = (MemHead *)membase->last;
1219 if (back) {
1220 back = MEMNEXT(back);
1221 }
1222 backok = nullptr;
1223 while (back) {
1224 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) {
1225 break;
1226 }
1227 backok = back;
1228 if (back->prev) {
1229 back = MEMNEXT(back->prev);
1230 }
1231 else {
1232 back = nullptr;
1233 }
1234 }
1235
1236 if (forw != back) {
1237 return ("MORE THAN 1 MEMORYBLOCK CORRUPT");
1238 }
1239
1240 if (forw == nullptr && back == nullptr) {
1241 /* no wrong headers found then but in search of memblock */
1242
1243 forw = static_cast<MemHead *>(membase->first);
1244 if (forw) {
1245 forw = MEMNEXT(forw);
1246 }
1247 forwok = nullptr;
1248 while (forw) {
1249 if (forw == memh) {
1250 break;
1251 }
1252 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) {
1253 break;
1254 }
1255 forwok = forw;
1256 if (forw->next) {
1257 forw = MEMNEXT(forw->next);
1258 }
1259 else {
1260 forw = nullptr;
1261 }
1262 }
1263 if (forw == nullptr) {
1264 return nullptr;
1265 }
1266
1267 back = (MemHead *)membase->last;
1268 if (back) {
1269 back = MEMNEXT(back);
1270 }
1271 backok = nullptr;
1272 while (back) {
1273 if (back == memh) {
1274 break;
1275 }
1276 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) {
1277 break;
1278 }
1279 backok = back;
1280 if (back->prev) {
1281 back = MEMNEXT(back->prev);
1282 }
1283 else {
1284 back = nullptr;
1285 }
1286 }
1287 }
1288
1289 if (forwok) {
1290 name = forwok->nextname;
1291 }
1292 else {
1293 name = "No name found";
1294 }
1295
1296 if (forw == memh) {
1297 /* to be sure but this block is removed from the list */
1298 if (forwok) {
1299 if (backok) {
1300 forwok->next = (MemHead *)&backok->next;
1301 backok->prev = (MemHead *)&forwok->next;
1302 forwok->nextname = backok->name;
1303 }
1304 else {
1305 forwok->next = nullptr;
1306 membase->last = (localLink *)&forwok->next;
1307 }
1308 }
1309 else {
1310 if (backok) {
1311 backok->prev = nullptr;
1312 membase->first = &backok->next;
1313 }
1314 else {
1315 membase->first = membase->last = nullptr;
1316 }
1317 }
1318 }
1319 else {
1320 MemorY_ErroR(name, "Additional error in header");
1321 return ("Additional error in header");
1322 }
1323
1324 return name;
1325}
1326
1328{
1329 size_t _peak_mem;
1330
1332 _peak_mem = peak_mem;
1334
1335 return _peak_mem;
1336}
1337
1344
1346{
1347 size_t _mem_in_use;
1348
1350 _mem_in_use = mem_in_use;
1352
1353 return _mem_in_use;
1354}
1355
1357{
1358 uint _totblock;
1359
1361 _totblock = totblock;
1363
1364 return _totblock;
1365}
1366
1367#ifndef NDEBUG
1368const char *MEM_guarded_name_ptr(void *vmemh)
1369{
1370 if (vmemh) {
1371 MemHead *memh = static_cast<MemHead *>(vmemh);
1372 memh--;
1373 return memh->name;
1374 }
1375
1376 return "MEM_guarded_name_ptr(nullptr)";
1377}
1378
1379void MEM_guarded_name_ptr_set(void *vmemh, const char *str)
1380{
1381 if (!vmemh) {
1382 return;
1383 }
1384
1385 MemHead *memh = static_cast<MemHead *>(vmemh);
1386 memh--;
1387 memh->name = str;
1388 if (memh->prev) {
1389 MEMNEXT(memh->prev)->nextname = str;
1390 }
1391}
1392#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)]
local_group_size(16, 16) .push_constant(Type b
static __attribute__((constructor)) void cpu_check()
Definition cpu_check.cc:94
#define printf
int len
#define str(s)
bool leak_detector_has_run
char free_after_leak_detection_message[]
format
void aligned_free(void *ptr)
Definition mallocn.cc:96
void * MEM_mallocN_aligned(size_t len, size_t alignment, const char *str)
Definition mallocn.cc:110
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
void * aligned_malloc(size_t size, size_t alignment)
Definition mallocn.cc:74
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)
struct MemTail MemTail
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)
struct localListBase localListBase
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
struct MemPrintBlock MemPrintBlock
#define MEMTAG2
void MEM_guarded_printmemlist_pydict()
void MEM_guarded_callbackmemlist(void(*func)(void *))
MemHead MemHeadAligned
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)
struct MemHead MemHead
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)
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()
struct localLink localLink
static void mem_lock_thread()
#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)
unsigned short uint16_t
Definition stdint.h:79
_W64 unsigned int uintptr_t
Definition stdint.h:119
_W64 int intptr_t
Definition stdint.h:118
const char * name
const char * nextname
PointerRNA * ptr
Definition wm_files.cc:4126