From 4e63773a4e0a0b8bb5c38d09de9c0d9b3d931f6c Mon Sep 17 00:00:00 2001 From: Henning Baldersheim Date: Fri, 1 Apr 2022 07:51:16 +0000 Subject: Log stacktrace when aborting in vespamalloc. --- vespamalloc/src/vespamalloc/malloc/common.h | 64 +++++++++++----------- vespamalloc/src/vespamalloc/malloc/datasegment.cpp | 16 +++--- vespamalloc/src/vespamalloc/malloc/malloc.h | 2 +- .../src/vespamalloc/malloc/memblockboundscheck.h | 2 +- vespamalloc/src/vespamalloc/malloc/mmappool.cpp | 15 +++-- vespamalloc/src/vespamalloc/malloc/threadlist.hpp | 8 +-- vespamalloc/src/vespamalloc/malloc/threadpool.h | 4 +- vespamalloc/src/vespamalloc/malloc/threadpool.hpp | 2 +- vespamalloc/src/vespamalloc/malloc/threadproxy.cpp | 17 +++--- vespamalloc/src/vespamalloc/util/osmem.cpp | 36 +++++++----- vespamalloc/src/vespamalloc/util/osmem.h | 9 +-- 11 files changed, 92 insertions(+), 83 deletions(-) diff --git a/vespamalloc/src/vespamalloc/malloc/common.h b/vespamalloc/src/vespamalloc/malloc/common.h index 892df72def4..a8bc8b102ec 100644 --- a/vespamalloc/src/vespamalloc/malloc/common.h +++ b/vespamalloc/src/vespamalloc/malloc/common.h @@ -3,6 +3,7 @@ #include #include +#include #include extern "C" void MallocRecurseOnSuspend(bool recurse) __attribute__ ((noinline)); @@ -56,34 +57,30 @@ using OSMemory = MmapMemory; using SizeClassT = int; constexpr size_t ALWAYS_REUSE_LIMIT = 0x100000ul; - -inline constexpr int msbIdx(uint64_t v) { - return (sizeof(v)*8 - 1) - __builtin_clzl(v); -} - -template -class CommonT -{ + +inline constexpr int +msbIdx(uint64_t v) { + return (sizeof(v) * 8 - 1) - __builtin_clzl(v); +} + +template +class CommonT { public: static constexpr size_t MAX_ALIGN = 0x200000ul; - enum {MinClassSize = MinClassSizeC}; + enum { + MinClassSize = MinClassSizeC + }; static constexpr SizeClassT sizeClass(size_t sz) noexcept { SizeClassT tmp(msbIdx(sz - 1) - (MinClassSizeC - 1)); - return (sz <= (1 << MinClassSizeC )) ? 0 : tmp; + return (sz <= (1 << MinClassSizeC)) ? 0 : tmp; } static constexpr size_t classSize(SizeClassT sc) noexcept { return (size_t(1) << (sc + MinClassSizeC)); } }; -inline void crash() { *((volatile unsigned *) nullptr) = 0; } - -template -inline void swap(T & a, T & b) { T tmp(a); a = b; b = tmp; } - -class Mutex -{ +class Mutex { public: Mutex() : _mutex(), _use(false) { } - ~Mutex() { quit(); } + ~Mutex() { quit(); } void lock(); void unlock(); static void addThread() { _threadCount.fetch_add(1); } @@ -94,37 +91,42 @@ public: void quit(); private: static std::atomic _threadCount; - static bool _stopRecursion; - Mutex(const Mutex & org); - Mutex & operator = (const Mutex & org); - pthread_mutex_t _mutex; - bool _use; + static bool _stopRecursion; + Mutex(const Mutex &org); + Mutex &operator=(const Mutex &org); + pthread_mutex_t _mutex; + bool _use; }; -class Guard -{ +class Guard { public: Guard(Mutex & m); - ~Guard() { _mutex->unlock(); } + ~Guard() { _mutex->unlock(); } private: - Mutex * _mutex; + Mutex *_mutex; }; -class IAllocator -{ +class IAllocator { public: virtual ~IAllocator() {} virtual bool initThisThread() = 0; virtual bool quitThisThread() = 0; virtual void enableThreadSupport() = 0; - virtual void setReturnAddressStop(const void * returnAddressStop) = 0; + virtual void setReturnAddressStop(const void *returnAddressStop) = 0; virtual size_t getMaxNumThreads() const = 0; }; void info(); -void logBigBlock(const void * ptr, size_t exact, size_t adjusted, size_t gross) __attribute__((noinline)); +void logBigBlock(const void *ptr, size_t exact, size_t adjusted, size_t gross) __attribute__((noinline)); void logStackTrace() __attribute__((noinline)); +#define ASSERT_STACKTRACE(a) { \ + if ( __builtin_expect(!(a), false) ) { \ + vespamalloc::logStackTrace(); \ + assert(a); \ + } \ +} + extern FILE * _G_logFile; extern size_t _G_bigBlockLimit; diff --git a/vespamalloc/src/vespamalloc/malloc/datasegment.cpp b/vespamalloc/src/vespamalloc/malloc/datasegment.cpp index 4c815476dab..28b69717fb5 100644 --- a/vespamalloc/src/vespamalloc/malloc/datasegment.cpp +++ b/vespamalloc/src/vespamalloc/malloc/datasegment.cpp @@ -60,7 +60,7 @@ void * DataSegment::getBlock(size_t & oldBlockSize, SizeClassT sc) size_t adjustedBlockSize = blockSize - BlockSize*(nextBlock-startBlock); newBlock = _osMemory.get(adjustedBlockSize); if (newBlock != nullptr) { - assert (newBlock == fromBlockId(nextBlock)); + ASSERT_STACKTRACE (newBlock == fromBlockId(nextBlock)); _freeList.removeLastBlock(); newBlock = fromBlockId(startBlock); _partialExtension++; @@ -70,7 +70,7 @@ void * DataSegment::getBlock(size_t & oldBlockSize, SizeClassT sc) } } else { bool result(_osMemory.reclaim(newBlock, blockSize)); - assert (result); + ASSERT_STACKTRACE (result); (void) result; } } else { @@ -83,7 +83,7 @@ void * DataSegment::getBlock(size_t & oldBlockSize, SizeClassT sc) } else if (newBlock == nullptr) { blockSize = 0; } else { - assert(blockId(newBlock)+numBlocks < BlockCount); + ASSERT_STACKTRACE(blockId(newBlock)+numBlocks < BlockCount); // assumes _osMemory.get will always return a value that does not make // "i" overflow the _blockList array; this will break when hitting the // 2T address space boundary. @@ -98,7 +98,7 @@ void * DataSegment::getBlock(size_t & oldBlockSize, SizeClassT sc) static int recurse = 0; if (recurse++ == 0) { perror("Failed extending datasegment: "); - assert(false); + ASSERT_STACKTRACE(false); } return nullptr; } @@ -106,7 +106,8 @@ void * DataSegment::getBlock(size_t & oldBlockSize, SizeClassT sc) return newBlock; } -void DataSegment::checkAndLogBigSegment() +void +DataSegment::checkAndLogBigSegment() { if (size_t(end()) >= _nextLogLimit) { fprintf(stderr, "Datasegment is growing ! Start:%p - End:%p : nextLogLimit = %lx\n", start(), end(), _nextLogLimit); @@ -121,7 +122,8 @@ void DataSegment::checkAndLogBigSegment() } } -void DataSegment::returnBlock(void *ptr) +void +DataSegment::returnBlock(void *ptr) { BlockIdT bId(blockId(ptr)); SizeClassT sc = _blockList[bId].sizeClass(); @@ -131,7 +133,7 @@ void DataSegment::returnBlock(void *ptr) if (numBlocks > _blockList[bId].realNumBlocks()) { numBlocks = _blockList[bId].realNumBlocks(); } - assert(_blockList[bId].freeChainLength() >= numBlocks); + ASSERT_STACKTRACE(_blockList[bId].freeChainLength() >= numBlocks); if ((_unmapSize < bsz) && _osMemory.release(ptr, numBlocks*BlockSize)) { for(BlockIdT i=0; i < numBlocks; i++) { BlockT & b = _blockList[bId + i]; diff --git a/vespamalloc/src/vespamalloc/malloc/malloc.h b/vespamalloc/src/vespamalloc/malloc/malloc.h index 24bfda0b840..0ecdb8c58e1 100644 --- a/vespamalloc/src/vespamalloc/malloc/malloc.h +++ b/vespamalloc/src/vespamalloc/malloc/malloc.h @@ -158,6 +158,7 @@ template void MemoryManager::crash() { fprintf(stderr, "vespamalloc detected unrecoverable error.\n"); + logStackTrace(); abort(); } @@ -220,7 +221,6 @@ void MemoryManager::freeSC(void *ptr, SizeClassT sc) tp.free(mem, sc); } else if (mem.validFree()) { fprintf(stderr, "Already deleted %p(%ld).\n", mem.ptr(), mem.size()); - // MemBlockPtrT::dumpInfo(_doubleDeleteLogLevel); crash(); } else { fprintf(stderr, "Someone has tamper with my pre/post signatures of my memoryblock %p(%ld).\n", mem.ptr(), mem.size()); diff --git a/vespamalloc/src/vespamalloc/malloc/memblockboundscheck.h b/vespamalloc/src/vespamalloc/malloc/memblockboundscheck.h index b465a4e834c..9d46dbce378 100644 --- a/vespamalloc/src/vespamalloc/malloc/memblockboundscheck.h +++ b/vespamalloc/src/vespamalloc/malloc/memblockboundscheck.h @@ -52,7 +52,7 @@ protected: void verifyFill() const __attribute__((noinline)); void setSize(size_t sz) { - assert(sz < 0x100000000ul); + ASSERT_STACKTRACE(sz < 0x100000000ul); static_cast(_ptr)[0] = sz; } void setAlignment(size_t alignment) { static_cast(_ptr)[1] = alignment; } diff --git a/vespamalloc/src/vespamalloc/malloc/mmappool.cpp b/vespamalloc/src/vespamalloc/malloc/mmappool.cpp index 34232018671..30e9985dae3 100644 --- a/vespamalloc/src/vespamalloc/malloc/mmappool.cpp +++ b/vespamalloc/src/vespamalloc/malloc/mmappool.cpp @@ -1,7 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include -#include -#include +#include "mmappool.h" +#include "common.h" #include namespace vespamalloc { @@ -17,7 +16,7 @@ MMapPool::MMapPool() } MMapPool::~MMapPool() { - assert(_mappings.empty()); + ASSERT_STACKTRACE(_mappings.empty()); } size_t @@ -37,7 +36,7 @@ MMapPool::getMmappedBytes() const { void * MMapPool::mmap(size_t sz) { void * buf(nullptr); - assert((sz & (_page_size - 1)) == 0); + ASSERT_STACKTRACE((sz & (_page_size - 1)) == 0); if (sz > 0) { const int flags(MAP_ANON | MAP_PRIVATE); const int prot(PROT_READ | PROT_WRITE); @@ -73,7 +72,7 @@ MMapPool::mmap(size_t sz) { #endif std::lock_guard guard(_mutex); auto [it, inserted] = _mappings.insert(std::make_pair(buf, MMapInfo(mmapId, sz))); - assert(inserted); + ASSERT_STACKTRACE(inserted); if (sz >= _G_bigBlockLimit) { size_t sum(0); std::for_each(_mappings.begin(), _mappings.end(), [&sum](const auto & e){ sum += e.second._sz; }); @@ -98,14 +97,14 @@ MMapPool::unmap(void * ptr) { _mappings.erase(found); } int munmap_ok = ::munmap(ptr, sz); - assert(munmap_ok == 0); + ASSERT_STACKTRACE(munmap_ok == 0); } size_t MMapPool::get_size(void * ptr) const { std::lock_guard guard(_mutex); auto found = _mappings.find(ptr); - assert(found != _mappings.end()); + ASSERT_STACKTRACE(found != _mappings.end()); return found->second._sz; } diff --git a/vespamalloc/src/vespamalloc/malloc/threadlist.hpp b/vespamalloc/src/vespamalloc/malloc/threadlist.hpp index 5f65e98d0ac..743090a4e12 100644 --- a/vespamalloc/src/vespamalloc/malloc/threadlist.hpp +++ b/vespamalloc/src/vespamalloc/malloc/threadlist.hpp @@ -75,11 +75,11 @@ bool ThreadListT::initThisThread() localId = i; } } - assert(localId >= 0); - assert(size_t(localId) < getMaxNumThreads()); + ASSERT_STACKTRACE(localId >= 0); + ASSERT_STACKTRACE(size_t(localId) < getMaxNumThreads()); _myPool = &_threadVector[localId]; - assert(getThreadId() == size_t(localId)); - assert(lidAccum < 0xffffffffu); + ASSERT_STACKTRACE(getThreadId() == size_t(localId)); + ASSERT_STACKTRACE(lidAccum < 0xffffffffu); getCurrent().init(lidAccum+1); return retval; diff --git a/vespamalloc/src/vespamalloc/malloc/threadpool.h b/vespamalloc/src/vespamalloc/malloc/threadpool.h index f49e6cf24af..30ece02ba29 100644 --- a/vespamalloc/src/vespamalloc/malloc/threadpool.h +++ b/vespamalloc/src/vespamalloc/malloc/threadpool.h @@ -54,9 +54,9 @@ private: void init(AllocPool & allocPool, SizeClassT sc) { if (_allocFrom == nullptr) { _allocFrom = allocPool.getFree(sc, 1); - assert(_allocFrom != nullptr); + ASSERT_STACKTRACE(_allocFrom != nullptr); _freeTo = allocPool.getFree(sc, 1); - assert(_freeTo != nullptr); + ASSERT_STACKTRACE(_freeTo != nullptr); } } void swap() { diff --git a/vespamalloc/src/vespamalloc/malloc/threadpool.hpp b/vespamalloc/src/vespamalloc/malloc/threadpool.hpp index 7d177c4c129..b5a283f6600 100644 --- a/vespamalloc/src/vespamalloc/malloc/threadpool.hpp +++ b/vespamalloc/src/vespamalloc/malloc/threadpool.hpp @@ -204,7 +204,7 @@ template void ThreadPoolT::init(int thrId) { setThreadId(thrId); - assert(_osThreadId.load(std::memory_order_relaxed) == -1); + ASSERT_STACKTRACE(_osThreadId.load(std::memory_order_relaxed) == -1); _osThreadId = pthread_self(); for (size_t i=0; (i < NELEMS(_memList)); i++) { _memList[i].init(*_allocPool, i); diff --git a/vespamalloc/src/vespamalloc/malloc/threadproxy.cpp b/vespamalloc/src/vespamalloc/malloc/threadproxy.cpp index c271b041d07..02eb624ee64 100644 --- a/vespamalloc/src/vespamalloc/malloc/threadproxy.cpp +++ b/vespamalloc/src/vespamalloc/malloc/threadproxy.cpp @@ -1,13 +1,14 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include "threadproxy.h" +#include "common.h" #include #include #include namespace vespamalloc { -IAllocator * _G_myMemP = NULL; +IAllocator * _G_myMemP = nullptr; void setAllocatorForThreads(IAllocator * allocator) { @@ -33,7 +34,7 @@ typedef int (*pthread_create_function) (pthread_t *thread, int linuxthreads_pthread_getattr_np(pthread_t pid, pthread_attr_t *dst); -static void * _G_mallocThreadProxyReturnAddress = NULL; +static void * _G_mallocThreadProxyReturnAddress = nullptr; static std::atomic _G_threadCount(1); // You always have the main thread. static void cleanupThread(void * arg) @@ -50,13 +51,13 @@ void * mallocThreadProxy (void * arg) ThreadArg * ta = (ThreadArg *) arg; void * tempReturnAddress = __builtin_return_address(0); - assert((_G_mallocThreadProxyReturnAddress == NULL) || (_G_mallocThreadProxyReturnAddress == tempReturnAddress)); + ASSERT_STACKTRACE((_G_mallocThreadProxyReturnAddress == nullptr) || (_G_mallocThreadProxyReturnAddress == tempReturnAddress)); _G_mallocThreadProxyReturnAddress = tempReturnAddress; vespamalloc::_G_myMemP->setReturnAddressStop(tempReturnAddress); vespamalloc::Mutex::addThread(); vespamalloc::_G_myMemP->initThisThread(); - void * result = NULL; + void * result = nullptr; DEBUG(fprintf(stderr, "arg(%p=%p), local(%p=%p)\n", &arg, arg, &ta, ta)); pthread_cleanup_push(cleanupThread, ta); @@ -95,13 +96,13 @@ local_pthread_create (pthread_t *thread, #endif } // A pointer to the library version of pthread_create. - static pthread_create_function real_pthread_create = NULL; + static pthread_create_function real_pthread_create = nullptr; const char * pthread_createName = "pthread_create"; - if (real_pthread_create == NULL) { + if (real_pthread_create == nullptr) { real_pthread_create = (pthread_create_function) dlsym (RTLD_NEXT, pthread_createName); - if (real_pthread_create == NULL) { + if (real_pthread_create == nullptr) { fprintf (stderr, "Could not find the pthread_create function!\n"); abort(); } @@ -110,7 +111,7 @@ local_pthread_create (pthread_t *thread, ThreadArg * args = new ThreadArg(start_routine, arg); pthread_attr_t locAttr; pthread_attr_t *attr(const_cast(attrOrg)); - if (attr == NULL) { + if (attr == nullptr) { pthread_attr_init(&locAttr); attr = &locAttr; } diff --git a/vespamalloc/src/vespamalloc/util/osmem.cpp b/vespamalloc/src/vespamalloc/util/osmem.cpp index bcc39074b9f..86637c53041 100644 --- a/vespamalloc/src/vespamalloc/util/osmem.cpp +++ b/vespamalloc/src/vespamalloc/util/osmem.cpp @@ -1,15 +1,25 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include -#include +#include "osmem.h" +#include +#include +#include +#include +#include +#include #include #include #include #include -#include -#include namespace vespamalloc { +Memory::Memory(size_t blockSize) + : _blockSize(std::max(blockSize, size_t(getpagesize()))), + _start(nullptr), + _end(nullptr) +{ } +Memory::~Memory() { } + void * MmapMemory::reserve(size_t & len) { @@ -17,7 +27,7 @@ MmapMemory::reserve(size_t & len) const size_t wLen(0x1000); void * wanted = get(wLen); int test = munmap(wanted, wLen); - assert( test == 0 ); + ASSERT_STACKTRACE( test == 0 ); (void) test; setStart(wanted); setEnd(getStart()); @@ -30,10 +40,10 @@ findInMemInfo(const char * wanted) size_t value(0); char memInfo[8192]; int fd(open("/proc/meminfo", O_RDONLY)); - assert(fd >= 0); + ASSERT_STACKTRACE(fd >= 0); if (fd >= 0) { int sz(read(fd, memInfo, sizeof(memInfo))); - assert((sz < int(sizeof(memInfo))) && (sz >= 0)); + ASSERT_STACKTRACE((sz < int(sizeof(memInfo))) && (sz >= 0)); memInfo[sz] = '\0'; const char * found(strstr(memInfo, wanted)); if (found != nullptr) { @@ -100,7 +110,7 @@ MmapMemory::setupHugePages() if (fd >= 0) { char mounts[8192]; int sz(read(fd, mounts, sizeof(mounts))); - assert((sz < int(sizeof(mounts))) && (sz >= 0)); + ASSERT_STACKTRACE((sz < int(sizeof(mounts))) && (sz >= 0)); (void) sz; const char * c = mounts; for (size_t lineNo(0); *c; lineNo++) { @@ -113,7 +123,7 @@ MmapMemory::setupHugePages() const char *fstype = getToken(c, e); if (strstr(fstype, "hugetlbfs") == fstype) { char mountCopy[512]; - assert(mountLen < sizeof(mountCopy)); + ASSERT_STACKTRACE(mountLen < sizeof(mountCopy)); strncpy(mountCopy, mount, mountLen); mountCopy[mountLen] = '\0'; if (verifyHugePagesMount(mountCopy)) { @@ -129,9 +139,9 @@ MmapMemory::setupHugePages() if (_hugePagesFileName[0] != '\0') { _blockSize = std::max(_blockSize, _hugePageSize); _hugePagesFd = open(_hugePagesFileName, O_CREAT | O_RDWR, 0755); - assert(_hugePagesFd >= 0); + ASSERT_STACKTRACE(_hugePagesFd >= 0); int retval(unlink(_hugePagesFileName)); - assert(retval == 0); + ASSERT_STACKTRACE(retval == 0); (void) retval; } } @@ -193,7 +203,7 @@ MmapMemory::getBasePages(size_t len, int mmapOpt, int fd, size_t offset) for (bool ok(false) ; !ok && (mem != MAP_FAILED); wanted += getBlockAlignment()) { if (mem != nullptr) { int tmp(munmap(mem, len)); - assert(tmp == 0); + ASSERT_STACKTRACE(tmp == 0); (void) tmp; } // no alignment to _blockSize needed? @@ -234,7 +244,7 @@ MmapMemory::freeTail(void * mem, size_t len) int ret(0); if ((_useMAdvLimit <= len) && (static_cast(mem) + len) == getEnd()) { ret = munmap(mem, len); - assert(ret == 0); + ASSERT_STACKTRACE(ret == 0); setEnd(mem); } return (ret == 0); diff --git a/vespamalloc/src/vespamalloc/util/osmem.h b/vespamalloc/src/vespamalloc/util/osmem.h index 52821af041e..d9f45937ab8 100644 --- a/vespamalloc/src/vespamalloc/util/osmem.h +++ b/vespamalloc/src/vespamalloc/util/osmem.h @@ -1,20 +1,15 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #pragma once -#include -#include -#include -#include #include -#include namespace vespamalloc { class Memory { public: - Memory(size_t blockSize) : _blockSize(std::max(blockSize, size_t(getpagesize()))), _start(nullptr), _end(nullptr) { } - virtual ~Memory() { } + Memory(size_t blockSize); + virtual ~Memory(); void * getStart() const { return _start; } void * getEnd() const { return _end; } size_t getMinBlockSize() const { return _blockSize; } -- cgit v1.2.3