From d8d16229d11eef2cda77d700bb48091682f328ea Mon Sep 17 00:00:00 2001 From: Mirek Fidler Date: Sun, 19 Dec 2021 14:53:30 +0100 Subject: [PATCH] Fixed reporting leaks for GLCtrl in Linux with Radeon driver, new memory leaks detection related functions. --- uppsrc/Core/App.cpp | 5 +++ uppsrc/Core/Heap.h | 5 +++ uppsrc/Core/Mt.cpp | 8 +++++ uppsrc/Core/Mt.h | 1 + uppsrc/Core/heapdbg.cpp | 47 ++++++++++++++++++++++++---- uppsrc/Core/src.tpp/Heap_en-us.tpp | 41 ++++++++++++++++++++++-- uppsrc/Core/src.tpp/Thread_en-us.tpp | 9 ++++-- uppsrc/GLCtrl/GLCtrl.cpp | 2 ++ uppsrc/GLCtrl/XGLCtrl.cpp | 2 ++ 9 files changed, 110 insertions(+), 10 deletions(-) diff --git a/uppsrc/Core/App.cpp b/uppsrc/Core/App.cpp index 37bd0299f..b2a653a11 100644 --- a/uppsrc/Core/App.cpp +++ b/uppsrc/Core/App.cpp @@ -440,10 +440,15 @@ void Exit(int code) throw ExitExc(); } +void MemorySetMainBegin__(); +void MemorySetMainEnd__(); + void AppExecute__(void (*app)()) { try { + MemorySetMainBegin__(); (*app)(); + MemorySetMainEnd__(); } catch(ExitExc) { return; diff --git a/uppsrc/Core/Heap.h b/uppsrc/Core/Heap.h index e1554b627..8a818f08c 100644 --- a/uppsrc/Core/Heap.h +++ b/uppsrc/Core/Heap.h @@ -170,6 +170,11 @@ inline void TinyFree(int, void *ptr) { return MemoryFree(ptr); } #endif +dword MemoryGetCurrentSerial(); + +void MemoryIgnoreNonMainLeaks(); +void MemoryIgnoreNonUppThreadsLeaks(); + struct MemoryIgnoreLeaksBlock { MemoryIgnoreLeaksBlock() { MemoryIgnoreLeaksBegin(); } ~MemoryIgnoreLeaksBlock() { MemoryIgnoreLeaksEnd(); } diff --git a/uppsrc/Core/Mt.cpp b/uppsrc/Core/Mt.cpp index a94ae03f6..d94e88056 100644 --- a/uppsrc/Core/Mt.cpp +++ b/uppsrc/Core/Mt.cpp @@ -69,6 +69,8 @@ struct sThreadParam { bool noshutdown; }; +static thread_local bool sUppThread = false; + static #ifdef PLATFORM_WIN32 #ifdef CPU_64 @@ -82,6 +84,7 @@ static sThreadRoutine(void *arg) { LLOG("sThreadRoutine"); + sUppThread = true; auto p = (sThreadParam *)arg; try { p->cb(); @@ -191,6 +194,11 @@ Thread::~Thread() #endif } +bool Thread::IsUpp() +{ + return sUppThread; +} + bool Thread::IsST() //the containing thread (of wich there may be multiple) has not run its Run() yet { return !threadr; diff --git a/uppsrc/Core/Mt.h b/uppsrc/Core/Mt.h index 934198815..34ed72490 100644 --- a/uppsrc/Core/Mt.h +++ b/uppsrc/Core/Mt.h @@ -80,6 +80,7 @@ public: static bool IsST(); static bool IsMain(); + static bool IsUpp(); static int GetCount(); static void BeginShutdownThreads(); static void EndShutdownThreads(); diff --git a/uppsrc/Core/heapdbg.cpp b/uppsrc/Core/heapdbg.cpp index 6b14d2073..cf7322bd5 100644 --- a/uppsrc/Core/heapdbg.cpp +++ b/uppsrc/Core/heapdbg.cpp @@ -2,6 +2,39 @@ // #define LOGAF +namespace Upp { + +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; @@ -85,12 +118,14 @@ void *MemoryAllocSz_(size_t& size); void DbgSet(DbgBlkHeader *p, size_t size) { - static dword serial_number = 0; + bool allow_leak = s_ignoreleaks || + sIgnoreNonUppThreadsLeaks && !Thread::IsUpp() && !Thread::IsMain() #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; + || 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__; @@ -209,7 +244,8 @@ void MemoryDumpLeaks() bool leaks = false; int n = 0; while(p != &dbg_live) { - if(p->serial) { + 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; @@ -222,7 +258,6 @@ void MemoryDumpLeaks() ++n; p = p->next; } - sprintf(b, "%d", n); VppLog() << "\n*** TOO MANY LEAKS (" << n << ") TO LIST THEM ALL\n"; break; } diff --git a/uppsrc/Core/src.tpp/Heap_en-us.tpp b/uppsrc/Core/src.tpp/Heap_en-us.tpp index efb4c5ced..4399c48fb 100644 --- a/uppsrc/Core/src.tpp/Heap_en-us.tpp +++ b/uppsrc/Core/src.tpp/Heap_en-us.tpp @@ -63,7 +63,7 @@ is slightly faster.&] [s2;%% Attempts to change the size of block at [%-*@3 ptr] to something closer to [%-*@3 newsize]. The real value is returned in [%-*@3 newsize]. Returns true on success.&] -[s3;%% &] +[s3; &] [s4; &] [s5;:Upp`:`:TinyAlloc`(int`): [@(0.0.255) void]_`*[* TinyAlloc]([@(0.0.255) int]_[*@3 size])&] [s2;%% Allocates the block of [%-*@3 size] bytes. This allows for allocation @@ -76,7 +76,7 @@ TinyFree.&] e], [@(0.0.255) void]_`*[*@3 ptr])&] [s2;%% Frees the block allocated with TinyAlloc, [%-*@3 size] has to be the same as during allocation.&] -[s3;%% &] +[s3; &] [s4; &] [s5;:Upp`:`:tiny`_new`(Args`.`.`.args`): [@(0.0.255) template]_<[@(0.0.255) class]_[*@4 T], [@(0.0.255) class...]_[*@4 Args]>_[*@4 T]_`*[* tiny`_new]([*@4 Args][@(0.0.255) ...]_args)&] @@ -122,6 +122,43 @@ b])&] [s2;%% This debug / diagnostics function limits memory usage to [%-*@3 kb] KBs. If the application allocates more, it stops with error.&] [s3;%% &] +[s4; &] +[s5;:Upp`:`:MemoryGetCurrentSerial`(`): [_^Upp`:`:dword^ dword]_[* MemoryGetCurrentSerial +]()&] +[s2;%% In debug mode, returns the serial number of the next allocated +block. This number is eventually listed in the log in case there +are any leaks.&] +[s3; &] +[s4; &] +[s5;:Upp`:`:MemoryIgnoreNonMainLeaks`(`): [@(0.0.255) void]_[* MemoryIgnoreNonMainLeaks]( +)&] +[s2;%% Makes leaks detector ignore leaks by global constructors.&] +[s3; &] +[s4; &] +[s5;:Upp`:`:MemoryIgnoreNonUppThreadsLeaks`(`): [@(0.0.255) void]_[* MemoryIgnoreNonUppTh +readsLeaks]()&] +[s2;%% Makes leaks dectector ignore leaks created by threads that +are not launched by U`+`+ Thread class.&] +[s3; &] +[s4; &] +[s5;:Upp`:`:MemoryIgnoreLeaksBegin`(`): [@(0.0.255) void]_[* MemoryIgnoreLeaksBegin]()&] +[s2;%% Makes leak detector ignore leaks of blocks allocated until +[%-* MemoryIgnoreLeaksEnd ]is called. Calls can be nested. This +is especially useful when working with 3rd party code that might +e.g. create static leaks (memory is allocated just once, but +library does not bother to deallocate on exit).&] +[s3; &] +[s4; &] +[s5;:Upp`:`:MemoryIgnoreLeaksEnd`(`): [@(0.0.255) void]_[* MemoryIgnoreLeaksEnd]()&] +[s2;%% Ends the suppression started by MemoryIgnoreLeaksBegin.&] +[s3;%% &] +[s4; &] +[s1;:Upp`:`:MemoryIgnoreLeaksBlock`:`:struct: [@(0.0.255) struct]_[* MemoryIgnoreLeaksBlo +ck]&] +[s2;%% This helper class calls [%-* MemoryIgnoreLeaksBegin] in constructor +and [%-* MemoryIgnoreLeaksEnd], in other works supresses leaks +till the end of block.&] +[s3;%% &] [ {{10000F(128)G(128)@1 [s0;%% [* Heap tuning]]}}&] [s0;%% &] [s0;%% Heap tuning is provided through MemoryOptions class. Constructor diff --git a/uppsrc/Core/src.tpp/Thread_en-us.tpp b/uppsrc/Core/src.tpp/Thread_en-us.tpp index c1d37745b..9b834b9eb 100644 --- a/uppsrc/Core/src.tpp/Thread_en-us.tpp +++ b/uppsrc/Core/src.tpp/Thread_en-us.tpp @@ -91,14 +91,19 @@ t]_[*@3 ms])&] [s3;%- &] [s4;%- &] [s5;:Thread`:`:IsST`(`):%- [@(0.0.255) static] [@(0.0.255) bool]_[* IsST]()&] -[s2; No additional thread was started yet (only the main thread is -running so far).&] +[s2; No additional U`+`+ Thread was started yet (only the main thread +is running so far).&] [s3;%- &] [s4;%- &] [s5;:Thread`:`:IsMain`(`):%- [@(0.0.255) static] [@(0.0.255) bool]_[* IsMain]()&] [s2; Returns true if current thread is main.&] [s3;%- &] [s4;%- &] +[s5;:Upp`:`:Thread`:`:IsUpp`(`):%- [@(0.0.255) static] [@(0.0.255) bool]_[* IsUpp]()&] +[s2; Calling thread was started using U`+`+ Thread class (for main +thread returns false).&] +[s3;%- &] +[s4;%- &] [s5;:Thread`:`:GetCount`(`):%- [@(0.0.255) static] [@(0.0.255) int]_[* GetCount]()&] [s2; Number of running threads (started using Thread class).&] [s3;%- &] diff --git a/uppsrc/GLCtrl/GLCtrl.cpp b/uppsrc/GLCtrl/GLCtrl.cpp index 44961cde5..08d230b26 100644 --- a/uppsrc/GLCtrl/GLCtrl.cpp +++ b/uppsrc/GLCtrl/GLCtrl.cpp @@ -32,6 +32,8 @@ void GLCtrl::Init() Add(pane.SizePos()); #endif restore_gl_viewport__ = SetCurrentViewport; + MemoryIgnoreNonMainLeaks(); + MemoryIgnoreNonUppThreadsLeaks(); // Linux drivers leak memory in threads } Image GLCtrl::MouseEvent(int event, Point p, int zdelta, dword keyflags) diff --git a/uppsrc/GLCtrl/XGLCtrl.cpp b/uppsrc/GLCtrl/XGLCtrl.cpp index cf625cc07..799429f81 100644 --- a/uppsrc/GLCtrl/XGLCtrl.cpp +++ b/uppsrc/GLCtrl/XGLCtrl.cpp @@ -90,6 +90,8 @@ void GLCtrl::Create() void GLCtrl::Sync() { + MemoryIgnoreLeaksBlock __; + if(win) { Rect r = GetScreenView() - GetTopCtrl()->GetScreenRect().TopLeft(); bool b = IsVisible() && r.GetWidth() > 0 && r.GetHeight() > 0;