nlib
heap/object_tracking/object_tracking.cpp

Sample that tracks an object with a memory leak and deletes it correctly.

Two 16-bit values can optionally be set. The following applications are possible.

#include <string.h>
using ::nlib_ns::heap::CentralHeap;
using ::nlib_ns::heap::CachedHeap;
const size_t heapmem_size = 1024 * 512;
NLIB_ALIGNAS(4096) static char heapmem[heapmem_size];
// All the threads allocates memory via CachedHeap from CentralHeap.
CentralHeap g_CentralHeap;
CachedHeap g_Heap;
class HeapSetup {
public:
HeapSetup() {
// Initializes CentralHeap with NMALLOC_HEAPOPTION_CHECK_1.
g_CentralHeap.Initialize(&heapmem[0], heapmem_size, NMALLOC_HEAPOPTION_CHECK_1);
}
~HeapSetup() {
g_CentralHeap.Finalize();
}
};
class Engine;
class Handle;
class Tire;
class Car;
template <class T, class HEAP_TAG>
struct Tp2Int : public ::nlib_ns::IntegralConstant<int, 0> {};
#define MY_HEAP_DEFINE_TP2INT(tp, tag, intval) \
template <> \
struct Tp2Int<tp, tag> : public ::nlib_ns::IntegralConstant<int, intval> {}
#define MY_HEAP_TP2INT(tp, tag) (Tp2Int<tp, tag>::value)
struct my_heap_tag {}; // the tag for g_CentralHeap
// Assigns a unique value to a type. 0 is for POD.
MY_HEAP_DEFINE_TP2INT(Engine, my_heap_tag, 1);
MY_HEAP_DEFINE_TP2INT(Handle, my_heap_tag, 2);
MY_HEAP_DEFINE_TP2INT(Tire, my_heap_tag, 3);
MY_HEAP_DEFINE_TP2INT(Car, my_heap_tag, 4);
class Engine {
public:
Engine() {}
~Engine() { nlib_printf(" Destroy Engine %p\n", this); }
static void* operator new(size_t n)
#ifdef NN_PLATFORM_CTR
throw()
#endif
{
void* p = g_Heap.Alloc(n, 128);
g_Heap.SetMark1(p, MY_HEAP_TP2INT(Engine, my_heap_tag));
return p;
}
static void operator delete(void* p) NLIB_NOEXCEPT {
if (p) {
nlib_printf(" Deallocate Engine %p\n", p);
g_Heap.Free(p);
}
}
// Omits other new/delete.....
};
class Handle {
public:
Handle() {}
~Handle() { nlib_printf(" Destroy Handle %p\n", this); }
static void* operator new(size_t n)
#ifdef NN_PLATFORM_CTR
throw()
#endif
{
void* p = g_Heap.Alloc(n);
g_Heap.SetMark1(p, MY_HEAP_TP2INT(Handle, my_heap_tag));
return p;
}
static void operator delete(void* p) NLIB_NOEXCEPT {
if (p) {
nlib_printf(" Deallocate Handle %p\n", p);
g_Heap.Free(p);
}
}
// Omits other new/delete.....
};
class Tire {
public:
Tire() {}
~Tire() { nlib_printf(" Destroy Tire %p\n", this); }
static void* operator new(size_t n)
#ifdef NN_PLATFORM_CTR
throw()
#endif
{
void* p = g_Heap.Alloc(n);
g_Heap.SetMark1(p, MY_HEAP_TP2INT(Tire, my_heap_tag));
return p;
}
static void operator delete(void* p) NLIB_NOEXCEPT {
if (p) {
nlib_printf(" Deallocate Tire %p\n", p);
g_Heap.Free(p);
}
}
// Omits other new/delete.....
};
class Car {
public:
Car() : m_Name(NULL), m_Engine(NULL), m_Handle(NULL) {
m_Tire[0] = m_Tire[1] = m_Tire[2] = m_Tire[3] = NULL;
}
~Car() {
nlib_printf(" Destroy Car %p\n", this);
// Leaks m_Name, m_Engine, m_Handle, m_Tire[0-3].
}
static void* operator new(size_t n)
#ifdef NN_PLATFORM_CTR
throw()
#endif
{
void* p = g_Heap.Alloc(n);
g_Heap.SetMark1(p, MY_HEAP_TP2INT(Car, my_heap_tag));
return p;
}
static void operator delete(void* p) NLIB_NOEXCEPT {
if (p) {
nlib_printf(" Deallocate Car %p\n", p);
g_Heap.Free(p);
}
}
void SetName(const char* name) {
size_t buflen = strlen(name) + 1;
m_Name = reinterpret_cast<char*>(g_Heap.Alloc(buflen));
g_Heap.SetMark2(m_Name, __LINE__);
nlib_strcpy(m_Name, buflen, name);
}
void SetEngine(Engine* p) { m_Engine = p; }
void SetHandle(Handle* p) { m_Handle = p; }
void SetTire(int i, Tire* p) { m_Tire[i] = p; }
private:
char* m_Name;
Engine* m_Engine;
Handle* m_Handle;
Tire* m_Tire[4];
};
#define MYCASE_TYPENAME(tp) \
case Tp2Int<tp, my_heap_tag>::value : return #tp
static const char* GetTypeName(uint16_t mark1) {
switch (mark1) {
MYCASE_TYPENAME(Engine);
MYCASE_TYPENAME(Handle);
MYCASE_TYPENAME(Tire);
MYCASE_TYPENAME(Car);
default:
return "POD or UNKNOWN";
}
}
static int print_info(void* ptr, size_t size, void*) {
uint16_t mark1, mark2;
void* objPtr = g_Heap.GetObjPtr(ptr);
g_Heap.GetMark(objPtr, &mark1, &mark2);
nlib_printf("ptr = %p, size = %" PRIuS ", typename = %s, __LINE__ = %d\n", ptr, size,
GetTypeName(mark1), mark2);
return 1;
}
#define MYCASE_DELETE(tp) \
case Tp2Int<tp, my_heap_tag>::value : delete reinterpret_cast<tp*>(objPtr); \
break
static int auto_delete(void* ptr, size_t, void*) {
uint16_t mark1, mark2;
void* objPtr = g_Heap.GetObjPtr(ptr);
g_Heap.GetMark(objPtr, &mark1, &mark2);
nlib_printf("deleting %s allocated at line %d:\n", GetTypeName(mark1), mark2);
switch (mark1) {
MYCASE_DELETE(Engine);
MYCASE_DELETE(Handle);
MYCASE_DELETE(Tire);
MYCASE_DELETE(Car);
default:
g_Heap.Free(objPtr);
}
return 0;
}
static void CarSample() {
Car* car = new Car();
g_Heap.SetMark2(car, __LINE__);
car->SetName("!!!!! My Super Car !!!!!");
{
Engine* engine = new Engine();
g_Heap.SetMark2(engine, __LINE__);
car->SetEngine(engine);
Handle* handle = new Handle();
g_Heap.SetMark2(handle, __LINE__);
car->SetHandle(handle);
Tire* tire1 = new Tire();
g_Heap.SetMark2(tire1, __LINE__);
car->SetTire(0, tire1);
Tire* tire2 = new Tire();
g_Heap.SetMark2(tire2, __LINE__);
car->SetTire(1, tire2);
Tire* tire3 = new Tire();
g_Heap.SetMark2(tire3, __LINE__);
car->SetTire(2, tire3);
Tire* tire4 = new Tire();
g_Heap.SetMark2(tire4, __LINE__);
car->SetTire(3, tire4);
}
nlib_printf("\n***** Show allocated ptrs *****\n");
g_Heap.ReleaseAllCache();
g_CentralHeap.WalkAllocatedPtrs(print_info, NULL);
nlib_printf("\n***** delete car *****\n");
delete car; // Memory leaks here.
}
static bool SampleMain(int, char**) {
HeapSetup obj;
{
if (!g_CentralHeap.MakeCache(&g_Heap)) {
return false;
}
CarSample();
nlib_printf("\n***** Deleting undeleted objects *****\n");
// You can delete the leaked objects with the correct destructors.
for (;;) {
g_Heap.ReleaseAllCache();
if (0 != g_CentralHeap.WalkAllocatedPtrs(auto_delete, NULL)) break;
}
}
return true;
}
NLIB_MAINFUNC