diff --git a/ConflictSet.cpp b/ConflictSet.cpp index 74faf0f..761d9d2 100644 --- a/ConflictSet.cpp +++ b/ConflictSet.cpp @@ -2819,6 +2819,7 @@ void removeKey(Node *n) { struct __attribute__((visibility("default"))) PeakPrinter { ~PeakPrinter() { + printf("malloc bytes: %g\n", double(mallocBytes)); printf("Peak malloc bytes: %g\n", double(peakMallocBytes)); printf("Node bytes: %g\n", double(nodeBytes)); printf("Peak node bytes: %g\n", double(peakNodeBytes)); diff --git a/HashTable.cpp b/HashTable.cpp index ed57b22..191ebda 100644 --- a/HashTable.cpp +++ b/HashTable.cpp @@ -102,7 +102,7 @@ ConflictSet::ConflictSet(int64_t oldestVersion) ConflictSet::~ConflictSet() { if (impl) { impl->~Impl(); - free(impl); + safe_free(impl); } } @@ -142,6 +142,6 @@ ConflictSet_create(int64_t oldestVersion) { __attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) { using Impl = ConflictSet::Impl; ((Impl *)cs)->~Impl(); - free(cs); + safe_free(cs); } } diff --git a/Internal.h b/Internal.h index 30ae021..238bcab 100644 --- a/Internal.h +++ b/Internal.h @@ -41,45 +41,47 @@ operator<=>(const std::span &lhs, // GCOVR_EXCL_START #if SHOW_MEMORY -#ifdef __APPLE__ -#include -#else -#include -#endif inline int64_t mallocBytes = 0; inline int64_t peakMallocBytes = 0; +constexpr auto kIntMallocHeaderSize = 16; #endif -// malloc that aborts on OOM and thus always returns a non-null pointer +// malloc that aborts on OOM and thus always returns a non-null pointer. Must be +// paired with `safe_free`. __attribute__((always_inline)) inline void *safe_malloc(size_t s) { +#if SHOW_MEMORY + mallocBytes += s; + if (mallocBytes > peakMallocBytes) { + peakMallocBytes = mallocBytes; + } + void *p = malloc(s + kIntMallocHeaderSize); + if (p == nullptr) { + abort(); + } + memcpy(p, &s, sizeof(s)); + return (char *)p + kIntMallocHeaderSize; +#else void *p = malloc(s); if (p == nullptr) { abort(); } -#if SHOW_MEMORY -#ifdef __APPLE__ - mallocBytes += s; -#else - mallocBytes += malloc_usable_size(p); -#endif - if (mallocBytes > peakMallocBytes) { - peakMallocBytes = mallocBytes; - } -#endif return p; +#endif } +// Must be paired with `safe_malloc`. +// // There's nothing safer about this than free. Only called safe_free for // symmetry with safe_malloc. __attribute__((always_inline)) inline void safe_free(void *p) { #if SHOW_MEMORY -#ifdef __APPLE__ - mallocBytes -= malloc_size(p); + size_t s; + memcpy(&s, (char *)p - kIntMallocHeaderSize, sizeof(s)); + mallocBytes -= s; + free((char *)p - kIntMallocHeaderSize); #else - mallocBytes -= malloc_usable_size(p); -#endif -#endif free(p); +#endif } // ==================== BEGIN ARENA IMPL ==================== @@ -164,7 +166,7 @@ inline Arena::Arena(int initialSize) : impl(nullptr) { inline void onDestroy(Arena::ArenaImpl *impl) { while (impl) { auto *prev = impl->prev; - free(impl); + safe_free(impl); impl = prev; } } diff --git a/SkipList.cpp b/SkipList.cpp index cd0d6dc..7c71a80 100644 --- a/SkipList.cpp +++ b/SkipList.cpp @@ -149,7 +149,7 @@ private: setMaxVersion(level, v); } - void destroy() { free(this); } + void destroy() { safe_free(this); } private: int getNodeSize() const { @@ -655,7 +655,7 @@ ConflictSet::ConflictSet(int64_t oldestVersion) ConflictSet::~ConflictSet() { if (impl) { impl->~Impl(); - free(impl); + safe_free(impl); } }