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