mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
322 lines
6.4 KiB
C++
322 lines
6.4 KiB
C++
#include "Core.h"
|
|
|
|
// #define LOGAF
|
|
|
|
namespace Upp {
|
|
|
|
bool NoMemoryLeaksCheck;
|
|
|
|
static bool sIgnoreNonMainLeaks;
|
|
static bool sIgnoreNonUppThreadsLeaks;
|
|
|
|
static dword serial_number = 0;
|
|
static dword serial_main_begin;
|
|
static dword serial_main_end;
|
|
|
|
dword MemoryGetCurrentSerial() { return serial_number; }
|
|
|
|
void MemoryIgnoreNonMainLeaks()
|
|
{ // ignore leaks outside _APP_MAIN
|
|
sIgnoreNonMainLeaks = true;
|
|
}
|
|
|
|
void MemoryIgnoreNonUppThreadsLeaks()
|
|
{ // ignore leaks in threads not launched by U++ Thread
|
|
sIgnoreNonUppThreadsLeaks = true;
|
|
}
|
|
|
|
void MemorySetMainBegin__()
|
|
{
|
|
serial_main_begin = serial_number;
|
|
}
|
|
|
|
void MemorySetMainEnd__()
|
|
{
|
|
serial_main_end = serial_number;
|
|
}
|
|
|
|
};
|
|
|
|
#if (defined(TESTLEAKS) || defined(HEAPDBG)) && defined(COMPILER_GCC) && defined(UPP_HEAP)
|
|
|
|
int sMemDiagInitCount;
|
|
|
|
#endif
|
|
|
|
namespace Upp {
|
|
|
|
extern bool AppNormalExit;
|
|
|
|
#if defined(UPP_HEAP)
|
|
|
|
#include "HeapImp.h"
|
|
|
|
#if defined(HEAPDBG)
|
|
|
|
extern bool PanicMode;
|
|
void HeapPanic(const char *text, void *pos, int size);
|
|
|
|
static StaticMutex sHeapLock2;
|
|
|
|
struct alignas(16) DbgBlkHeader {
|
|
size_t size;
|
|
DbgBlkHeader *prev;
|
|
DbgBlkHeader *next;
|
|
dword serial;
|
|
|
|
void LinkSelf() {
|
|
next = prev = this;
|
|
}
|
|
void Unlink() {
|
|
prev->next = next;
|
|
next->prev = prev;
|
|
}
|
|
void Insert(DbgBlkHeader *lnk) {
|
|
lnk->prev = this;
|
|
lnk->next = next;
|
|
next->prev = lnk;
|
|
next = lnk;
|
|
}
|
|
};
|
|
|
|
static const char *DbgFormat(char *b, DbgBlkHeader *p)
|
|
{
|
|
snprintf(b, 100, "--memory-breakpoint__ %u ", (unsigned int)~(p->serial ^ (uintptr_t)p));
|
|
return b;
|
|
}
|
|
|
|
static void DbgHeapPanic(const char *text, DbgBlkHeader *p)
|
|
{
|
|
char h[256];
|
|
char b[100];
|
|
strcpy(h, text);
|
|
strcat(h, DbgFormat(b, p));
|
|
HeapPanic(h, p + 1, (int)(uintptr_t)p->size);
|
|
}
|
|
|
|
static DbgBlkHeader dbg_live = { 0, &dbg_live, &dbg_live, 0 };
|
|
|
|
static dword s_allocbreakpoint;
|
|
static thread_local dword s_ignoreleaks;
|
|
|
|
void MemoryIgnoreLeaksBegin()
|
|
{
|
|
Mutex::Lock __(sHeapLock2);
|
|
s_ignoreleaks++;
|
|
}
|
|
|
|
void MemoryIgnoreLeaksEnd()
|
|
{
|
|
Mutex::Lock __(sHeapLock2);
|
|
s_ignoreleaks--;
|
|
}
|
|
|
|
void MemoryBreakpoint(dword serial)
|
|
{
|
|
s_allocbreakpoint = serial;
|
|
}
|
|
|
|
void *MemoryAllocSz_(size_t& size);
|
|
|
|
void DbgSet(DbgBlkHeader *p, size_t size)
|
|
{
|
|
bool allow_leak = s_ignoreleaks ||
|
|
sIgnoreNonUppThreadsLeaks && !Thread::IsUpp() && !Thread::IsMain()
|
|
#if (defined(TESTLEAKS) || defined(HEAPDBG)) && defined(COMPILER_GCC) && defined(UPP_HEAP)
|
|
|| sMemDiagInitCount == 0
|
|
#endif
|
|
;
|
|
|
|
p->serial = allow_leak ? 0 : ~ ++serial_number ^ (dword)(uintptr_t) p;
|
|
p->size = size;
|
|
if(s_allocbreakpoint && s_allocbreakpoint == serial_number)
|
|
__BREAK__;
|
|
dbg_live.Insert(p);
|
|
Poke32le((byte *)(p + 1) + p->size, p->serial);
|
|
}
|
|
|
|
void *MemoryAllocSz(size_t& size)
|
|
{
|
|
if(PanicMode)
|
|
return malloc(size);
|
|
Mutex::Lock __(sHeapLock2);
|
|
size += sizeof(DbgBlkHeader) + sizeof(dword);
|
|
DbgBlkHeader *p = (DbgBlkHeader *)MemoryAllocSz_(size);
|
|
size -= sizeof(DbgBlkHeader) + sizeof(dword);
|
|
DbgSet(p, size);
|
|
#ifdef LOGAF
|
|
char h[200];
|
|
sprintf(h, "ALLOCATED %d at %p - %p", size, p + 1, (byte *)(p + 1) + size);
|
|
DLOG(h);
|
|
#endif
|
|
return p + 1;
|
|
}
|
|
|
|
void *MemoryAlloc(size_t size)
|
|
{
|
|
return MemoryAllocSz(size);
|
|
}
|
|
|
|
void MemoryFree_(void *ptr);
|
|
|
|
void DbgCheck(DbgBlkHeader *p)
|
|
{
|
|
if((dword)Peek32le((byte *)(p + 1) + p->size) != p->serial)
|
|
DbgHeapPanic("Heap is corrupted ", p);
|
|
}
|
|
|
|
void MemoryFree(void *ptr)
|
|
{
|
|
#ifdef LOGAF
|
|
char h[200];
|
|
sprintf(h, "FREE %p", ptr);
|
|
DLOG(h);
|
|
#endif
|
|
if(PanicMode)
|
|
return;
|
|
if(!ptr) return;
|
|
Mutex::Lock __(sHeapLock2);
|
|
DbgBlkHeader *p = (DbgBlkHeader *)ptr - 1;
|
|
DbgCheck(p);
|
|
p->Unlink();
|
|
MemoryFree_(p);
|
|
}
|
|
|
|
bool MemoryTryRealloc_(void *ptr, size_t& newsize);
|
|
|
|
bool MemoryTryRealloc__(void *ptr, size_t& newsize)
|
|
{
|
|
if(!ptr || PanicMode) return false;
|
|
Mutex::Lock __(sHeapLock2);
|
|
DbgBlkHeader *p = (DbgBlkHeader *)ptr - 1;
|
|
DbgCheck(p);
|
|
size_t sz = newsize;
|
|
sz += sizeof(DbgBlkHeader) + sizeof(dword);
|
|
if(MemoryTryRealloc_((DbgBlkHeader *)ptr - 1, sz)) {
|
|
newsize = sz - sizeof(DbgBlkHeader) - sizeof(dword);
|
|
p->Unlink();
|
|
DbgSet(p, newsize);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
size_t GetMemoryBlockSize_(void *ptr);
|
|
|
|
size_t GetMemoryBlockSize(void *ptr)
|
|
{
|
|
if(!ptr) return 0;
|
|
return ((DbgBlkHeader *)ptr - 1)->size;
|
|
}
|
|
|
|
void *MemoryAlloc32() { return MemoryAlloc(32); }
|
|
void MemoryFree32(void *ptr) { return MemoryFree(ptr); }
|
|
|
|
void MemoryCheckDebug()
|
|
{
|
|
if(PanicMode)
|
|
return;
|
|
MemoryCheck();
|
|
Mutex::Lock __(sHeapLock2);
|
|
DbgBlkHeader *p = dbg_live.next;
|
|
while(p != &dbg_live) {
|
|
if((dword)Peek32le((byte *)(p + 1) + p->size) != p->serial)
|
|
DbgHeapPanic("HEAP CHECK: Heap is corrupted ", p);
|
|
p = p->next;
|
|
}
|
|
while(p != &dbg_live);
|
|
}
|
|
|
|
void MemoryDumpLeaks()
|
|
{
|
|
if(PanicMode || NoMemoryLeaksCheck)
|
|
return;
|
|
#ifdef PLATFORM_MACOS
|
|
return; // ignore leaks in macos
|
|
#endif
|
|
if(IsMainRunning()) {
|
|
VppLog() << "Application was terminated in a non-standard way (e.g. exit(x) call or Ctrl+C)\n";
|
|
}
|
|
#ifndef PLATFORM_POSIX
|
|
if(s_ignoreleaks)
|
|
Panic("Ignore leaks Begin/End mismatch!");
|
|
#endif
|
|
MemoryCheckDebug();
|
|
DbgBlkHeader *p = dbg_live.next;
|
|
bool leaks = false;
|
|
int n = 0;
|
|
while(p != &dbg_live) {
|
|
dword serial = (unsigned int)~(p->serial ^ (uintptr_t)p);
|
|
if(p->serial && (!sIgnoreNonMainLeaks || serial >= serial_main_begin && serial < serial_main_end)) {
|
|
if(!leaks)
|
|
VppLog() << "\n\nHeap leaks detected:\n";
|
|
leaks = true;
|
|
char b[100];
|
|
DbgFormat(b, p);
|
|
VppLog() << '\n' << b << ": ";
|
|
HexDump(VppLog(), p + 1, (int)(uintptr_t)p->size, 64);
|
|
if(++n > 16) {
|
|
while(p->next != &dbg_live && n < 10000000) {
|
|
++n;
|
|
p = p->next;
|
|
}
|
|
VppLog() << "\n*** TOO MANY LEAKS (" << n << ") TO LIST THEM ALL\n";
|
|
break;
|
|
}
|
|
}
|
|
p = p->next;
|
|
}
|
|
if(!leaks)
|
|
return;
|
|
#ifdef PLATFORM_WIN32
|
|
MessageBox(::GetActiveWindow(),
|
|
"Heap leaks detected !",
|
|
"Warning",
|
|
MB_ICONSTOP|MB_OK|MB_APPLMODAL);
|
|
#else
|
|
if(!IsPanicMode())
|
|
Panic("Heap leaks detected!");
|
|
#endif
|
|
Heap::AuxFinalCheck();
|
|
}
|
|
|
|
#ifdef COMPILER_MSC
|
|
|
|
#pragma warning(disable: 4074)
|
|
#pragma init_seg(compiler)
|
|
|
|
EXITBLOCK { MemoryDumpLeaks(); }
|
|
|
|
#endif
|
|
|
|
#ifdef COMPILER_GCC
|
|
|
|
void MemoryInitDiagnostics()
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
#if (defined(TESTLEAKS) || defined(HEAPDBG)) && defined(COMPILER_GCC) && defined(UPP_HEAP)
|
|
|
|
MemDiagCls::MemDiagCls()
|
|
{
|
|
if(sMemDiagInitCount++ == 0)
|
|
UPP::MemoryInitDiagnostics();
|
|
}
|
|
|
|
MemDiagCls::~MemDiagCls()
|
|
{
|
|
if(--sMemDiagInitCount == 0)
|
|
UPP::MemoryDumpLeaks();
|
|
}
|
|
|
|
static const MemDiagCls sMemDiagHelper __attribute__ ((init_priority (101)));
|
|
|
|
#endif
|