mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 06:05:58 -06:00
This might bring some incompatibilities in the code that expects wchar to be 16 bit, which escpecially involves dealing with Win32 (and to lesser extend MacOS) APIs, so if your application is doing that, please check all instances of WCHAR (UniChar on MacOS) or even wchar especially type casts. To support host APIs, char16 is introduced (but there is no 16-bit String varian). Use ToSystemCharsetW, FromSystemCharsetW to convert texts to Win32 API. - Support of drawing non-BMP characters in GUI - Vastly improved character font replacement code (when drawing characters missing with requested font, replacement font is used) - Last instances of Win32 ANSI calls (those ending with A) are removed - UTF handling routines are refactored and their's naming is unified - RTF is now being able to handle non-BMP characters (RTF is used as clipboard format for RichText) Other minor changes: - fixed TryRealloc issue - improved MemoryCheck - Removed MemoryAlloc48/MemoryFree48 - In theide Background parsing should less often cause delays in the main thread
285 lines
5.6 KiB
C++
285 lines
5.6 KiB
C++
#include "Core.h"
|
|
|
|
// #define LOGAF
|
|
|
|
#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)
|
|
{
|
|
sprintf(b, "--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)
|
|
{
|
|
static dword serial_number = 0;
|
|
#if (defined(TESTLEAKS) || defined(HEAPDBG)) && defined(COMPILER_GCC) && defined(UPP_HEAP)
|
|
p->serial = sMemDiagInitCount == 0 || s_ignoreleaks ? 0 : ~ ++serial_number ^ (dword)(uintptr_t) p;
|
|
#else
|
|
p->serial = s_ignoreleaks ? 0 : ~ ++serial_number ^ (dword)(uintptr_t) p;
|
|
#endif
|
|
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)
|
|
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) {
|
|
if(p->serial) {
|
|
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;
|
|
}
|
|
sprintf(b, "%d", n);
|
|
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
|