2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * Memory allocation which keeps track on allocated memory counters
25 #include <string.h> /* memcpy */
26 #include <sys/types.h>
28 #include "MEM_guardedalloc.h"
30 /* to ensure strict conversions */
31 #include "../../source/blender/blenlib/BLI_strict_flags.h"
33 #include "atomic_ops.h"
34 #include "mallocn_intern.h"
36 typedef struct MemHead {
37 /* Length of allocated memory block. */
41 typedef struct MemHeadAligned {
46 static unsigned int totblock = 0;
47 static size_t mem_in_use = 0, mmap_in_use = 0, peak_mem = 0;
48 static bool malloc_debug_memset = false;
50 static void (*error_callback)(const char *) = NULL;
51 static void (*thread_lock_callback)(void) = NULL;
52 static void (*thread_unlock_callback)(void) = NULL;
55 MEMHEAD_MMAP_FLAG = 1,
56 MEMHEAD_ALIGN_FLAG = 2,
59 #define MEMHEAD_FROM_PTR(ptr) (((MemHead *)ptr) - 1)
60 #define PTR_FROM_MEMHEAD(memhead) (memhead + 1)
61 #define MEMHEAD_ALIGNED_FROM_PTR(ptr) (((MemHeadAligned *)ptr) - 1)
62 #define MEMHEAD_IS_MMAP(memhead) ((memhead)->len & (size_t)MEMHEAD_MMAP_FLAG)
63 #define MEMHEAD_IS_ALIGNED(memhead) ((memhead)->len & (size_t)MEMHEAD_ALIGN_FLAG)
65 /* Uncomment this to have proper peak counter. */
66 #define USE_ATOMIC_MAX
68 MEM_INLINE void update_maximum(size_t *maximum_value, size_t value)
71 atomic_fetch_and_update_max_z(maximum_value, value);
73 *maximum_value = value > *maximum_value ? value : *maximum_value;
78 __attribute__((format(printf, 1, 2)))
81 print_error(const char *str, ...)
87 vsnprintf(buf, sizeof(buf), str, ap);
89 buf[sizeof(buf) - 1] = '\0';
97 static void mem_lock_thread(void)
99 if (thread_lock_callback)
100 thread_lock_callback();
103 static void mem_unlock_thread(void)
105 if (thread_unlock_callback)
106 thread_unlock_callback();
110 size_t MEM_lockfree_allocN_len(const void *vmemh)
113 return MEMHEAD_FROM_PTR(vmemh)->len & ~((size_t)(MEMHEAD_MMAP_FLAG | MEMHEAD_ALIGN_FLAG));
120 void MEM_lockfree_freeN(void *vmemh)
122 MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
123 size_t len = MEM_lockfree_allocN_len(vmemh);
126 print_error("Attempt to free NULL pointer\n");
127 #ifdef WITH_ASSERT_ABORT
133 atomic_sub_and_fetch_u(&totblock, 1);
134 atomic_sub_and_fetch_z(&mem_in_use, len);
136 if (MEMHEAD_IS_MMAP(memh)) {
137 atomic_sub_and_fetch_z(&mmap_in_use, len);
139 /* our windows mmap implementation is not thread safe */
142 if (munmap(memh, len + sizeof(MemHead)))
143 printf("Couldn't unmap memory\n");
149 if (UNLIKELY(malloc_debug_memset && len)) {
150 memset(memh + 1, 255, len);
152 if (UNLIKELY(MEMHEAD_IS_ALIGNED(memh))) {
153 MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
154 aligned_free(MEMHEAD_REAL_PTR(memh_aligned));
162 void *MEM_lockfree_dupallocN(const void *vmemh)
166 MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
167 const size_t prev_size = MEM_lockfree_allocN_len(vmemh);
168 if (UNLIKELY(MEMHEAD_IS_MMAP(memh))) {
169 newp = MEM_lockfree_mapallocN(prev_size, "dupli_mapalloc");
171 else if (UNLIKELY(MEMHEAD_IS_ALIGNED(memh))) {
172 MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
173 newp = MEM_lockfree_mallocN_aligned(
174 prev_size, (size_t)memh_aligned->alignment, "dupli_malloc");
177 newp = MEM_lockfree_mallocN(prev_size, "dupli_malloc");
179 memcpy(newp, vmemh, prev_size);
184 void *MEM_lockfree_reallocN_id(void *vmemh, size_t len, const char *str)
189 MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
190 size_t old_len = MEM_lockfree_allocN_len(vmemh);
192 if (LIKELY(!MEMHEAD_IS_ALIGNED(memh))) {
193 newp = MEM_lockfree_mallocN(len, "realloc");
196 MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
197 newp = MEM_lockfree_mallocN_aligned(len, (size_t)memh_aligned->alignment, "realloc");
203 memcpy(newp, vmemh, len);
206 /* grow (or remain same size) */
207 memcpy(newp, vmemh, old_len);
211 MEM_lockfree_freeN(vmemh);
214 newp = MEM_lockfree_mallocN(len, str);
220 void *MEM_lockfree_recallocN_id(void *vmemh, size_t len, const char *str)
225 MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
226 size_t old_len = MEM_lockfree_allocN_len(vmemh);
228 if (LIKELY(!MEMHEAD_IS_ALIGNED(memh))) {
229 newp = MEM_lockfree_mallocN(len, "recalloc");
232 MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
233 newp = MEM_lockfree_mallocN_aligned(len, (size_t)memh_aligned->alignment, "recalloc");
239 memcpy(newp, vmemh, len);
242 memcpy(newp, vmemh, old_len);
247 memset(((char *)newp) + old_len, 0, len - old_len);
252 MEM_lockfree_freeN(vmemh);
255 newp = MEM_lockfree_callocN(len, str);
261 void *MEM_lockfree_callocN(size_t len, const char *str)
265 len = SIZET_ALIGN_4(len);
267 memh = (MemHead *)calloc(1, len + sizeof(MemHead));
271 atomic_add_and_fetch_u(&totblock, 1);
272 atomic_add_and_fetch_z(&mem_in_use, len);
273 update_maximum(&peak_mem, mem_in_use);
275 return PTR_FROM_MEMHEAD(memh);
277 print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
280 (unsigned int)mem_in_use);
284 void *MEM_lockfree_calloc_arrayN(size_t len, size_t size, const char *str)
287 if (UNLIKELY(!MEM_size_safe_multiply(len, size, &total_size))) {
289 "Calloc array aborted due to integer overflow: "
290 "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total %u\n",
294 (unsigned int)mem_in_use);
299 return MEM_lockfree_callocN(total_size, str);
302 void *MEM_lockfree_mallocN(size_t len, const char *str)
306 len = SIZET_ALIGN_4(len);
308 memh = (MemHead *)malloc(len + sizeof(MemHead));
311 if (UNLIKELY(malloc_debug_memset && len)) {
312 memset(memh + 1, 255, len);
316 atomic_add_and_fetch_u(&totblock, 1);
317 atomic_add_and_fetch_z(&mem_in_use, len);
318 update_maximum(&peak_mem, mem_in_use);
320 return PTR_FROM_MEMHEAD(memh);
322 print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
325 (unsigned int)mem_in_use);
329 void *MEM_lockfree_malloc_arrayN(size_t len, size_t size, const char *str)
332 if (UNLIKELY(!MEM_size_safe_multiply(len, size, &total_size))) {
334 "Malloc array aborted due to integer overflow: "
335 "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total %u\n",
339 (unsigned int)mem_in_use);
344 return MEM_lockfree_mallocN(total_size, str);
347 void *MEM_lockfree_mallocN_aligned(size_t len, size_t alignment, const char *str)
349 /* Huge alignment values doesn't make sense and they wouldn't fit into 'short' used in the
351 assert(alignment < 1024);
353 /* We only support alignments that are a power of two. */
354 assert(IS_POW2(alignment));
356 /* Some OS specific aligned allocators require a certain minimal alignment. */
357 if (alignment < ALIGNED_MALLOC_MINIMUM_ALIGNMENT) {
358 alignment = ALIGNED_MALLOC_MINIMUM_ALIGNMENT;
361 /* It's possible that MemHead's size is not properly aligned,
362 * do extra padding to deal with this.
364 * We only support small alignments which fits into short in
365 * order to save some bits in MemHead structure.
367 size_t extra_padding = MEMHEAD_ALIGN_PADDING(alignment);
369 len = SIZET_ALIGN_4(len);
371 MemHeadAligned *memh = (MemHeadAligned *)aligned_malloc(
372 len + extra_padding + sizeof(MemHeadAligned), alignment);
375 /* We keep padding in the beginning of MemHead,
376 * this way it's always possible to get MemHead
377 * from the data pointer.
379 memh = (MemHeadAligned *)((char *)memh + extra_padding);
381 if (UNLIKELY(malloc_debug_memset && len)) {
382 memset(memh + 1, 255, len);
385 memh->len = len | (size_t)MEMHEAD_ALIGN_FLAG;
386 memh->alignment = (short)alignment;
387 atomic_add_and_fetch_u(&totblock, 1);
388 atomic_add_and_fetch_z(&mem_in_use, len);
389 update_maximum(&peak_mem, mem_in_use);
391 return PTR_FROM_MEMHEAD(memh);
393 print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
396 (unsigned int)mem_in_use);
400 void *MEM_lockfree_mapallocN(size_t len, const char *str)
404 /* on 64 bit, simply use calloc instead, as mmap does not support
405 * allocating > 4 GB on Windows. the only reason mapalloc exists
406 * is to get around address space limitations in 32 bit OSes. */
407 if (sizeof(void *) >= 8)
408 return MEM_lockfree_callocN(len, str);
410 len = SIZET_ALIGN_4(len);
413 /* our windows mmap implementation is not thread safe */
416 memh = mmap(NULL, len + sizeof(MemHead), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
421 if (memh != (MemHead *)-1) {
422 memh->len = len | (size_t)MEMHEAD_MMAP_FLAG;
423 atomic_add_and_fetch_u(&totblock, 1);
424 atomic_add_and_fetch_z(&mem_in_use, len);
425 atomic_add_and_fetch_z(&mmap_in_use, len);
427 update_maximum(&peak_mem, mem_in_use);
428 update_maximum(&peak_mem, mmap_in_use);
430 return PTR_FROM_MEMHEAD(memh);
433 "Mapalloc returns null, fallback to regular malloc: "
434 "len=" SIZET_FORMAT " in %s, total %u\n",
437 (unsigned int)mmap_in_use);
438 return MEM_lockfree_callocN(len, str);
441 void MEM_lockfree_printmemlist_pydict(void)
445 void MEM_lockfree_printmemlist(void)
450 void MEM_lockfree_callbackmemlist(void (*func)(void *))
452 (void)func; /* Ignored. */
455 void MEM_lockfree_printmemlist_stats(void)
457 printf("\ntotal memory len: %.3f MB\n", (double)mem_in_use / (double)(1024 * 1024));
458 printf("peak memory len: %.3f MB\n", (double)peak_mem / (double)(1024 * 1024));
460 "\nFor more detailed per-block statistics run Blender with memory debugging command line "
463 #ifdef HAVE_MALLOC_STATS
464 printf("System Statistics:\n");
469 void MEM_lockfree_set_error_callback(void (*func)(const char *))
471 error_callback = func;
474 bool MEM_lockfree_consistency_check(void)
479 void MEM_lockfree_set_lock_callback(void (*lock)(void), void (*unlock)(void))
481 thread_lock_callback = lock;
482 thread_unlock_callback = unlock;
485 void MEM_lockfree_set_memory_debug(void)
487 malloc_debug_memset = true;
490 size_t MEM_lockfree_get_memory_in_use(void)
495 size_t MEM_lockfree_get_mapped_memory_in_use(void)
500 unsigned int MEM_lockfree_get_memory_blocks_in_use(void)
506 void MEM_lockfree_reset_peak_memory(void)
508 peak_mem = mem_in_use;
511 size_t MEM_lockfree_get_peak_memory(void)
517 const char *MEM_lockfree_name_ptr(void *vmemh)
520 return "unknown block name ptr";
523 return "MEM_lockfree_name_ptr(NULL)";