File indexing completed on 2024-05-12 05:43:33
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 "config-elf-dissector.h" 0019 #include "typemodel.h" 0020 0021 #include <navigator/codenavigatorprinter.h> 0022 #include <elf/elffileset.h> 0023 #if HAVE_DWARF 0024 #include <dwarf/dwarfinfo.h> 0025 #include <dwarf/dwarfdie.h> 0026 #include <dwarf/dwarfcudie.h> 0027 #endif 0028 #include <printers/dwarfprinter.h> 0029 #include <checks/structurepackingcheck.h> 0030 0031 #if HAVE_DWARF 0032 #include <dwarf.h> 0033 #endif 0034 0035 #include <QIcon> 0036 #include <QTime> 0037 #include <QDebug> 0038 0039 TypeModel::TypeModel(QObject* parent): QAbstractItemModel(parent) 0040 { 0041 setFileSet(nullptr); 0042 } 0043 0044 TypeModel::~TypeModel() = default; 0045 0046 void TypeModel::setFileSet(ElfFileSet* fileSet) 0047 { 0048 QTime t; 0049 t.start(); 0050 beginResetModel(); 0051 const auto l = [](TypeModel* m) { m->endResetModel(); }; 0052 const auto endReset = std::unique_ptr<TypeModel, decltype(l)>(this, l); 0053 0054 m_fileSet = fileSet; 0055 m_childMap.clear(); 0056 m_parentMap.clear(); 0057 m_nodes.clear(); 0058 m_nodes.resize(1); 0059 m_childMap.resize(1); 0060 m_hasInvalidDies = false; 0061 0062 if (!fileSet) 0063 return; 0064 0065 for (int i = 0; i < fileSet->size(); ++i) 0066 addFile(fileSet->file(i)); 0067 0068 qDebug() << "Found" << m_nodes.size() << "types, took" << t.elapsed() << "ms."; 0069 } 0070 0071 void TypeModel::addFile(ElfFile* file) 0072 { 0073 const auto dwarf = file->dwarfInfo(); 0074 if (!dwarf) 0075 return; 0076 0077 #if HAVE_DWARF 0078 foreach (const auto cu, dwarf->compilationUnits()) { 0079 foreach (const auto die, cu->children()) 0080 addDwarfDieRecursive(die, 0); 0081 } 0082 #endif 0083 } 0084 0085 bool dieInherits(DwarfDie *parentDie, DwarfDie *childDie) 0086 { 0087 #if HAVE_DWARF 0088 if (parentDie == childDie) 0089 return true; 0090 DwarfDie *baseDie = childDie->inheritedFrom(); 0091 if (!baseDie) 0092 return false; 0093 return dieInherits(parentDie, baseDie); 0094 #else 0095 return false; 0096 #endif 0097 } 0098 0099 bool isBetterDie(DwarfDie *prevDie, DwarfDie *newDie) 0100 { 0101 #if HAVE_DWARF 0102 // we don't care about increasing the level of detail for structure nodes 0103 if (prevDie->tag() != DW_TAG_class_type && prevDie->tag() != DW_TAG_structure_type) 0104 return false; 0105 0106 // declarations are always worse then the real one 0107 if (prevDie->attribute(DW_AT_declaration).toBool()) 0108 return true; 0109 0110 // size is also a good indicator for this belonging to a complete DIE 0111 if (prevDie->typeSize() == 0) 0112 return true; 0113 0114 // walk down the inheritance tree 0115 if (dieInherits(prevDie, newDie)) 0116 return true; 0117 0118 // if we have member children, that's better 0119 // TODO 0120 0121 // TODO what else? 0122 #endif 0123 0124 return false; 0125 } 0126 0127 bool TypeModel::addDwarfDieRecursive(DwarfDie* die, uint32_t parentId) 0128 { 0129 #if HAVE_DWARF 0130 if (!die->dwarfInfo()->isValid()) { 0131 m_hasInvalidDies = true; 0132 return false; 0133 } 0134 0135 switch (die->tag()) { 0136 case DW_TAG_class_type: 0137 case DW_TAG_namespace: 0138 case DW_TAG_structure_type: // TODO we can also have nested types in DW_TAG_subprograms! 0139 break; 0140 default: 0141 return false; 0142 } 0143 0144 QVector<uint32_t> children; 0145 if (parentId < (uint32_t)m_childMap.size()) 0146 children = m_childMap.at(parentId); 0147 0148 const auto dieName = die->typeName(); 0149 const auto it = std::lower_bound(children.constBegin(), children.constEnd(), die, [this, &dieName](uint32_t nodeId, DwarfDie *die) { 0150 const auto lhs = m_nodes.at(nodeId); 0151 if (lhs.die->tag() == die->tag()) 0152 return m_nodes.at(nodeId).die->typeName() < dieName; 0153 return lhs.die->tag() < die->tag(); 0154 }); 0155 0156 uint32_t nodeId; 0157 bool nodeExits; 0158 // *it will be invalid when inserting below, as m_childMap might change, although m_childMap[parentId] stays unchanged 0159 const int childInsertIndex = std::distance(children.constBegin(), it); 0160 0161 // TODO what about anon stuff, name() is empty there, typeName() isn't, but that merges too much 0162 // TODO what about local symbols, compare CUs? 0163 if (it != children.constEnd() && m_nodes.at(*it).die->tag() == die->tag() && m_nodes.at(*it).die->typeName() == dieName) { 0164 nodeId = *it; 0165 if (isBetterDie(m_nodes.at(nodeId).die, die)) 0166 m_nodes[nodeId].die = die; 0167 nodeExits = true; 0168 } else { 0169 nodeId = std::max((uint32_t)m_nodes.size(), parentId + 1); 0170 nodeExits = false; 0171 } 0172 0173 bool childCreated = false; 0174 foreach (auto child, die->children()) 0175 childCreated |= addDwarfDieRecursive(child, nodeId); 0176 0177 if (!nodeExits && (childCreated || die->tag() == DW_TAG_class_type || die->tag() == DW_TAG_structure_type)) { 0178 m_nodes.resize(std::max((uint32_t)m_nodes.size(), nodeId + 1)); 0179 m_nodes[nodeId].die = die; 0180 m_childMap.resize(std::max((uint32_t)m_childMap.size(), nodeId + 1)); 0181 m_childMap[parentId].insert(childInsertIndex, nodeId); 0182 m_parentMap.resize(std::max((uint32_t)m_parentMap.size(), nodeId + 1)); 0183 m_parentMap[nodeId] = parentId; 0184 return true; 0185 } 0186 #endif 0187 0188 return false; 0189 } 0190 0191 int TypeModel::columnCount(const QModelIndex& parent) const 0192 { 0193 Q_UNUSED(parent); 0194 return 2; 0195 } 0196 0197 int TypeModel::rowCount(const QModelIndex& parent) const 0198 { 0199 if (parent.column() > 0) 0200 return 0; 0201 const uint32_t parentId = parent.internalId(); 0202 return m_childMap.at(parentId).size(); 0203 } 0204 0205 QVariant TypeModel::data(const QModelIndex& index, int role) const 0206 { 0207 if (!index.isValid()) 0208 return {}; 0209 0210 #if HAVE_DWARF 0211 const auto node = m_nodes.at(index.internalId()); 0212 switch (role) { 0213 case Qt::DisplayRole: 0214 if (index.column() == 0) 0215 return node.die->typeName(); 0216 else if (index.column() == 1 && (node.die->tag() == DW_TAG_class_type || node.die->tag() == DW_TAG_structure_type)) 0217 return node.die->typeSize(); 0218 return {}; 0219 case TypeModel::DetailRole: 0220 { 0221 QString s = DwarfPrinter::dieRichText(node.die); 0222 s += CodeNavigatorPrinter::sourceLocationRichText(node.die); 0223 0224 if ((node.die->tag() == DW_TAG_structure_type || node.die->tag() == DW_TAG_class_type) && node.die->typeSize() > 0) { 0225 s += QLatin1String("<tt><pre>"); 0226 StructurePackingCheck check; 0227 check.setElfFileSet(m_fileSet); 0228 s += check.checkOneStructure(node.die).toHtmlEscaped(); 0229 s += QLatin1String("</pre></tt><br/>"); 0230 } 0231 0232 return s; 0233 } 0234 case Qt::DecorationRole: 0235 if (index.column() != 0) 0236 return {}; 0237 switch (node.die->tag()) { 0238 case DW_TAG_namespace: 0239 return QIcon::fromTheme(QStringLiteral("code-context")); 0240 case DW_TAG_class_type: 0241 case DW_TAG_structure_type: 0242 return QIcon::fromTheme(QStringLiteral("code-class")); 0243 case DW_TAG_subprogram: 0244 return QIcon::fromTheme(QStringLiteral("code-function")); 0245 } 0246 return {}; 0247 }; 0248 #endif 0249 0250 return {}; 0251 } 0252 0253 QModelIndex TypeModel::index(int row, int column, const QModelIndex& parent) const 0254 { 0255 const int32_t parentId = parent.internalId(); 0256 if (!hasIndex(row, column, parent)) 0257 return {}; 0258 0259 return createIndex(row, column, m_childMap.at(parentId).at(row)); 0260 } 0261 0262 QModelIndex TypeModel::parent(const QModelIndex& child) const 0263 { 0264 const int32_t childId = child.internalId(); 0265 if (childId == 0) 0266 return QModelIndex(); 0267 const int32_t parentId = m_parentMap.at(childId); 0268 if (parentId == 0) 0269 return QModelIndex(); 0270 const int32_t grandParentId = m_parentMap.at(parentId); 0271 const int row = m_childMap.at(grandParentId).indexOf(parentId); 0272 0273 return createIndex(row, 0, parentId); 0274 } 0275 0276 QVariant TypeModel::headerData(int section, Qt::Orientation orientation, int role) const 0277 { 0278 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { 0279 switch (section) { 0280 case 0: return tr("Data Type"); 0281 case 1: return tr("Size"); 0282 } 0283 } 0284 return QAbstractItemModel::headerData(section, orientation, role); 0285 }