summaryrefslogtreecommitdiffstats
path: root/vespamalloc
diff options
context:
space:
mode:
authorHenning Baldersheim <balder@yahoo-inc.com>2022-04-01 07:51:16 +0000
committerHenning Baldersheim <balder@yahoo-inc.com>2022-04-01 07:51:16 +0000
commit4e63773a4e0a0b8bb5c38d09de9c0d9b3d931f6c (patch)
tree6ebb0899bc96fa283359a89f9511017150a689f2 /vespamalloc
parentef437449ccfc1a6c170e601eee95f3ad8ed85687 (diff)
Log stacktrace when aborting in vespamalloc.
Diffstat (limited to 'vespamalloc')
-rw-r--r--vespamalloc/src/vespamalloc/malloc/common.h64
-rw-r--r--vespamalloc/src/vespamalloc/malloc/datasegment.cpp16
-rw-r--r--vespamalloc/src/vespamalloc/malloc/malloc.h2
-rw-r--r--vespamalloc/src/vespamalloc/malloc/memblockboundscheck.h2
-rw-r--r--vespamalloc/src/vespamalloc/malloc/mmappool.cpp15
-rw-r--r--vespamalloc/src/vespamalloc/malloc/threadlist.hpp8
-rw-r--r--vespamalloc/src/vespamalloc/malloc/threadpool.h4
-rw-r--r--vespamalloc/src/vespamalloc/malloc/threadpool.hpp2
-rw-r--r--vespamalloc/src/vespamalloc/malloc/threadproxy.cpp17
-rw-r--r--vespamalloc/src/vespamalloc/util/osmem.cpp36
-rw-r--r--vespamalloc/src/vespamalloc/util/osmem.h9
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 <new>
#include <atomic>
+#include <cassert>
#include <vespamalloc/util/osmem.h>
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 <size_t MinClassSizeC>
-class CommonT
-{
+
+inline constexpr int
+msbIdx(uint64_t v) {
+ return (sizeof(v) * 8 - 1) - __builtin_clzl(v);
+}
+
+template<size_t MinClassSizeC>
+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 <typename T>
-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<uint32_t> _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 <typename MemBlockPtrT, typename ThreadListT>
void MemoryManager<MemBlockPtrT, ThreadListT>::crash()
{
fprintf(stderr, "vespamalloc detected unrecoverable error.\n");
+ logStackTrace();
abort();
}
@@ -220,7 +221,6 @@ void MemoryManager<MemBlockPtrT, ThreadListT>::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<uint32_t *>(_ptr)[0] = sz;
}
void setAlignment(size_t alignment) { static_cast<uint32_t *>(_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 <vespamalloc/malloc/mmappool.h>
-#include <vespamalloc/malloc/common.h>
-#include <cassert>
+#include "mmappool.h"
+#include "common.h"
#include <sys/mman.h>
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<MemBlockPtrT, ThreadStatT>::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 <typename MemBlockPtrT, typename ThreadStatT >
void ThreadPoolT<MemBlockPtrT, ThreadStatT>::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 <dlfcn.h>
#include <pthread.h>
#include <cstdio>
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<size_t> _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<pthread_attr_t *>(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 <vespamalloc/util/osmem.h>
-#include <stdio.h>
+#include "osmem.h"
+#include <vespamalloc/malloc/common.h>
+#include <cstdio>
+#include <cassert>
+#include <cerrno>
+#include <cstdlib>
+#include <unistd.h>
#include <fcntl.h>
#include <sys/statfs.h>
#include <sys/mman.h>
#include <linux/mman.h>
-#include <algorithm>
-#include <errno.h>
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<char *>(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 <cctype>
-#include <cstdlib>
-#include <unistd.h>
-#include <cassert>
#include <cstring>
-#include <algorithm>
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; }