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 }