22 #if __linux__ || __APPLE__ || __FreeBSD__ 38 #if defined(__clang__) || defined(__GNUC__) 40 #define ATTRIBUTE_NO_SANITIZE_ADDRESS \ 41 __attribute__ ((no_sanitize_address)) 43 #if defined(__GNUC__) && __GNUC__ >= 5 44 #define ATTRIBUTE_NO_SANITIZE_THREAD \ 45 __attribute__ ((no_sanitize_thread)) 47 #define ATTRIBUTE_NO_SANITIZE_THREAD 50 #define ATTRIBUTE_NO_SANITIZE \ 51 ATTRIBUTE_NO_SANITIZE_ADDRESS ATTRIBUTE_NO_SANITIZE_THREAD 54 #define ATTRIBUTE_NO_SANITIZE 75 #define BYPASS_CHECKER 0 78 #define LEAK_CHECKER 0 101 #define USE_ATOMICS 0 106 #if defined(_MSC_VER) || USE_ATOMICS 115 #define COUNTER_ZERO { 0 } 119 #if defined(_MSC_VER) || USE_ATOMICS 128 #if defined(_MSC_VER) || USE_ATOMICS 129 return (curr += inc);
131 return __sync_add_and_fetch(&curr, inc);
137 #if defined(_MSC_VER) || USE_ATOMICS 138 return (curr -= dec);
140 return __sync_sub_and_fetch(&curr, dec);
145 #define INIT_HEAP_SIZE 1024 * 1024 154 #define PPREFIX "malloc_tracker ### " 185 #if !defined(__APPLE__) 186 #define HAVE_THREAD_LOCAL 1 188 #define HAVE_THREAD_LOCAL 0 191 #if HAVE_THREAD_LOCAL 192 static thread_local LocalStats
tl_stats = { 0, 0, 0 };
204 #if HAVE_THREAD_LOCAL 217 tl_stats.total_allocs = 0;
218 tl_stats.current_allocs = 0;
225 #if HAVE_THREAD_LOCAL 226 tl_stats.total_allocs++;
227 tl_stats.current_allocs++;
228 tl_stats.bytes += inc;
230 if (tl_stats.bytes > tl_delay_threshold)
249 #if HAVE_THREAD_LOCAL 250 tl_stats.current_allocs--;
251 tl_stats.bytes -= dec;
253 if (tl_stats.bytes < -tl_delay_threshold)
288 fprintf(stderr,
PPREFIX "floating %zu, peak %zu, base %zu\n",
294 memory_limit_indication = size;
305 ssize_t
high = 0,
low = 0, close = 0;
308 void init(ssize_t current) {
309 high =
low = close = current;
313 void aggregate(ssize_t current) {
314 if (high < current) high = current;
315 if (
low > current)
low = current;
326 if (!mp_enable)
return;
330 mp_float.init(float_current);
331 mp_base.init(base_current);
336 mp_float.aggregate(float_current);
337 mp_base.aggregate(base_current);
346 void RunTask(
const std::chrono::steady_clock::time_point& tp)
final;
354 void MemoryProfiler::RunTask(
const std::chrono::steady_clock::time_point&) {
366 line <<
"class" <<
"MemProfile" 367 <<
"event" <<
"profile" 368 <<
"total" << copy_float.close + copy_base.close
369 <<
"float" << copy_float.close
370 <<
"base" << copy_base.close;
372 line.
sub(
"float_hlc")
373 <<
"high" << copy_float.high
374 <<
"low" << copy_float.low
375 <<
"close" << copy_float.close;
378 <<
"high" << copy_base.high
379 <<
"low" << copy_base.low
380 <<
"close" << copy_base.close;
384 sched.
Add(std::chrono::milliseconds(250),
385 new MemoryProfiler(logger),
true);
391 #if __linux__ || __APPLE__ || __FreeBSD__ 394 static __attribute__ ((constructor))
void init() {
397 real_malloc = (
malloc_type)dlsym(RTLD_DEFAULT,
"__interceptor_malloc");
400 real_realloc = (
realloc_type)dlsym(RTLD_DEFAULT,
"__interceptor_realloc");
402 fprintf(stderr,
PPREFIX "dlerror %s\n", dlerror());
406 real_free = (
free_type)dlsym(RTLD_DEFAULT,
"__interceptor_free");
408 fprintf(stderr,
PPREFIX "dlerror %s\n", dlerror());
412 fprintf(stderr,
PPREFIX "using AddressSanitizer's malloc\n");
417 real_malloc = (
malloc_type)dlsym(RTLD_NEXT,
"malloc");
419 fprintf(stderr,
PPREFIX "dlerror %s\n", dlerror());
423 real_realloc = (
realloc_type)dlsym(RTLD_NEXT,
"realloc");
425 fprintf(stderr,
PPREFIX "dlerror %s\n", dlerror());
431 real_free = (
free_type)dlsym(RTLD_NEXT,
"free");
433 fprintf(stderr,
PPREFIX "dlerror %s\n", dlerror());
439 static __attribute__ ((destructor))
void finish() {
442 "exiting, total: %zu, peak: %zu, current: %zu / %zu, " 443 "allocs: %zu, unfreed: %zu\n",
454 #if !defined(NDEBUG) && BYPASS_CHECKER 455 static constexpr
size_t kBypassCheckerSize = 1024 * 1024;
456 static std::pair<void*, size_t> s_bypass_checker[kBypassCheckerSize];
457 static std::mutex s_bypass_mutex;
462 #if defined(_MSC_VER) 468 fprintf(stderr,
PPREFIX "bypass_malloc(%zu size) = %p (current %zu / %zu)\n",
473 if (log_bypass_operations && size >= log_bypass_operations_threshold) {
474 fprintf(stderr,
PPREFIX "bypass_malloc(%zu size) = %p (current %zu / %zu)\n",
478 if (profile_bypass_operations) {
480 stdout, 16,
PPREFIX "bypass profile %zu", size);
483 #if !defined(NDEBUG) && BYPASS_CHECKER 485 std::unique_lock<std::mutex> lock(s_bypass_mutex);
487 for (i = 0; i < kBypassCheckerSize; ++i) {
488 if (s_bypass_checker[i].first !=
nullptr)
continue;
489 s_bypass_checker[i].first = ptr;
490 s_bypass_checker[i].second = size;
493 if (i == kBypassCheckerSize) abort();
513 #if !defined(NDEBUG) && BYPASS_CHECKER 515 std::unique_lock<std::mutex> lock(s_bypass_mutex);
517 for (i = 0; i < kBypassCheckerSize; ++i) {
518 if (s_bypass_checker[i].first != ptr)
continue;
520 if (s_bypass_checker[i].second == size) {
521 s_bypass_checker[i].first =
nullptr;
525 printf(
PPREFIX "bypass_free() checker: " 526 "ptr %p size %zu mismatches allocation of %zu\n",
527 ptr, size, s_bypass_checker[i].second);
530 if (i == kBypassCheckerSize) {
531 printf(
PPREFIX "bypass_free() checker: " 532 "ptr = %p size %zu was not found\n", ptr, size);
544 #if defined(_MSC_VER) 553 #if defined(_MSC_VER) 554 void* ptr = _aligned_malloc(size, alignment);
557 if (real_aligned_alloc) {
562 void* mem =
real_malloc((alignment - 1) +
sizeof(
void*) + size);
564 uintptr_t uptr =
reinterpret_cast<uintptr_t
>(mem) +
sizeof(
void*);
565 uptr += alignment - (uptr & (alignment - 1));
566 ptr =
reinterpret_cast<void*
>(uptr);
569 (
reinterpret_cast<void**
>(ptr))[-1] = mem;
573 fprintf(stderr,
PPREFIX "bypass_aligned_alloc(%zu align %zu size) = %p (current %zu / %zu)\n",
578 #if !defined(NDEBUG) && BYPASS_CHECKER 580 std::unique_lock<std::mutex> lock(s_bypass_mutex);
582 for (i = 0; i < kBypassCheckerSize; ++i) {
583 if (s_bypass_checker[i].first !=
nullptr)
continue;
584 s_bypass_checker[i].first = ptr;
585 s_bypass_checker[i].second = size;
588 if (i == kBypassCheckerSize) abort();
608 #if !defined(NDEBUG) && BYPASS_CHECKER 610 std::unique_lock<std::mutex> lock(s_bypass_mutex);
612 for (i = 0; i < kBypassCheckerSize; ++i) {
613 if (s_bypass_checker[i].first != ptr)
continue;
615 if (s_bypass_checker[i].second == size) {
616 s_bypass_checker[i].first =
nullptr;
620 printf(
PPREFIX "bypass_aligned_free() checker: " 621 "ptr %p size %zu mismatches allocation of %zu\n",
622 ptr, size, s_bypass_checker[i].second);
625 if (i == kBypassCheckerSize) {
626 printf(
PPREFIX "bypass_aligned_free() checker: " 627 "ptr = %p size %zu was not found\n", ptr, size);
639 #if defined(_MSC_VER) 640 return _aligned_free(ptr);
642 if (real_aligned_alloc) {
646 real_free((reinterpret_cast<void**>(ptr))[-1]);
664 #if defined(_MSC_VER) || USE_ATOMICS 671 fprintf(stderr,
PPREFIX "init heap full !!!\n");
678 *
reinterpret_cast<size_t*
>(ret) = aligned_size;
684 fprintf(stderr,
PPREFIX "malloc(%zu / %zu) = %p on init heap\n",
685 size, aligned_size, static_cast<void*>(ret +
padding));
695 fprintf(stderr,
PPREFIX "realloc(%p) = on init heap\n", ptr);
698 ptr =
static_cast<char*
>(ptr) -
padding;
700 if (*reinterpret_cast<size_t*>(
703 "realloc(%p) has no sentinel !!! memory corruption?\n",
707 size_t oldsize = *
reinterpret_cast<size_t*
>(ptr);
709 if (oldsize >= size) {
711 return static_cast<char*
>(ptr) +
padding;
715 ptr =
static_cast<char*
>(ptr) +
padding;
716 void* newptr =
malloc(size);
717 memcpy(newptr, ptr, oldsize);
727 ptr =
static_cast<char*
>(ptr) -
padding;
729 if (*reinterpret_cast<size_t*>(
732 "free(%p) has no sentinel !!! memory corruption?\n",
736 size_t size = *
reinterpret_cast<size_t*
>(ptr);
740 fprintf(stderr,
PPREFIX "free(%p) -> %zu on init heap\n", ptr, size);
747 #define MALLOC_USABLE_SIZE malloc_size 748 #include <malloc/malloc.h> 753 #define MALLOC_USABLE_SIZE malloc_usable_size 754 #include <malloc_np.h> 758 #define NOEXCEPT noexcept 759 #define MALLOC_USABLE_SIZE malloc_usable_size 768 static constexpr
size_t kLeakCheckerSize = 1024 * 1024;
769 static constexpr
size_t kLeakCheckerBacktrace = 32;
770 struct LeakCheckerEntry {
774 void * addrlist[kLeakCheckerBacktrace];
776 static LeakCheckerEntry s_leak_checker[kLeakCheckerSize];
777 static std::mutex s_leak_mutex;
778 static size_t s_leak_round = 0;
780 static void leakchecker_malloc(
void* ptr,
size_t size) {
781 std::unique_lock<std::mutex> lock(s_leak_mutex);
783 for (i = 0; i < kLeakCheckerSize; ++i) {
784 if (s_leak_checker[i].ptr !=
nullptr)
continue;
785 s_leak_checker[i].ptr = ptr;
786 s_leak_checker[i].size = size;
787 s_leak_checker[i].round = s_leak_round;
790 backtrace(s_leak_checker[i].addrlist, kLeakCheckerBacktrace);
793 if (i == kLeakCheckerSize) abort();
796 static void leakchecker_free(
void* ptr) {
797 std::unique_lock<std::mutex> lock(s_leak_mutex);
799 for (i = 0; i < kLeakCheckerSize; ++i) {
800 if (s_leak_checker[i].ptr == ptr) {
801 s_leak_checker[i].ptr =
nullptr;
805 if (i == kLeakCheckerSize) {
806 printf(
PPREFIX "leak_free() checker: " 807 "ptr = %p was not found\n", ptr);
818 std::unique_lock<std::mutex> lock(s_leak_mutex);
819 for (
size_t i = 0; i < kLeakCheckerSize; ++i) {
820 if (s_leak_checker[i].ptr ==
nullptr)
continue;
822 if (s_leak_checker[i].round == s_leak_round) {
823 void** addrlist = s_leak_checker[i].addrlist;
824 printf(
PPREFIX "leak checker: " 825 "ptr %p size %zu new unfreed allocation: " 826 "%p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p " 827 "%p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p\n",
828 s_leak_checker[i].ptr, s_leak_checker[i].size,
829 addrlist[0], addrlist[1], addrlist[2], addrlist[3],
830 addrlist[4], addrlist[5], addrlist[6], addrlist[7],
831 addrlist[8], addrlist[9], addrlist[10], addrlist[11],
832 addrlist[12], addrlist[13], addrlist[14], addrlist[15],
833 addrlist[16], addrlist[17], addrlist[18], addrlist[19],
834 addrlist[20], addrlist[21], addrlist[22], addrlist[23],
835 addrlist[24], addrlist[25], addrlist[26], addrlist[27],
836 addrlist[28], addrlist[29], addrlist[30], addrlist[31]);
848 #if defined(MALLOC_USABLE_SIZE) 858 void *
malloc(
size_t size) NOEXCEPT {
864 void* ret = (*real_malloc)(size);
866 fprintf(stderr,
PPREFIX "malloc(%zu size) = %p (current %zu / %zu)\n",
871 size_t size_used = MALLOC_USABLE_SIZE(ret);
875 fprintf(stderr,
PPREFIX "malloc(%zu size / %zu used) = %p (current %zu / %zu)\n",
881 static thread_local
bool recursive =
false;
887 stdout, 16,
PPREFIX "profile %zu", size);
896 static thread_local
bool recursive =
false;
899 leakchecker_malloc(ret, size);
910 void free(
void* ptr) NOEXCEPT {
923 "free(%p) outside init heap and without real_free !!!\n", ptr);
927 size_t size_used = MALLOC_USABLE_SIZE(ptr);
931 fprintf(stderr,
PPREFIX "free(%p) -> %zu (current %zu / %zu)\n",
936 leakchecker_free(ptr);
945 void *
calloc(
size_t nmemb,
size_t size) NOEXCEPT {
948 if (!ret)
return ret;
949 memset(ret, 0, size);
955 void *
realloc(
void* ptr,
size_t size) NOEXCEPT {
957 if (static_cast<char*>(ptr) >= static_cast<char*>(
init_heap) &&
968 if (ptr ==
nullptr) {
972 size_t oldsize_used = MALLOC_USABLE_SIZE(ptr);
975 void* newptr = (*real_realloc)(ptr, size);
976 if (!newptr)
return nullptr;
978 size_t newsize_used = MALLOC_USABLE_SIZE(newptr);
985 "realloc(%zu -> %zu / %zu) = %p (current %zu / %zu)\n",
986 oldsize_used, size, newsize_used, newptr,
991 "realloc(%zu -> %zu / %zu) = %p -> %p (current %zu / %zu)\n",
992 oldsize_used, size, newsize_used, ptr, newptr,
1002 #elif !defined(_MSC_VER) // GENERIC IMPLEMENTATION for Unix 1019 void* ret = (*real_malloc)(
padding + size);
1023 fprintf(stderr,
PPREFIX "malloc(%zu) = %p (current %zu / %zu)\n",
1024 size, static_cast<char*>(ret) +
padding,
1029 *
reinterpret_cast<size_t*
>(ret) = size;
1030 *
reinterpret_cast<size_t*
>(
1033 return static_cast<char*
>(ret) +
padding;
1042 if (static_cast<char*>(ptr) >=
init_heap &&
1050 "free(%p) outside init heap and without real_free !!!\n", ptr);
1054 ptr =
static_cast<char*
>(ptr) -
padding;
1056 if (*reinterpret_cast<size_t*>(
1059 "free(%p) has no sentinel !!! memory corruption?\n", ptr);
1062 size_t size = *
reinterpret_cast<size_t*
>(ptr);
1066 fprintf(stderr,
PPREFIX "free(%p) -> %zu (current %zu / %zu)\n",
1076 void *
calloc(
size_t nmemb,
size_t size) NOEXCEPT {
1078 if (!size)
return nullptr;
1079 void* ret =
malloc(size);
1080 if (!ret)
return ret;
1081 memset(ret, 0, size);
1089 if (static_cast<char*>(ptr) >= static_cast<char*>(
init_heap) &&
1090 static_cast<char*>(ptr) <=
1101 if (ptr ==
nullptr) {
1105 ptr =
static_cast<char*
>(ptr) -
padding;
1107 if (*reinterpret_cast<size_t*>(
1110 "free(%p) has no sentinel !!! memory corruption?\n", ptr);
1113 size_t oldsize = *
reinterpret_cast<size_t*
>(ptr);
1118 void* newptr = (*real_realloc)(ptr,
padding + size);
1124 "realloc(%zu -> %zu) = %p (current %zu / %zu)\n",
1128 "realloc(%zu -> %zu) = %p -> %p (current %zu / %zu)\n",
1132 *
reinterpret_cast<size_t*
>(newptr) = size;
1134 return static_cast<char*
>(newptr) +
padding;
1139 #else // if defined(_MSC_VER) 1143 #endif // IMPLEMENTATION SWITCH low_type low
member containing lower significant integer value
void update_peak(ssize_t float_curr, ssize_t base_curr)
static uint_pair max()
return an uint_pair instance containing the largest value possible
static free_type real_free
void StartMemProfiler(common::ProfileThread &sched, common::JsonLogger &logger)
launch profiler task
high_type high
member containing higher significant integer value
void flush_memory_statistics()
method to flush thread-local memory statistics when memory_exceeded
void * bypass_aligned_alloc(size_t alignment, size_t size) noexcept
bypass malloc tracker and access aligned_alloc() directly
void *(*)(size_t) malloc_type
function pointer to the real procedures, loaded using dlsym()
void set_memory_limit_indication(ssize_t size)
static constexpr size_t init_alignment
align allocations to init_heap to this number by rounding up allocations
static void dec_count(size_t dec)
decrement allocation to statistics
bool memory_exceeded
memory limit exceeded indicator
ssize_t memory_limit_indication
void *(*)(size_t, size_t) aligned_alloc_type
void bypass_aligned_free(void *ptr, size_t size) noexcept
bypass malloc tracker and access aligned_alloc() directly
void bypass_free(void *ptr, size_t size) noexcept
bypass malloc tracker and access free() directly
void malloc_tracker_reset_peak()
user function to reset the peak allocation to current
static constexpr size_t sentinel
a sentinel value prefixed to each allocation
void malloc_tracker_print_status()
user function which prints current and peak allocation to stderr
static void update_memprofile(ssize_t float_current, ssize_t base_current)
static malloc_type real_malloc
ssize_t malloc_tracker_total_allocs()
user function to return total number of allocations
ssize_t malloc_tracker_current()
user function to return the currently allocated amount of memory
void malloc_tracker_print_leaks()
user function which prints new unfreed areas to stdout since the last call
void Add(const Period &period, ProfileTask *task, bool own_task=false)
Register a regularly scheduled callback.
void * bypass_malloc(size_t size) noexcept
bypass malloc tracker and access malloc() directly
static CounterType float_curr
static constexpr bool profile_operations
static realloc_type real_realloc
void * malloc(size_t size) NOEXCEPT
exported malloc symbol that overrides loading from libc
static CounterType total_allocs
ssize_t malloc_tracker_peak()
user function to return the peak allocation
static constexpr bool profile_bypass_operations
static thread_local LocalStats tl_stats
static CounterType total_bytes
static CounterType mp_next_bar
static aligned_alloc_type real_aligned_alloc
static void * preinit_realloc(void *ptr, size_t size)
static CounterType peak_bytes
static constexpr int log_operations_init_heap
void * calloc(size_t nmemb, size_t size) NOEXCEPT
static constexpr size_t padding
#define INIT_HEAP_SIZE
a simple memory heap for allocations prior to dlsym loading
static const size_t bytes
number of bytes in uint_pair
void *(*)(void *, size_t) realloc_type
JsonLine sub(const Key &key)
return JsonLine has sub-dictionary of this one
static void inc_count(size_t inc)
add allocation to statistics
static CounterType init_heap_use
static constexpr bool mp_enable
static constexpr size_t log_bypass_operations_threshold
static void preinit_free(void *ptr)
static ssize_t sync_sub_and_fetch(CounterType &curr, ssize_t dec)
static constexpr bool log_operations
static CounterType base_curr
static char init_heap[1024 *1024]
JsonLogger is a receiver of JSON output objects for logging.
static constexpr bool log_bypass_operations
static void * preinit_malloc(size_t size) noexcept
static const ssize_t tl_delay_threshold
void free(void *ptr) NOEXCEPT
exported free symbol that overrides loading from libc
#define ATTRIBUTE_NO_SANITIZE
static CounterType current_allocs
void * realloc(void *ptr, size_t size) NOEXCEPT
exported realloc() symbol that overrides loading from libc
void print_raw_backtrace(FILE *out, unsigned int max_frames, const char *fmt,...)
Print a plain hex stack backtrace of the called function to FILE* out, prefixed with the given printf...
static constexpr size_t log_operations_threshold
static ssize_t sync_add_and_fetch(CounterType &curr, ssize_t inc)
void(*)(void *) free_type
JsonLine is an object used to aggregate a set of key:value pairs for output into a JSON log...