Track call sites of QMacAutoReleasePools for debugging pool usage

Printing pools by calling [NSAutoreleasePool showPools] will now give
a more detailed view of where the various pools in the stack were
created, eg:

AUTORELEASE POOLS for thread 0x1000be5c0
17 releases pending.
[0x107802000]  ................  PAGE  (hot) (cold)
[0x107802038]  ################  POOL 0x107802038
[0x107802040]       0x107791c70    ^-- allocated in function: main
[0x107802048]  ################  POOL 0x107802048
[0x107802050]       0x1073b2e80    ^-- allocated in function: QCocoaWindow::initialize()
[0x107802058]       0x107111ed0  NSCompositeAppearance
[0x107802060]       0x107111ed0  NSCompositeAppearance
[0x107802068]       0x107111ed0  NSCompositeAppearance
[0x107802070]       0x1073bbe10  __NSCFString
[0x107802078]       0x1073bbde0  _NSViewBackingLayer
[0x107802080]       0x1073bc100  NSWeakObjectValue
[0x107802088]       0x1073bbe40  QNSView
[0x107802090]       0x1073bbe40  QNSView
[0x107802098]       0x107111ed0  NSCompositeAppearance
[0x1078020a0]       0x107111ed0  NSCompositeAppearance
[0x1078020a8]       0x1073bbe40  QNSView
[0x1078020b0]  ################  POOL 0x1078020b0
[0x1078020b8]       0x1073bbe30    ^-- allocated in function: QCocoaWindow::recreateWindowIfNeeded()

Change-Id: I97faf30db5835fea2f05320435b1b8c334a478d1
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
Tor Arne Vestbø 2019-02-12 13:02:48 +01:00
parent d6c474b49b
commit 6c18f86af7

View File

@ -48,6 +48,11 @@
#include <UIKit/UIKit.h>
#endif
#include <execinfo.h>
#include <dlfcn.h>
#include <cxxabi.h>
#include <objc/runtime.h>
#include <qdebug.h>
QT_BEGIN_NAMESPACE
@ -127,22 +132,54 @@ QT_USE_NAMESPACE
}
@end
QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAutoReleasePoolTracker);
QT_BEGIN_NAMESPACE
/*
Manages a scoped auto-release pool.
To track autoreleases without any pools in place, such as in main()
before the runloop has started, export OBJC_DEBUG_MISSING_POOLS=YES
and break in objc_autoreleaseNoPool, e.g.:
br set -n objc_autoreleaseNoPool -c "[((NSObject*)$r14) class] == [QNSWindow class]"
*/
QMacAutoReleasePool::QMacAutoReleasePool()
: pool([[NSAutoreleasePool alloc] init])
{
[[[QMacAutoReleasePoolTracker alloc] initWithPool:
Class trackerClass = [QMacAutoReleasePoolTracker class];
#ifdef QT_DEBUG
void *poolFrame = nullptr;
if (__builtin_available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 5.0, *)) {
void *frame;
if (backtrace_from_fp(__builtin_frame_address(0), &frame, 1))
poolFrame = frame;
} else {
static const int maxFrames = 3;
void *callstack[maxFrames];
if (backtrace(callstack, maxFrames) == maxFrames)
poolFrame = callstack[maxFrames - 1];
}
if (poolFrame) {
Dl_info info;
if (dladdr(poolFrame, &info) && info.dli_sname) {
const char *symbolName = info.dli_sname;
if (symbolName[0] == '_') {
int status;
if (char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, 0, &status))
symbolName = demangled;
}
char *className = nullptr;
asprintf(&className, " ^-- allocated in function: %s", symbolName);
if (Class existingClass = objc_getClass(className))
trackerClass = existingClass;
else
trackerClass = objc_duplicateClass(trackerClass, className, 0);
free(className);
if (symbolName != info.dli_sname)
free((char*)symbolName);
}
}
#endif
[[[trackerClass alloc] initWithPool:
reinterpret_cast<NSAutoreleasePool **>(&pool)] autorelease];
}