1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
4 * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 **************************************************************************/
28 #include "vogl_core.h"
29 #include "vogl_console.h"
31 #include "vogl_threading.h"
32 #include "vogl_strutils.h"
35 // Set to 1 to enable stb_malloc, otherwise voglcore uses plain malloc/free/realloc
36 #ifndef VOGL_USE_STB_MALLOC
37 #define VOGL_USE_STB_MALLOC 1
40 #ifndef VOGL_RAND_FILL_ALLLOCATED_MEMORY
41 #define VOGL_RAND_FILL_ALLLOCATED_MEMORY 0
44 #ifndef VOGL_SCRUB_FREED_MEMORY
45 #define VOGL_SCRUB_FREED_MEMORY 0
48 #if VOGL_RAND_FILL_ALLLOCATED_MEMORY || VOGL_SCRUB_FREED_MEMORY
49 #warning VOGL_RAND_FILL_ALLLOCATED_MEMORY and/or VOGL_SCRUB_FREED_MEMORY is enabled
52 #ifdef VOGL_USE_WIN32_API
53 #include "vogl_winhdr.h"
60 #if VOGL_USE_STB_MALLOC
61 #include "vogl_stb_heap.h"
64 #if VOGL_MALLOC_DEBUGGING
73 #undef vogl_check_heap
74 #undef vogl_print_heap_stats
78 #undef vogl_delete_array
80 #if VOGL_MALLOC_DEBUGGING
81 #warning vogl_mem.cpp: Malloc debugging enabled
84 #if VOGL_USE_STB_MALLOC
86 #define STB_ALLOC_INITIAL_HEAP_SIZE 32U * 1024U * 1024U
87 // Purposely written C-style so we can safely do heap allocs BEFORE C++ global construction time.
89 static uint64_t g_initial_heap_storage[(STBM_HEAP_SIZEOF + STB_ALLOC_INITIAL_HEAP_SIZE + sizeof(uint64_t) - 1) / sizeof(uint64_t)];
90 static stbm_heap *g_pHeap;
91 static pthread_mutex_t g_mutex;
92 static size_t g_page_size;
94 // This flag helps us detect calls to malloc, etc. from within asynchronous signal handlers.
95 static bool g_allocating_flag;
97 static void *sys_alloc(void *user_context, size_t size_requested, size_t *size_provided)
99 VOGL_NOTE_UNUSED(user_context);
103 g_page_size = sysconf(_SC_PAGE_SIZE);
108 size_requested = (size_requested + g_page_size - 1) & (~(g_page_size - 1));
110 void *p = mmap(NULL, size_requested, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
112 if ((!p) || (p == MAP_FAILED))
116 // Not using strerror_r because it fails when we're completely out of memory.
117 char *pError_desc = strerror(e);
120 sprintf(buf, "%s: mmap() of %zu bytes failed! Reason: %s (errno 0x%x)\n", VOGL_FUNCTION_NAME, size_requested, pError_desc, e);
122 write(STDERR_FILENO, buf, strlen(buf));
126 *size_provided = size_requested;
131 static void sys_free(void *user_context, void *ptr, size_t size)
133 VOGL_NOTE_UNUSED(user_context);
135 int res = munmap(ptr, size);
140 // Not using strerror_r because it fails when we're completely out of memory.
141 char *pError_desc = strerror(e);
144 sprintf(buf, "%s: munmap() ptr 0x%" PRIxPTR " size 0x%" PRIxPTR " failed! Reason: %s (errno 0x%x)\n", VOGL_FUNCTION_NAME, (uintptr_t)ptr, size, pError_desc, e);
146 write(STDERR_FILENO, buf, strlen(buf));
151 #if VOGL_MALLOC_DEBUGGING
152 static void *rmalloc_malloc_callback(size_t size, void *pUser)
154 VOGL_NOTE_UNUSED(pUser);
155 return stbm_alloc(NULL, g_pHeap, size, 0);
158 static void rmalloc_free_callback(void *ptr, void *pUser)
160 VOGL_NOTE_UNUSED(pUser);
161 stbm_free(NULL, g_pHeap, ptr);
164 static void *rmalloc_realloc_callback(void *ptr, size_t size, void *pUser)
166 VOGL_NOTE_UNUSED(pUser);
167 return stbm_realloc(NULL, g_pHeap, ptr, size, 0);
171 static void init_heap()
176 pthread_mutex_init(&g_mutex, NULL);
178 stbm_heap_config config;
179 memset(&config, 0, sizeof(config));
180 config.system_alloc = sys_alloc;
181 config.system_free = sys_free;
183 g_pHeap = stbm_heap_init(g_initial_heap_storage, sizeof(g_initial_heap_storage), &config);
185 #if VOGL_MALLOC_DEBUGGING
186 Rmalloc_set_callbacks(rmalloc_malloc_callback, rmalloc_free_callback, rmalloc_realloc_callback, NULL);
190 static void lock_heap()
192 VOGL_ASSERT(g_pHeap);
194 pthread_mutex_lock(&g_mutex);
196 VOGL_ASSERT(!g_allocating_flag);
197 g_allocating_flag = true;
200 static void unlock_heap()
202 g_allocating_flag = false;
204 VOGL_ASSERT(g_pHeap);
205 pthread_mutex_unlock(&g_mutex);
208 static void *malloc_block(size_t size, const char *pFile_line)
210 VOGL_ASSERT(g_pHeap);
214 #if VOGL_MALLOC_DEBUGGING
215 void *p = Rmalloc(size, pFile_line);
217 VOGL_NOTE_UNUSED(pFile_line);
218 void *p = stbm_alloc(NULL, g_pHeap, size, 0);
226 static void *realloc_block(void *p, size_t size, const char *pFile_line)
228 VOGL_ASSERT(g_pHeap);
232 #if VOGL_MALLOC_DEBUGGING
233 void *q = Rrealloc(p, size, pFile_line);
235 VOGL_NOTE_UNUSED(pFile_line);
236 void *q = stbm_realloc(NULL, g_pHeap, p, size, 0);
244 static void free_block(void *p, const char *pFile_line)
246 VOGL_ASSERT(g_pHeap);
250 #if VOGL_MALLOC_DEBUGGING
251 Rfree(p, pFile_line);
253 VOGL_NOTE_UNUSED(pFile_line);
254 stbm_free(NULL, g_pHeap, p);
260 static size_t msize_block(void *p, const char *pFile_line)
262 VOGL_ASSERT(g_pHeap);
266 #if VOGL_MALLOC_DEBUGGING
267 size_t n = Rmalloc_usable_size(p, pFile_line);
269 VOGL_NOTE_UNUSED(pFile_line);
270 size_t n = stbm_get_allocation_size(p);
278 static void print_stats(const char *pFile_line)
280 VOGL_ASSERT(g_pHeap);
282 #if VOGL_MALLOC_DEBUGGING
285 Rmalloc_stat(pFile_line);
289 VOGL_NOTE_UNUSED(pFile_line);
293 static void check(const char *pFile_line)
295 VOGL_ASSERT(g_pHeap);
297 #if VOGL_MALLOC_DEBUGGING
300 Rmalloc_test(pFile_line);
304 VOGL_NOTE_UNUSED(pFile_line);
308 #else // !VOGL_USE_STB_MALLOC
310 static bool g_heap_initialized;
311 static pthread_mutex_t g_mutex;
313 static void init_heap()
315 if (g_heap_initialized)
318 pthread_mutex_init(&g_mutex, NULL);
319 g_heap_initialized = true;
322 #if VOGL_USE_STB_MALLOC
323 static void lock_heap()
325 VOGL_ASSERT(g_heap_initialized);
326 pthread_mutex_lock(&g_mutex);
329 static void unlock_heap()
331 VOGL_ASSERT(g_heap_initialized);
332 pthread_mutex_unlock(&g_mutex);
336 static void *malloc_block(size_t size, const char *pFile_line)
338 VOGL_ASSERT(g_heap_initialized);
339 VOGL_NOTE_UNUSED(pFile_line);
341 #if VOGL_MALLOC_DEBUGGING
343 void *p = Rmalloc(size, pFile_line);
346 void *p = malloc(size);
352 static void *realloc_block(void *p, size_t size, const char *pFile_line)
354 VOGL_ASSERT(g_heap_initialized);
355 VOGL_NOTE_UNUSED(pFile_line);
357 #if VOGL_MALLOC_DEBUGGING
359 void *q = Rrealloc(p, size, pFile_line);
362 void *q = realloc(p, size);
368 static void free_block(void *p, const char *pFile_line)
370 VOGL_ASSERT(g_heap_initialized);
371 VOGL_NOTE_UNUSED(pFile_line);
373 #if VOGL_MALLOC_DEBUGGING
375 Rfree(p, pFile_line);
382 static size_t msize_block(void *p, const char *pFile_line)
384 VOGL_ASSERT(g_heap_initialized);
385 VOGL_NOTE_UNUSED(pFile_line);
387 #if VOGL_MALLOC_DEBUGGING
389 size_t n = Rmalloc_usable_size(p, pFile_line);
392 size_t n = malloc_usable_size(p);
398 static void print_stats(const char *pFile_line)
400 VOGL_ASSERT(g_heap_initialized);
401 VOGL_NOTE_UNUSED(pFile_line);
403 #if VOGL_MALLOC_DEBUGGING
405 Rmalloc_stat(pFile_line);
410 static void check(const char *pFile_line)
412 VOGL_ASSERT(g_heap_initialized);
413 VOGL_NOTE_UNUSED(pFile_line);
415 #if VOGL_MALLOC_DEBUGGING
417 Rmalloc_test(pFile_line);
422 #endif // #ifdef VOGL_USE_STB_MALLOC
424 class global_heap_constructor
427 global_heap_constructor()
429 //printf("global_heap_constructor\n");
435 global_heap_constructor g_global_heap_constructor __attribute__((init_priority(102)));
437 VOGL_NAMESPACE_BEGIN(vogl)
439 void vogl_init_heap()
445 void vogl_mem_error(const char *pMsg, const char *pFile_line)
448 vogl::vogl_sprintf_s(buf, sizeof(buf), "%s: Fatal error: %s. Originally called from %s.\n", VOGL_FUNCTION_NAME, pMsg, pFile_line ? pFile_line : "?");
449 vogl_fail(buf, __FILE__, __LINE__);
453 #if VOGL_RAND_FILL_ALLLOCATED_MEMORY || VOGL_SCRUB_FREED_MEMORY
454 static uint32 g_cur_rand = 0xDEADBEEF;
456 static void random_fill(void *p, size_t size)
458 #define JSR (jsr ^= (jsr << 17), jsr ^= (jsr >> 13), jsr ^= (jsr << 5));
459 uint32 jsr = g_cur_rand;
461 while (size >= sizeof(uint32) * 4)
463 static_cast<uint32 *>(p)[0] = jsr;
465 static_cast<uint32 *>(p)[1] = jsr;
467 static_cast<uint32 *>(p)[2] = jsr;
469 static_cast<uint32 *>(p)[3] = jsr;
472 size -= sizeof(uint32) * 4;
473 p = static_cast<uint32 *>(p) + 4;
476 while (size >= sizeof(uint32))
478 static_cast<uint32 *>(p)[0] = jsr;
481 size -= sizeof(uint32);
482 p = static_cast<uint32 *>(p);
487 static_cast<uint8 *>(p)[0] = static_cast<uint8>(jsr);
491 p = static_cast<uint8 *>(p) + 1;
497 #endif // VOGL_RAND_FILL_ALLLOCATED_MEMORY || VOGL_SCRUB_FREED_MEMORY
499 void *vogl_tracked_malloc(const char *pFile_line, size_t size, size_t *pActual_size)
501 size = (size + sizeof(uint32) - 1U) & ~(sizeof(uint32) - 1U);
503 size = sizeof(uint32);
505 if (size > VOGL_MAX_POSSIBLE_HEAP_BLOCK_SIZE)
507 vogl_mem_error("vogl_malloc: size too big", pFile_line);
511 uint8 *p_new = (uint8 *)malloc_block(size, pFile_line);
513 VOGL_ASSERT((reinterpret_cast<ptr_bits_t>(p_new) & (VOGL_MIN_ALLOC_ALIGNMENT - 1)) == 0);
517 vogl_mem_error("vogl_malloc: out of memory", pFile_line);
523 *pActual_size = msize_block(p_new, pFile_line);
525 if ((size) && (*pActual_size >= static_cast<uint64_t>(size) * 16U))
527 // I've seen this happen with glibc's absolutely terrible debug heap crap, best to let the caller know that shit is going to die
528 fprintf(stderr, "%s: malloc_usable_size may be misbehaving! Requested %zu bytes, but the usable size is reported as %zu bytes.\n", __FUNCTION__, size, *pActual_size);
532 #if VOGL_RAND_FILL_ALLLOCATED_MEMORY
533 random_fill(p_new, size);
539 void *vogl_tracked_realloc(const char *pFile_line, void *p, size_t size, size_t *pActual_size)
541 if ((ptr_bits_t)p & (VOGL_MIN_ALLOC_ALIGNMENT - 1))
543 vogl_mem_error("vogl_realloc: bad ptr", pFile_line);
547 if (size > VOGL_MAX_POSSIBLE_HEAP_BLOCK_SIZE)
549 vogl_mem_error("vogl_realloc: size too big!", pFile_line);
553 if ((size) && (size < sizeof(uint32)))
554 size = sizeof(uint32);
556 #if VOGL_RAND_FILL_ALLLOCATED_MEMORY || VOGL_SCRUB_FREED_MEMORY
557 size_t orig_size = p ? msize_block(p, pFile_line) : 0;
559 #if VOGL_SCRUB_FREED_MEMORY
560 if ((orig_size) && (!size))
561 random_fill(p, orig_size);
565 void *p_new = realloc_block(p, size, pFile_line);
567 VOGL_ASSERT((reinterpret_cast<ptr_bits_t>(p_new) & (VOGL_MIN_ALLOC_ALIGNMENT - 1)) == 0);
576 *pActual_size = msize_block(p_new, pFile_line);
578 *pActual_size = msize_block(p, pFile_line);
580 if (*pActual_size >= static_cast<uint64_t>(size) * 16U)
582 // I've seen this happen with glibc's absolutely terrible debug heap crap, best to let the caller know that shit is going to die
583 fprintf(stderr, "%s: malloc_usable_size may be misbehaving! Requested %zu bytes, but the usable size is reported as %zu bytes.\n", __FUNCTION__, size, *pActual_size);
588 #if VOGL_RAND_FILL_ALLLOCATED_MEMORY
589 if ((size) && (p_new))
591 size_t new_size = msize_block(p_new, pFile_line);
593 if (new_size > orig_size)
594 random_fill(static_cast<uint8 *>(p_new) + orig_size, new_size - orig_size);
601 void *vogl_tracked_calloc(const char *pFile_line, size_t count, size_t size, size_t *pActual_size)
603 size_t total = count * size;
604 void *p = vogl_tracked_malloc(pFile_line, total, pActual_size);
610 void vogl_tracked_free(const char *pFile_line, void *p)
615 if (reinterpret_cast<ptr_bits_t>(p) & (VOGL_MIN_ALLOC_ALIGNMENT - 1))
617 vogl_mem_error("vogl_free: bad ptr", pFile_line);
621 #if VOGL_SCRUB_FREED_MEMORY
622 random_fill(p, msize_block(p, pFile_line));
625 free_block(p, pFile_line);
628 size_t vogl_msize(void *p)
633 if (reinterpret_cast<ptr_bits_t>(p) & (VOGL_MIN_ALLOC_ALIGNMENT - 1))
635 vogl_mem_error("vogl_msize: bad ptr", VOGL_FILE_POS_STRING);
639 return msize_block(p, VOGL_FILE_POS_STRING);
642 void vogl_tracked_print_stats(const char *pFile_line)
644 print_stats(pFile_line);
647 void vogl_tracked_check_heap(const char *pFile_line)
652 // Also see_CrtSetDbgFlag ()
653 int status = _CrtCheckMemory();
654 VOGL_VERIFY(status == TRUE);
655 #elif defined(__GNUC__)
656 // App MUST have been linked with -mlcheck! Unfortunately -mlcheck() causes the CRT's heap API's to be unusable on some dev boxes, no idea why.
659 fprintf(stderr, "%s: Need implementation\n", __FUNCTION__);
663 VOGL_NAMESPACE_END(vogl)
665 extern "C" void *vogl_realloc(const char *pFile_line, void *p, size_t new_size)
667 return vogl::vogl_tracked_realloc(pFile_line, p, new_size, NULL);