File indexing completed on 2024-05-12 05:43:31
0001 /* 0002 Copyright (C) 2015 Volker Krause <vkrause@kde.org> 0003 0004 This program is free software; you can redistribute it and/or modify it 0005 under the terms of the GNU Library General Public License as published by 0006 the Free Software Foundation; either version 2 of the License, or (at your 0007 option) any later version. 0008 0009 This program is distributed in the hope that it will be useful, but WITHOUT 0010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 0011 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 0012 License for more details. 0013 0014 You should have received a copy of the GNU General Public License 0015 along with this program. If not, see <https://www.gnu.org/licenses/>. 0016 */ 0017 0018 #include "dependencymodel.h" 0019 0020 #include <elf/elffileset.h> 0021 #include <elf/elffile.h> 0022 #include <elf/elfdynamicsection.h> 0023 #include <elf/elfsymboltablesection.h> 0024 #include <elf/elfhashsection.h> 0025 #include <checks/dependenciescheck.h> 0026 0027 #include <QIcon> 0028 0029 #include <cassert> 0030 #include <memory> 0031 0032 DependencyModel::DependencyModel(QObject* parent): QAbstractItemModel(parent) 0033 { 0034 } 0035 0036 DependencyModel::~DependencyModel() 0037 { 0038 } 0039 0040 ElfFileSet* DependencyModel::fileSet() const 0041 { 0042 return m_fileSet; 0043 } 0044 0045 void DependencyModel::setFileSet(ElfFileSet* fileSet) 0046 { 0047 beginResetModel(); 0048 const auto l = [](DependencyModel* m) { m->endResetModel(); }; 0049 const auto endReset = std::unique_ptr<DependencyModel, decltype(l)>(this, l); 0050 0051 m_fileIndex.clear(); 0052 m_childMap.clear(); 0053 m_parentMap.clear(); 0054 m_uniqueIndex = 0; 0055 0056 m_fileSet = fileSet; 0057 if (!fileSet || fileSet->size() == 0) 0058 return; 0059 0060 // build up indexes to make the tree building more efficient 0061 for (int i = 0; i < fileSet->size(); ++i) { 0062 const auto file = fileSet->file(i); 0063 if (!file->dynamicSection()) 0064 continue; 0065 const auto soName = file->dynamicSection()->soName(); 0066 if (!soName.isEmpty()) 0067 m_fileIndex.insert(soName, i); 0068 else 0069 m_fileIndex.insert(file->fileName().toUtf8(), i); 0070 } 0071 0072 // setup root 0073 m_parentMap.resize(1); 0074 m_parentMap[0] = 0; 0075 m_childMap.resize(1); 0076 0077 // TODO identify top-level files (for now we just assume it's the first one) 0078 0079 // fill the first level 0080 ++m_uniqueIndex; 0081 assert((uint32_t)m_parentMap.size() == m_uniqueIndex); 0082 assert((uint32_t)m_childMap.size() == m_uniqueIndex); 0083 0084 m_parentMap.push_back(0); 0085 m_childMap[0].push_back(makeId(m_uniqueIndex, 0)); 0086 m_childMap.push_back({}); 0087 0088 // clear symbol usage count table, will be filled on demand 0089 m_symbolCountTable.clear(); 0090 m_symbolCountTable.resize(m_fileSet->size()); 0091 for (int i = 0; i < m_fileSet->size(); ++i) { 0092 m_symbolCountTable[i].resize(m_fileSet->size()); 0093 m_symbolCountTable[i].fill(-1, m_fileSet->size()); 0094 } 0095 } 0096 0097 QVariant DependencyModel::data(const QModelIndex& index, int role) const 0098 { 0099 if (!index.isValid() || !m_fileSet) 0100 return {}; 0101 0102 const auto file = fileIndex(index.internalId()); 0103 const auto parentIdx = parent(index); 0104 const auto parentFile = fileIndex(parentIdx.internalId()); 0105 0106 switch (role) { 0107 case Qt::DisplayRole: 0108 { 0109 if (index.column() == 0) { 0110 if (file != InvalidFile) 0111 return m_fileSet->file(file)->displayName(); 0112 return m_fileSet->file(parentFile)->dynamicSection()->neededLibraries().at(index.row()); 0113 } 0114 0115 if (index.column() == 1 && file != InvalidFile && parentIdx.isValid()) { 0116 auto count = m_symbolCountTable.at(parentFile).at(file); 0117 if (count == -1) 0118 count = m_symbolCountTable[parentFile][file] = usedSymbolCount(parentFile, file); 0119 return count; 0120 } 0121 0122 break; 0123 } 0124 case Qt::DecorationRole: 0125 if (index.column()) 0126 break; 0127 if (file == InvalidFile) 0128 return QIcon::fromTheme(QStringLiteral("dialog-error")); 0129 if (hasCycle(index)) 0130 return QIcon::fromTheme(QStringLiteral("dialog-warning")); 0131 break; 0132 case Qt::ToolTipRole: 0133 if (hasCycle(index)) 0134 return tr("Cyclic dependency!"); 0135 if (file != InvalidFile) 0136 return m_fileSet->file(file)->fileName(); 0137 else 0138 return tr("Dependency not found!"); 0139 case UserFileRole: 0140 if (parentFile == InvalidFile || !parentIdx.isValid()) 0141 return QVariant::fromValue<ElfFile*>(nullptr); 0142 return QVariant::fromValue(m_fileSet->file(parentFile)); 0143 case ProviderFileRole: 0144 if (file == InvalidFile) 0145 return QVariant::fromValue<ElfFile*>(nullptr); 0146 return QVariant::fromValue(m_fileSet->file(file)); 0147 } 0148 0149 return {}; 0150 } 0151 0152 int DependencyModel::columnCount(const QModelIndex& parent) const 0153 { 0154 Q_UNUSED(parent); 0155 return 2; 0156 } 0157 0158 int DependencyModel::rowCount(const QModelIndex& parent) const 0159 { 0160 if (!m_fileSet || parent.column() > 0) 0161 return 0; 0162 0163 const auto node = nodeId(parent.internalId()); 0164 assert(node < (uint32_t)m_childMap.size()); 0165 0166 { 0167 // this reference will become invalid as soon as we modify m_childMap below! 0168 const auto &children = m_childMap.at(node); 0169 if (!children.isEmpty()) 0170 return children.size(); 0171 } 0172 0173 const auto file = fileIndex(parent.internalId()); 0174 if (file == InvalidFile || hasCycle(parent) || !m_fileSet->file(file)->dynamicSection()) 0175 return 0; 0176 0177 const auto needed = m_fileSet->file(file)->dynamicSection()->neededLibraries(); 0178 if (needed.isEmpty()) 0179 return 0; 0180 0181 for (const auto &need : needed) { 0182 const uint64_t childNode = makeId(++m_uniqueIndex, fileIndex(need)); 0183 m_parentMap.push_back(parent.internalId()); 0184 m_childMap.push_back({}); 0185 m_childMap[node].push_back(childNode); 0186 assert((uint32_t)m_parentMap.size() == m_uniqueIndex + 1); 0187 assert((uint32_t)m_childMap.size() == m_uniqueIndex + 1); 0188 } 0189 0190 return m_childMap.at(node).size(); 0191 } 0192 0193 QModelIndex DependencyModel::parent(const QModelIndex& child) const 0194 { 0195 if (!m_fileSet || !child.isValid()) 0196 return {}; 0197 0198 const auto node = nodeId(child.internalId()); 0199 assert((uint32_t)m_parentMap.size() > node); 0200 0201 const auto parentId = m_parentMap.at(node); 0202 const auto parentNode = nodeId(parentId); 0203 if (parentNode == 0) 0204 return {}; 0205 0206 assert((uint32_t)m_parentMap.size() > parentNode); 0207 const auto grandParentNode = nodeId(m_parentMap.at(parentNode)); 0208 0209 assert((uint32_t)m_childMap.size() > grandParentNode); 0210 const auto &children = m_childMap.at(grandParentNode); 0211 const auto row = children.indexOf(parentId); 0212 assert(row >= 0); 0213 return createIndex(row, 0, parentId); 0214 } 0215 0216 QModelIndex DependencyModel::index(int row, int column, const QModelIndex& parent) const 0217 { 0218 if (!m_fileSet || !hasIndex(row, column, parent)) 0219 return {}; 0220 0221 const auto node = nodeId(parent.internalId()); 0222 assert(node < (uint32_t)m_childMap.size()); 0223 0224 const auto &children = m_childMap.at(node); 0225 if (row >= children.size()) 0226 return {}; 0227 assert(row < children.size()); 0228 0229 return createIndex(row, column, children.at(row)); 0230 } 0231 0232 QVariant DependencyModel::headerData(int section, Qt::Orientation orientation, int role) const 0233 { 0234 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { 0235 switch (section) { 0236 case 0: return tr("Library"); 0237 case 1: return tr("Symbols Used"); 0238 } 0239 } 0240 return QAbstractItemModel::headerData(section, orientation, role); 0241 } 0242 0243 uint64_t DependencyModel::makeId(uint32_t id, int32_t fileIndex) const 0244 { 0245 uint64_t f = (uint32_t)fileIndex; 0246 return id | f << 32; 0247 } 0248 0249 int32_t DependencyModel::fileIndex(uint64_t qmiId) const 0250 { 0251 return qmiId >> 32; 0252 } 0253 0254 int32_t DependencyModel::fileIndex(const QByteArray& needed) const 0255 { 0256 const auto it = m_fileIndex.constFind(needed); 0257 if (it != m_fileIndex.cend()) 0258 return it.value(); 0259 return InvalidFile; 0260 } 0261 0262 uint32_t DependencyModel::nodeId(uint64_t qmiId) const 0263 { 0264 return qmiId; 0265 } 0266 0267 bool DependencyModel::hasCycle(const QModelIndex& index) const 0268 { 0269 const auto file = fileIndex(index.internalId()); 0270 0271 QModelIndex parentIndex(index); 0272 forever { 0273 parentIndex = parent(parentIndex); 0274 if (!parentIndex.isValid()) 0275 return false; 0276 const auto parentFile = fileIndex(parentIndex.internalId()); 0277 if (parentFile == file) 0278 return true; 0279 } 0280 Q_UNREACHABLE(); 0281 } 0282 0283 int DependencyModel::usedSymbolCount(int parentId, int fileId) const 0284 { 0285 assert(parentId != fileId); 0286 assert(parentId >= 0); 0287 assert(fileId >= 0); 0288 return DependenciesCheck::usedSymbolCount(m_fileSet->file(parentId), m_fileSet->file(fileId)); 0289 }