From d0f2c4fcf79222d5c6f3c17188a811b0a47833c6 Mon Sep 17 00:00:00 2001 From: Tannin Date: Wed, 17 Jul 2013 20:58:58 +0200 Subject: - now avoids a few unnecessary copy operations during generation of the directory structure - bugfix: circular dependency caused a memory leak - bugfix: removing a single mod lead to the wrong mod being deleted --- src/directoryrefresher.cpp | 5 ++ src/directoryrefresher.h | 4 +- src/main.cpp | 2 + src/mainwindow.cpp | 23 +++++--- src/mainwindow.h | 2 +- src/modinfo.cpp | 4 +- src/modinfo.h | 2 +- src/modinfodialog.cpp | 2 +- src/modinfodialog.h | 2 +- src/modlist.cpp | 3 +- src/organizer.pro | 1 + src/pluginlist.cpp | 10 ++-- src/shared/directoryentry.cpp | 134 +++++++++++++++++++++++++++++++----------- src/shared/directoryentry.h | 53 ++++++++++------- src/shared/leaktrace.cpp | 109 ++++++++++++++++++++++++++++++++++ src/shared/leaktrace.h | 24 ++++++++ src/shared/shared.pro | 8 ++- src/shared/util.cpp | 35 +++++++---- src/syncoverwritedialog.cpp | 4 +- src/syncoverwritedialog.h | 2 +- 20 files changed, 337 insertions(+), 92 deletions(-) create mode 100644 src/shared/leaktrace.cpp create mode 100644 src/shared/leaktrace.h (limited to 'src') diff --git a/src/directoryrefresher.cpp b/src/directoryrefresher.cpp index c6d458de..70bcf5b6 100644 --- a/src/directoryrefresher.cpp +++ b/src/directoryrefresher.cpp @@ -33,6 +33,11 @@ DirectoryRefresher::DirectoryRefresher() { } +DirectoryRefresher::~DirectoryRefresher() +{ + delete m_DirectoryStructure; +} + DirectoryEntry *DirectoryRefresher::getDirectoryStructure() { QMutexLocker locker(&m_RefreshLock); diff --git a/src/directoryrefresher.h b/src/directoryrefresher.h index 5b785b2f..a6c8f00b 100644 --- a/src/directoryrefresher.h +++ b/src/directoryrefresher.h @@ -21,10 +21,10 @@ along with Mod Organizer. If not, see . #define DIRECTORYREFRESHER_H #include -#include #include #include #include +#include /** @@ -43,6 +43,8 @@ public: **/ DirectoryRefresher(); + ~DirectoryRefresher(); + /** * @brief retrieve the updated directory structure * diff --git a/src/main.cpp b/src/main.cpp index b92bb538..5be0f374 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ + #include #include #include @@ -66,6 +67,7 @@ along with Mod Organizer. If not, see . #include #include + #pragma comment(linker, "/manifestDependency:\"name='dlls' processorArchitecture='x86' version='1.0.0.0' type='win32' \"") diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 8c309131..99b37feb 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -93,6 +93,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include #include @@ -310,6 +311,7 @@ MainWindow::~MainWindow() m_RefresherThread.wait(); delete ui; delete m_GameInfo; + delete m_DirectoryStructure; } void MainWindow::updateStyle(const QString&) @@ -1321,9 +1323,9 @@ void MainWindow::on_profileBox_currentIndexChanged(int index) void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &directorySoFar, const DirectoryEntry &directoryEntry, bool conflictsOnly) { { - std::vector files = directoryEntry.getFiles(); + std::vector files = directoryEntry.getFiles(); for (auto iter = files.begin(); iter != files.end(); ++iter) { - FileEntry *current = *iter; + FileEntry::Ptr current = *iter; if (conflictsOnly && (current->getAlternatives().size() == 0)) { continue; } @@ -1626,9 +1628,9 @@ void MainWindow::refreshBSAList() std::vector > items; - std::vector files = m_DirectoryStructure->getFiles(); + std::vector files = m_DirectoryStructure->getFiles(); for (auto iter = files.begin(); iter != files.end(); ++iter) { - FileEntry *current = *iter; + FileEntry::Ptr current = *iter; QString filename = ToQString(current->getName().c_str()); QString extension = filename.right(3).toLower(); @@ -1898,8 +1900,8 @@ QString MainWindow::resolvePath(const QString &fileName) const if (m_DirectoryStructure == NULL) { return QString(); } - const FileEntry *file = m_DirectoryStructure->searchFile(ToWString(fileName), NULL); - if (file != NULL) { + const FileEntry::Ptr file = m_DirectoryStructure->searchFile(ToWString(fileName), NULL); + if (file.get() != NULL) { return ToQString(file->getFullPath()); } else { return QString(); @@ -2551,12 +2553,15 @@ void MainWindow::removeMod_clicked() { try { QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection() && selection->selectedRows().count() > 1 ) { + if (selection->hasSelection() && selection->selectedRows().count() > 1) { QString mods; QStringList modNames; foreach (QModelIndex idx, selection->selectedRows()) { // QString name = ModInfo::getByIndex(m_ModListGroupProxy->mapToSource(idx).row())->name(); QString name = idx.data().toString(); + if (!ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->isRegular()) { + continue; + } mods += "
  • " + name + "
  • "; modNames.append(name); } @@ -3624,9 +3629,9 @@ void MainWindow::writeDataToFile(QFile &file, const QString &directory, const Di // directoryEntry.getFiles(current, end); // for (; current != end; ++current) { - std::vector files = directoryEntry.getFiles(); + std::vector files = directoryEntry.getFiles(); for (auto iter = files.begin(); iter != files.end(); ++iter) { - FileEntry *current = *iter; + FileEntry::Ptr current = *iter; bool isArchive = false; int origin = current->getOrigin(isArchive); if (isArchive) { diff --git a/src/mainwindow.h b/src/mainwindow.h index df0fa586..23d5f0da 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -29,7 +29,6 @@ along with Mod Organizer. If not, see . #include #include #include -#include #include "executableslist.h" #include "modlist.h" #include "pluginlist.h" @@ -50,6 +49,7 @@ along with Mod Organizer. If not, see . #include "tutorialcontrol.h" #include "savegameinfowidgetgamebryo.h" #include +#include namespace Ui { class MainWindow; diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 00b7c2af..1bb2daad 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -671,7 +671,7 @@ ModInfoRegular::EConflictType ModInfoRegular::isConflicted() const std::wstring name = ToWString(m_Name); if ((*m_DirectoryStructure)->originExists(name)) { FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name); - std::vector files = origin.getFiles(); + std::vector files = origin.getFiles(); for (auto iter = files.begin(); iter != files.end() && (!overwrite || !overwritten || !regular); ++iter) { const std::vector &alternatives = (*iter)->getAlternatives(); if (alternatives.size() == 0) { @@ -716,7 +716,7 @@ bool ModInfoRegular::isRedundant() const std::wstring name = ToWString(m_Name); if ((*m_DirectoryStructure)->originExists(name)) { FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name); - std::vector files = origin.getFiles(); + std::vector files = origin.getFiles(); bool ignore = false; for (auto iter = files.begin(); iter != files.end(); ++iter) { if ((*iter)->getOrigin(ignore) == origin.getID()) { diff --git a/src/modinfo.h b/src/modinfo.h index 05e26590..74a77cd2 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -21,7 +21,6 @@ along with Mod Organizer. If not, see . #define MODINFO_H #include "nexusinterface.h" -#include #include #include @@ -34,6 +33,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include using MOBase::ModRepositoryFileInfo; diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index cf299530..3cf31f7e 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -181,7 +181,7 @@ void ModInfoDialog::refreshLists() ui->overwrittenTree->clear(); if (m_Origin != NULL) { - std::vector files = m_Origin->getFiles(); + std::vector files = m_Origin->getFiles(); for (auto iter = files.begin(); iter != files.end(); ++iter) { QString relativeName = QDir::fromNativeSeparators(ToQString((*iter)->getRelativePath())); QString fileName = relativeName.mid(0).prepend(m_RootPath); diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 176f8f75..c3f3838c 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -24,7 +24,6 @@ along with Mod Organizer. If not, see . #include "modinfo.h" #include "categories.h" #include "tutorabledialog.h" -#include #include #include @@ -37,6 +36,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include namespace Ui { diff --git a/src/modlist.cpp b/src/modlist.cpp index 7a8d9e0b..45bed00a 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -655,6 +655,7 @@ void ModList::removeRow(int row, const QModelIndex&) if (m_Profile == NULL) return; ModInfo::Ptr modInfo = ModInfo::getByIndex(row); + if (!modInfo->isRegular()) return; QMessageBox confirmBox(QMessageBox::Question, tr("Confirm"), tr("Are you sure you want to remove \"%1\"?").arg(modInfo->name()), QMessageBox::Yes | QMessageBox::No); @@ -793,7 +794,7 @@ bool ModList::eventFilter(QObject *obj, QEvent *event) if (rows.count() > 1) { emit removeSelectedMods(); } else if (rows.count() == 1) { - removeRow(rows[0].row(), QModelIndex()); + removeRow(rows[0].data(Qt::UserRole + 1).toInt(), QModelIndex()); } return true; } diff --git a/src/organizer.pro b/src/organizer.pro index 657e0bce..fd8a4c61 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -184,6 +184,7 @@ CONFIG(debug, debug|release) { DSTDIR = $$PWD/../../outputd LIBS += -L$$OUT_PWD/../shared/debug -L$$OUT_PWD/../bsatk/debug LIBS += -L$$OUT_PWD/../uibase/debug + LIBS += -lDbgHelp } else { OUTDIR = $$OUT_PWD/release DSTDIR = $$PWD/../../output diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index df132287..61069c2f 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -116,10 +116,10 @@ void PluginList::refresh(const QString &profileName, const DirectoryEntry &baseD m_CurrentProfile = profileName; - std::vector files = baseDirectory.getFiles(); + std::vector files = baseDirectory.getFiles(); for (auto iter = files.begin(); iter != files.end(); ++iter) { - FileEntry *current = *iter; - if (current == NULL) { + FileEntry::Ptr current = *iter; + if (current.get() == NULL) { continue; } QString filename = ToQString(current->getName()); @@ -450,8 +450,8 @@ bool PluginList::saveLoadOrder(DirectoryEntry &directoryStructure) for (std::vector::iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) { std::wstring espName = ToWString(iter->m_Name); - const FileEntry *fileEntry = directoryStructure.findFile(espName); - if (fileEntry != NULL) { + const FileEntry::Ptr fileEntry = directoryStructure.findFile(espName); + if (fileEntry.get() != NULL) { QString fileName; bool archive = false; int originid = fileEntry->getOrigin(archive); diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 165af798..ceba113d 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -27,10 +27,17 @@ along with Mod Organizer. If not, see . #include "util.h" #include "windows_error.h" #include +#include "util.h" +#include "leaktrace.h" + +#include + namespace MOShared { +static int s_Count = 0; + class OriginConnection { @@ -43,7 +50,9 @@ public: OriginConnection() : m_NextID(0) - {} + { + LEAK_TRACE; + } FilesOrigin& createOrigin(const std::wstring &originName, const std::wstring &directory, int priority, boost::shared_ptr fileRegister, boost::shared_ptr originConnection) { @@ -123,7 +132,7 @@ void FilesOrigin::enable(bool enabled) if (!enabled) { std::set copy = m_Files; for (auto iter = copy.begin(); iter != copy.end(); ++iter) { - m_FileRegister->removeOrigin(*iter, m_ID); + m_FileRegister.lock()->removeOrigin(*iter, m_ID); } m_Files.clear(); } @@ -151,9 +160,41 @@ std::wstring tail(const std::wstring &source, const size_t count) } +FilesOrigin::FilesOrigin() + : m_ID(0), m_Disabled(false), m_Name(), m_Path(), m_Priority(0) +{ + LEAK_TRACE; +} + +FilesOrigin::FilesOrigin(const FilesOrigin &reference) + : m_ID(reference.m_ID) + , m_Disabled(reference.m_Disabled) + , m_Name(reference.m_Name) + , m_Path(reference.m_Path) + , m_Priority(reference.m_Priority) + , m_FileRegister(reference.m_FileRegister) + , m_OriginConnection(reference.m_OriginConnection) +{ + LEAK_TRACE; +} + + +FilesOrigin::FilesOrigin(int ID, const std::wstring &name, const std::wstring &path, int priority, boost::shared_ptr fileRegister, boost::shared_ptr originConnection) + : m_ID(ID), m_Disabled(false), m_Name(name), m_Path(path), m_Priority(priority), + m_FileRegister(fileRegister), m_OriginConnection(originConnection) +{ + LEAK_TRACE; +} + +FilesOrigin::~FilesOrigin() +{ + LEAK_UNTRACE; +} + + void FilesOrigin::setPriority(int priority) { - m_OriginConnection->changePriorityLookup(m_Priority, priority); + m_OriginConnection.lock()->changePriorityLookup(m_Priority, priority); m_Priority = priority; } @@ -161,7 +202,7 @@ void FilesOrigin::setPriority(int priority) void FilesOrigin::setName(const std::wstring &name) { - m_OriginConnection->changeNameLookup(m_Name, name); + m_OriginConnection.lock()->changeNameLookup(m_Name, name); // change path too if (tail(m_Path, m_Name.length()) == m_Name) { m_Path = m_Path.substr(0, m_Path.length() - m_Name.length()).append(name); @@ -169,12 +210,12 @@ void FilesOrigin::setName(const std::wstring &name) m_Name = name; } -std::vector FilesOrigin::getFiles() const +std::vector FilesOrigin::getFiles() const { - std::vector result; + std::vector result; for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { - result.push_back(m_FileRegister->getFile(*iter)); + result.push_back(m_FileRegister.lock()->getFile(*iter)); } return result; @@ -275,11 +316,22 @@ static bool ByOriginPriority(DirectoryEntry *entry, int LHS, int RHS) } +FileEntry::FileEntry() + : m_Index(UINT_MAX), m_Name(), m_Parent(NULL) +{ + LEAK_TRACE; +} + FileEntry::FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent) : m_Index(index), m_Name(name), m_Parent(parent), m_Origin(-1), m_Archive(L"") { + LEAK_TRACE; } +FileEntry::~FileEntry() +{ + LEAK_UNTRACE; +} void FileEntry::sortOrigins() { @@ -322,28 +374,35 @@ std::wstring FileEntry::getRelativePath() const } - - - // // DirectoryEntry // DirectoryEntry::DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID) : m_OriginConnection(new OriginConnection), - m_Name(name), m_Parent(parent), m_Populated(false), m_Origin(originID) + m_Name(name), m_Parent(parent), m_Populated(false), m_Origin(originID), m_TopLevel(true) { m_FileRegister.reset(new FileRegister(m_OriginConnection)); + LEAK_TRACE; } DirectoryEntry::DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID, boost::shared_ptr fileRegister, boost::shared_ptr originConnection) : m_FileRegister(fileRegister), m_OriginConnection(originConnection), - m_Name(name), m_Parent(parent), m_Populated(false), m_Origin(originID) -{} + m_Name(name), m_Parent(parent), m_Populated(false), m_Origin(originID), m_TopLevel(false) +{ + LEAK_TRACE; +} DirectoryEntry::~DirectoryEntry() { +/* if (m_TopLevel) { + if (m_FileRegister.use_count() > 1) { +log("this should not happen"); + delete m_FileRegister.get(); + } + }*/ + LEAK_UNTRACE; clear(); } @@ -573,7 +632,7 @@ int DirectoryEntry::anyOrigin() const { bool ignore; for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { - FileEntry *entry = m_FileRegister->getFile(iter->second); + FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); if (!entry->isFromArchive()) { return entry->getOrigin(ignore); } @@ -612,8 +671,8 @@ FilesOrigin &DirectoryEntry::getOriginByName(const std::wstring &name) const int DirectoryEntry::getOrigin(const std::wstring &path, bool &archive) { const DirectoryEntry *directory = NULL; - const FileEntry *file = searchFile(path, &directory); - if (file != NULL) { + const FileEntry::Ptr file = searchFile(path, &directory); + if (file.get() != NULL) { return file->getOrigin(archive); } else { if (directory != NULL) { @@ -624,9 +683,9 @@ int DirectoryEntry::getOrigin(const std::wstring &path, bool &archive) } } -std::vector DirectoryEntry::getFiles() const +std::vector DirectoryEntry::getFiles() const { - std::vector result; + std::vector result; for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { result.push_back(m_FileRegister->getFile(iter->second)); } @@ -634,7 +693,7 @@ std::vector DirectoryEntry::getFiles() const } -const FileEntry *DirectoryEntry::searchFile(const std::wstring &path, const DirectoryEntry **directory) const +const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const DirectoryEntry **directory) const { if (directory != NULL) { *directory = NULL; @@ -682,7 +741,7 @@ DirectoryEntry *DirectoryEntry::findSubDirectory(const std::wstring &name) const } -const FileEntry *DirectoryEntry::findFile(const std::wstring &name) +const FileEntry::Ptr DirectoryEntry::findFile(const std::wstring &name) { auto iter = m_Files.find(name); if (iter != m_Files.end()) { @@ -734,8 +793,16 @@ DirectoryEntry *DirectoryEntry::getSubDirectoryRecursive(const std::wstring &pat FileRegister::FileRegister(boost::shared_ptr originConnection) : m_OriginConnection(originConnection) { + LEAK_TRACE; } +FileRegister::~FileRegister() +{ + LEAK_UNTRACE; + m_Files.clear(); +} + + FileEntry::Index FileRegister::generateIndex() { static FileEntry::Index sIndex = 0; @@ -747,38 +814,38 @@ bool FileRegister::indexValid(FileEntry::Index index) const return m_Files.find(index) != m_Files.end(); } -FileEntry &FileRegister::createFile(const std::wstring &name, DirectoryEntry *parent) +FileEntry::Ptr FileRegister::createFile(const std::wstring &name, DirectoryEntry *parent) { FileEntry::Index index = generateIndex(); - m_Files[index] = FileEntry(index, name, parent); + m_Files[index] = FileEntry::Ptr(new FileEntry(index, name, parent)); return m_Files[index]; } -FileEntry *FileRegister::getFile(FileEntry::Index index) +FileEntry::Ptr FileRegister::getFile(FileEntry::Index index) { auto iter = m_Files.find(index); if (iter != m_Files.end()) { - return &iter->second; + return iter->second; } return NULL; } -void FileRegister::unregisterFile(FileEntry &file) +void FileRegister::unregisterFile(FileEntry::Ptr file) { bool ignore; // unregister from origin - int originID = file.getOrigin(ignore); - m_OriginConnection->getByID(originID).removeFile(file.getIndex()); - const std::vector &alternatives = file.getAlternatives(); + int originID = file->getOrigin(ignore); + m_OriginConnection->getByID(originID).removeFile(file->getIndex()); + const std::vector &alternatives = file->getAlternatives(); for (auto iter = alternatives.begin(); iter != alternatives.end(); ++iter) { - m_OriginConnection->getByID(*iter).removeFile(file.getIndex()); + m_OriginConnection->getByID(*iter).removeFile(file->getIndex()); } // unregister from directory - if (file.getParent() != NULL) { - file.getParent()->removeFile(file.getIndex()); + if (file->getParent() != NULL) { + file->getParent()->removeFile(file->getIndex()); } } @@ -796,7 +863,7 @@ void FileRegister::removeOrigin(FileEntry::Index index, int originID) { auto iter = m_Files.find(index); if (iter != m_Files.end()) { - if (iter->second.removeOrigin(originID)) { + if (iter->second->removeOrigin(originID)) { unregisterFile(iter->second); } } @@ -805,7 +872,8 @@ void FileRegister::removeOrigin(FileEntry::Index index, int originID) void FileRegister::sortOrigins() { for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { - iter->second.sortOrigins(); + iter->second->sortOrigins(); } } + } // namespace MOShared diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index c6d155de..498ebfe1 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -20,6 +20,7 @@ along with Mod Organizer. If not, see . #ifndef DIRECTORYENTRY_H #define DIRECTORYENTRY_H + #include #include #include @@ -29,6 +30,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include "util.h" @@ -46,12 +48,16 @@ public: typedef unsigned int Index; + typedef boost::shared_ptr Ptr; + public: - FileEntry() : m_Index(UINT_MAX), m_Name(), m_Parent(NULL) {} + FileEntry(); FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent); + ~FileEntry(); + Index getIndex() const { return m_Index; } void addOrigin(int origin, FILETIME fileTime, const std::wstring &archive); @@ -105,8 +111,9 @@ class FilesOrigin { friend class OriginConnection; public: - FilesOrigin() - : m_ID(0), m_Disabled(false), m_Name(), m_Path(), m_Priority(0) { } + FilesOrigin(); + FilesOrigin(const FilesOrigin &reference); + ~FilesOrigin(); // sets priority for this origin, but it will overwrite the exisiting mapping for this priority, // the previous origin will no longer be referenced @@ -120,7 +127,7 @@ public: int getID() const { return m_ID; } const std::wstring &getPath() const { return m_Path; } - std::vector getFiles() const; + std::vector getFiles() const; void enable(bool enabled); bool isDisabled() const { return m_Disabled; } @@ -131,9 +138,8 @@ public: private: FilesOrigin(int ID, const std::wstring &name, const std::wstring &path, int priority, - boost::shared_ptr fileRegister, boost::shared_ptr originConnection) - : m_ID(ID), m_Disabled(false), m_Name(name), m_Path(path), m_Priority(priority), - m_FileRegister(fileRegister), m_OriginConnection(originConnection) {} + boost::shared_ptr fileRegister, boost::shared_ptr originConnection); + private: @@ -145,8 +151,8 @@ private: std::wstring m_Name; std::wstring m_Path; int m_Priority; - boost::shared_ptr m_FileRegister; - boost::shared_ptr m_OriginConnection; + boost::weak_ptr m_FileRegister; + boost::weak_ptr m_OriginConnection; }; @@ -157,11 +163,12 @@ class FileRegister public: FileRegister(boost::shared_ptr originConnection); + ~FileRegister(); bool indexValid(FileEntry::Index index) const; - FileEntry &createFile(const std::wstring &name, DirectoryEntry *parent); - FileEntry *getFile(FileEntry::Index index); + FileEntry::Ptr createFile(const std::wstring &name, DirectoryEntry *parent); + FileEntry::Ptr getFile(FileEntry::Index index); void removeFile(FileEntry::Index index); void removeOrigin(FileEntry::Index index, int originID); @@ -172,11 +179,11 @@ private: FileEntry::Index generateIndex(); - void unregisterFile(FileEntry &file); + void unregisterFile(FileEntry::Ptr file); private: - std::map m_Files; + std::map m_Files; boost::shared_ptr m_OriginConnection; @@ -197,7 +204,6 @@ public: void clear(); bool isPopulated() const { return m_Populated; } - boost::shared_ptr getRegister() { return m_FileRegister; } const DirectoryEntry *getParent() const { return m_Parent; } // add files to this directory (and subdirectories) from the specified origin. That origin may exist or not @@ -214,7 +220,7 @@ public: int getOrigin(const std::wstring &path, bool &archive); - std::vector getFiles() const; + std::vector getFiles() const; void getSubDirectories(std::vector::const_iterator &begin, std::vector::const_iterator &end) const { begin = m_SubDirectories.begin(); end = m_SubDirectories.end(); @@ -226,12 +232,12 @@ public: * @param name name of the file * @return fileentry object for the file or NULL if no file matches */ - const FileEntry *findFile(const std::wstring &name); + const FileEntry::Ptr findFile(const std::wstring &name); /** search through this directory and all subdirectories for a file by the specified name. if directory is not NULL, the referenced variable will be set to true if the path refers to a directory. the returned pointer is NULL in that case */ - const FileEntry *searchFile(const std::wstring &path, const DirectoryEntry **directory) const; + const FileEntry::Ptr searchFile(const std::wstring &path, const DirectoryEntry **directory) const; void insertFile(const std::wstring &filePath, FilesOrigin &origin, FILETIME fileTime); @@ -250,8 +256,8 @@ public: auto iter = m_Files.find(fileName); if (iter != m_Files.end()) { if (origin != NULL) { - FileEntry *entry = m_FileRegister->getFile(iter->second); - if (entry != NULL) { + FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); + if (entry.get() != NULL) { bool ignore; *origin = entry->getOrigin(ignore); } @@ -262,13 +268,16 @@ public: private: + DirectoryEntry(const DirectoryEntry &reference); + DirectoryEntry &operator=(const DirectoryEntry &reference); + void insert(const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, const std::wstring &archive) { auto iter = m_Files.find(fileName); - FileEntry *file = NULL; + FileEntry::Ptr file; if (iter != m_Files.end()) { file = m_FileRegister->getFile(iter->second); } else { - file = &m_FileRegister->createFile(fileName, this); + file = m_FileRegister->createFile(fileName, this); m_Files[fileName] = file->getIndex(); } file->addOrigin(origin.getID(), fileTime, archive); @@ -310,6 +319,8 @@ private: bool m_Populated; + bool m_TopLevel; + }; diff --git a/src/shared/leaktrace.cpp b/src/shared/leaktrace.cpp new file mode 100644 index 00000000..0c618b68 --- /dev/null +++ b/src/shared/leaktrace.cpp @@ -0,0 +1,109 @@ +#include "leaktrace.h" +#include +#include +#include +#include +#include + + +static const int FRAMES_TO_SKIP = 3; // StackData::StackData(), __TraceData::regTrace(), TraceAlloc() +static const int FRAMES_TO_CAPTURE = 10; + + +void initDbgIfNecessary() +{ + HANDLE process = ::GetCurrentProcess(); + static std::set initialized; + if (initialized.find(::GetCurrentProcessId()) == initialized.end()) { + static bool firstCall = true; + if (firstCall) { + ::SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); + firstCall = false; + } + if (!::SymInitialize(process, NULL, TRUE)) { + printf("failed to initialize symbols: %d", ::GetLastError()); + } + initialized.insert(::GetCurrentProcessId()); + } +} + + +class StackData { + friend bool operator==(const StackData &LHS, const StackData &RHS); + friend bool operator<(const StackData &LHS, const StackData &RHS); +public: + StackData() { + m_Count = ::CaptureStackBackTrace(FRAMES_TO_SKIP, FRAMES_TO_CAPTURE, m_Stack, &m_Hash); + } + std::string toString() const { + initDbgIfNecessary(); + + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; + PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + + std::ostringstream stackStream; + + for(unsigned int i = 0; i < m_Count; ++i) { + DWORD64 displacement = 0; + if (!::SymFromAddr(::GetCurrentProcess(), (DWORD64)m_Stack[i], &displacement, symbol)) { + stackStream << m_Count - i - 1 << ": [" << m_Stack[i] << "]\n"; + } else { + stackStream << m_Count - i - 1 << ": " << symbol->Name << "\n"; + } + } + return stackStream.str(); + } +private: + LPVOID m_Stack[FRAMES_TO_CAPTURE]; + USHORT m_Count; + ULONG m_Hash; +}; + +bool operator==(const StackData &LHS, const StackData &RHS) { + return LHS.m_Hash == RHS.m_Hash; +} + +bool operator<(const StackData &LHS, const StackData &RHS) { + return LHS.m_Hash < RHS.m_Hash; +} + + + +static struct __TraceData { + void regTrace(void *pointer) { + m_Traces[reinterpret_cast(pointer)] = StackData(); + } + void deregTrace(void *pointer) { + auto iter = m_Traces.find(reinterpret_cast(pointer)); + if (iter != m_Traces.end()) { + m_Traces.erase(iter); + } + } + + ~__TraceData() { + std::map result; + for (auto iter = m_Traces.begin(); iter != m_Traces.end(); ++iter) { + result[iter->second] += 1; + } + for (auto iter = result.begin(); iter != result.end(); ++iter) { + printf("-----------------------------------\n" + "%d objects not freed, allocated at:\n%s", + iter->second, iter->first.toString().c_str()); + } + } + + std::map m_Traces; +} __trace; + + +void LeakTrace::TraceAlloc(void *ptr) +{ + __trace.regTrace(ptr); +} + +void LeakTrace::TraceDealloc(void *ptr) +{ + __trace.deregTrace(ptr); +} diff --git a/src/shared/leaktrace.h b/src/shared/leaktrace.h new file mode 100644 index 00000000..78764260 --- /dev/null +++ b/src/shared/leaktrace.h @@ -0,0 +1,24 @@ +#ifndef LEAKTRACE_H +#define LEAKTRACE_H + + +namespace LeakTrace { + +void TraceAlloc(void *ptr); +void TraceDealloc(void *ptr); + +}; + +#ifdef TRACE_LEAKS + +#define LEAK_TRACE LeakTrace::TraceAlloc(this) +#define LEAK_UNTRACE LeakTrace::TraceDealloc(this) + +#else // TRACE_LEAKS + +#define LEAK_TRACE +#define LEAK_UNTRACE + +#endif // TRACE_LEAKS + +#endif // LEAKTRACE_H diff --git a/src/shared/shared.pro b/src/shared/shared.pro index 9489958a..ab0bd8a0 100644 --- a/src/shared/shared.pro +++ b/src/shared/shared.pro @@ -15,7 +15,7 @@ INCLUDEPATH += ../bsatk "$(BOOSTPATH)" CONFIG(debug, debug|release) { LIBS += -L$$OUT_PWD/../bsatk/debug - + LIBS += -lDbgHelp } else { LIBS += -L$$OUT_PWD/../bsatk/release } @@ -39,7 +39,8 @@ SOURCES += \ falloutnvinfo.cpp \ util.cpp \ skyriminfo.cpp \ - appconfig.cpp + appconfig.cpp \ + leaktrace.cpp HEADERS += \ inject.h \ @@ -53,4 +54,5 @@ HEADERS += \ util.h \ skyriminfo.h \ appconfig.h \ - appconfig.inc + appconfig.inc \ + leaktrace.h diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 1c4bf5b6..4378e03c 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -24,6 +24,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include namespace MOShared { @@ -147,24 +148,38 @@ VS_FIXEDFILEINFO GetFileVersion(const std::wstring &fileName) std::string GetStack() { -#ifdef DEBUG +#ifdef _DEBUG HANDLE process = ::GetCurrentProcess(); - static bool firstCall = true; - if (firstCall) { - ::SymInitialize(process, NULL, TRUE); - firstCall = false; + static std::set initialized; + if (initialized.find(::GetCurrentProcessId()) == initialized.end()) { + static bool firstCall = true; + if (firstCall) { + ::SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); + firstCall = false; + } + if (!::SymInitialize(process, NULL, TRUE)) { + log("failed to initialize symbols: %d", ::GetLastError()); + } + initialized.insert(::GetCurrentProcessId()); } LPVOID stack[32]; WORD frames = ::CaptureStackBackTrace(0, 100, stack, NULL); - SYMBOL_INFO_PACKAGE symbol; - symbol.si.SizeOfStruct = sizeof(SYMBOL_INFO); - symbol.si.MaxNameLen = MAX_SYM_NAME; + + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; + PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; std::ostringstream stackStream; for(unsigned int i = 0; i < frames; ++i) { - ::SymFromAddr(process, (DWORD64)(stack[i]), 0, &symbol.si); - stackStream << frames - i - 1 << ": " << symbol.si.Name << "\n"; + DWORD64 addr = (DWORD64)stack[i]; + DWORD64 displacement = 0; + if (!::SymFromAddr(::GetCurrentProcess(), addr, &displacement, symbol)) { + stackStream << frames - i - 1 << ": " << stack[i] << " - " << ::GetLastError() << " (error)\n"; + } else { + stackStream << frames - i - 1 << ": " << symbol->Name << "\n"; + } } return stackStream.str(); #else diff --git a/src/syncoverwritedialog.cpp b/src/syncoverwritedialog.cpp index 8a496e24..32b76c97 100644 --- a/src/syncoverwritedialog.cpp +++ b/src/syncoverwritedialog.cpp @@ -90,10 +90,10 @@ void SyncOverwriteDialog::readTree(const QString &path, DirectoryEntry *director newItem = NULL; } } else { - const FileEntry *entry = directoryStructure->findFile(ToWString(file)); + const FileEntry::Ptr entry = directoryStructure->findFile(ToWString(file)); QComboBox* combo = new QComboBox(ui->syncTree); combo->addItem(tr(""), -1); - if (entry != NULL) { + if (entry.get() != NULL) { bool ignore; int origin = entry->getOrigin(ignore); addToComboBox(combo, ToQString(m_DirectoryStructure->getOriginByID(origin).getName()), origin); diff --git a/src/syncoverwritedialog.h b/src/syncoverwritedialog.h index 74c9a58f..7fbd876e 100644 --- a/src/syncoverwritedialog.h +++ b/src/syncoverwritedialog.h @@ -21,9 +21,9 @@ along with Mod Organizer. If not, see . #define SYNCOVERWRITEDIALOG_H -#include #include "tutorabledialog.h" #include +#include namespace Ui { -- cgit v1.3.1