File indexing completed on 2024-05-12 15:56:58
0001 /* 0002 * SPDX-FileCopyrightText: 2010 Cyrille Berger <cberger@cberger.net> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 #include "kis_memory_leak_tracker.h" 0008 0009 #include <QMutex> 0010 #include <QGlobalStatic> 0011 0012 #include "kis_debug.h" 0013 0014 // Those defines are used to ignore classes that are often leaked due to a KisPaintDevice leak 0015 #define IGNORE_MEMENTO_ITEM 0016 #define IGNORE_TILE 0017 0018 Q_GLOBAL_STATIC(KisMemoryLeakTracker, s_instance) 0019 0020 // Common function 0021 KisMemoryLeakTracker* KisMemoryLeakTracker::instance() 0022 { 0023 return s_instance; 0024 } 0025 #ifdef HAVE_MEMORY_LEAK_TRACKER 0026 0027 #include <QHash> 0028 0029 #ifdef Q_OS_LINUX 0030 0031 struct BacktraceInfo { 0032 BacktraceInfo() : trace(0), size(0) { 0033 } 0034 ~BacktraceInfo() { 0035 delete[] trace; 0036 } 0037 void** trace; 0038 int size; 0039 }; 0040 0041 #define BACKTRACE_SIZE 10 0042 0043 #include <execinfo.h> 0044 0045 #ifdef HAVE_BACKTRACE_SUPPORT 0046 #define MAKE_BACKTRACEINFO \ 0047 BacktraceInfo* info = new BacktraceInfo; \ 0048 info->trace = new void*[BACKTRACE_SIZE]; \ 0049 int n = backtrace(info->trace, BACKTRACE_SIZE); \ 0050 info->size = n; 0051 #else 0052 #define MAKE_BACKTRACEINFO \ 0053 BacktraceInfo* info = 0; 0054 #endif 0055 0056 struct WhatInfo { 0057 QHash<const void*, BacktraceInfo*> infos; 0058 QString name; 0059 }; 0060 0061 struct KisMemoryLeakTracker::Private { 0062 QHash<const void*, WhatInfo > whatWhoWhen; 0063 template<typename _T_> 0064 void dumpReferencedObjectsAndDelete(QHash<const _T_*, WhatInfo >&, bool _delete); 0065 QMutex m; 0066 }; 0067 0068 template<typename _T_> 0069 void KisMemoryLeakTracker::Private::dumpReferencedObjectsAndDelete(QHash<const _T_*, WhatInfo >& map, bool _delete) 0070 { 0071 QMutexLocker l(&m); 0072 for (typename QHash<const _T_*, WhatInfo >::iterator it = map.begin(); 0073 it != map.end(); ++it) { 0074 qWarning() << "Object " << it.key() << "(" << it.value().name << ") is still referenced by " << it.value().infos.size() << " objects:"; 0075 for (QHash<const void*, BacktraceInfo*>::iterator it2 = it.value().infos.begin(); 0076 it2 != it.value().infos.end(); ++it2) { 0077 qWarning() << "Referenced by " << it2.key() << " at:"; 0078 #ifdef HAVE_BACKTRACE_SUPPORT 0079 BacktraceInfo* info = it2.value(); 0080 char** strings = backtrace_symbols(info->trace, info->size); 0081 for (int i = 0; i < info->size; ++i) { 0082 qWarning() << strings[i]; 0083 } 0084 if (_delete) { 0085 delete info; 0086 it2.value() = 0; 0087 } 0088 #else 0089 Q_UNUSED(_delete); 0090 qWarning() << "Enable backtrace support by running 'cmake -DHAVE_BACKTRACE_SUPPORT=ON'"; 0091 #endif 0092 } 0093 qWarning() << "====="; 0094 } 0095 } 0096 0097 KisMemoryLeakTracker::KisMemoryLeakTracker() : d(new Private) 0098 { 0099 } 0100 0101 KisMemoryLeakTracker::~KisMemoryLeakTracker() 0102 { 0103 if (d->whatWhoWhen.isEmpty()) { 0104 qInfo() << "No leak detected."; 0105 } else { 0106 qWarning() << "****************************************"; 0107 qWarning() << (d->whatWhoWhen.size()) << " leaks have been detected"; 0108 d->dumpReferencedObjectsAndDelete(d->whatWhoWhen, true); 0109 qWarning() << "****************************************"; 0110 #ifndef NDEBUG 0111 qFatal("Leaks have been detected... fix krita."); 0112 #endif 0113 } 0114 delete d; 0115 } 0116 0117 void KisMemoryLeakTracker::reference(const void* what, const void* bywho, const char* whatName) 0118 { 0119 if(what == 0x0) return; 0120 0121 QMutexLocker l(&d->m); 0122 0123 if (whatName == 0 || ( strcmp(whatName, "PK13KisSharedData") != 0 0124 #ifdef IGNORE_MEMENTO_ITEM 0125 && strcmp(whatName, "PK14KisMementoItem") != 0 0126 #endif 0127 #ifdef IGNORE_TILE 0128 && strcmp(whatName, "PK7KisTile") != 0 0129 #endif 0130 ) ) { 0131 MAKE_BACKTRACEINFO 0132 d->whatWhoWhen[what].infos[bywho] = info; 0133 if (whatName) { 0134 d->whatWhoWhen[what].name = whatName; 0135 } 0136 } 0137 } 0138 0139 void KisMemoryLeakTracker::dereference(const void* what, const void* bywho) 0140 { 0141 QMutexLocker l(&d->m); 0142 if (d->whatWhoWhen.contains(what)) { 0143 QHash<const void*, BacktraceInfo*>& whoWhen = d->whatWhoWhen[what].infos; 0144 delete whoWhen[bywho]; 0145 whoWhen.remove(bywho); 0146 if (whoWhen.isEmpty()) { 0147 d->whatWhoWhen.remove(what); 0148 } 0149 } 0150 } 0151 0152 void KisMemoryLeakTracker::dumpReferences() 0153 { 0154 qWarning() << "****************************************"; 0155 qWarning() << (d->whatWhoWhen.size()) << " objects are currently referenced"; 0156 d->dumpReferencedObjectsAndDelete(d->whatWhoWhen, false); 0157 qWarning() << "****************************************"; 0158 } 0159 0160 void KisMemoryLeakTracker::dumpReferences(const void* what) 0161 { 0162 QMutexLocker l(&d->m); 0163 if (!d->whatWhoWhen.contains(what)) { 0164 qWarning() << "Object " << what << " is not tracked"; 0165 return; 0166 } 0167 0168 WhatInfo& info = d->whatWhoWhen[what]; 0169 qInfo() << "Object " << what << "(" << info.name << ") is still referenced by " << info.infos.size() << " objects:"; 0170 for (QHash<const void*, BacktraceInfo*>::iterator it2 = info.infos.begin(); 0171 it2 != info.infos.end(); ++it2) { 0172 qInfo() << "Referenced by " << it2.key() << " at:"; 0173 #ifdef HAVE_BACKTRACE_SUPPORT 0174 BacktraceInfo* info = it2.value(); 0175 char** strings = backtrace_symbols(info->trace, info->size); 0176 for (int i = 0; i < info->size; ++i) { 0177 qInfo() << strings[i]; 0178 } 0179 #else 0180 qInfo() << "Enable backtrace support in kis_memory_leak_tracker.cpp"; 0181 #endif 0182 } 0183 qInfo() << "====="; 0184 } 0185 0186 #else 0187 #error "Hum, no memory leak tracker for your platform" 0188 #endif 0189 0190 #else 0191 0192 KisMemoryLeakTracker::KisMemoryLeakTracker() : d(0) 0193 { 0194 } 0195 0196 KisMemoryLeakTracker::~KisMemoryLeakTracker() 0197 { 0198 } 0199 0200 void KisMemoryLeakTracker::reference(const void* what, const void* bywho, const char* whatName) 0201 { 0202 Q_UNUSED(what); 0203 Q_UNUSED(bywho); 0204 Q_UNUSED(whatName); 0205 } 0206 0207 void KisMemoryLeakTracker::dereference(const void* what, const void* bywho) 0208 { 0209 Q_UNUSED(what); 0210 Q_UNUSED(bywho); 0211 } 0212 0213 void KisMemoryLeakTracker::dumpReferences() 0214 { 0215 } 0216 0217 void KisMemoryLeakTracker::dumpReferences(const void* what) 0218 { 0219 Q_UNUSED(what); 0220 } 0221 0222 #endif