//===- Analyze.cpp - PDB analysis functions ---------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "Analyze.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" #include "llvm/DebugInfo/CodeView/TypeDatabase.h" #include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/RawError.h" #include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" #include using namespace llvm; using namespace llvm::codeview; using namespace llvm::pdb; static StringRef getLeafTypeName(TypeLeafKind LT) { switch (LT) { #define TYPE_RECORD(ename, value, name) \ case ename: \ return #name; #include "llvm/DebugInfo/CodeView/TypeRecords.def" default: break; } return "UnknownLeaf"; } namespace { struct HashLookupVisitor : public TypeVisitorCallbacks { struct Entry { TypeIndex TI; CVType Record; }; explicit HashLookupVisitor(TpiStream &Tpi) : Tpi(Tpi) {} Error visitTypeBegin(CVType &Record) override { uint32_t H = Tpi.getHashValues()[I]; Record.Hash = H; TypeIndex TI(I + TypeIndex::FirstNonSimpleIndex); Lookup[H].push_back(Entry{TI, Record}); ++I; return Error::success(); } uint32_t I = 0; DenseMap> Lookup; TpiStream &Tpi; }; } AnalysisStyle::AnalysisStyle(PDBFile &File) : File(File) {} Error AnalysisStyle::dump() { auto Tpi = File.getPDBTpiStream(); if (!Tpi) return Tpi.takeError(); TypeDatabase TypeDB(Tpi->getNumTypeRecords()); TypeDatabaseVisitor DBV(TypeDB); TypeVisitorCallbackPipeline Pipeline; HashLookupVisitor Hasher(*Tpi); // Add them to the database Pipeline.addCallbackToPipeline(DBV); // Store their hash values Pipeline.addCallbackToPipeline(Hasher); if (auto EC = codeview::visitTypeStream(Tpi->typeArray(), Pipeline)) return EC; auto &Adjusters = Tpi->getHashAdjusters(); DenseSet AdjusterSet; for (const auto &Adj : Adjusters) { assert(AdjusterSet.find(Adj.second) == AdjusterSet.end()); AdjusterSet.insert(Adj.second); } uint32_t Count = 0; outs() << "Searching for hash collisions\n"; for (const auto &H : Hasher.Lookup) { if (H.second.size() <= 1) continue; ++Count; outs() << formatv("Hash: {0}, Count: {1} records\n", H.first, H.second.size()); for (const auto &R : H.second) { auto Iter = AdjusterSet.find(R.TI.getIndex()); StringRef Prefix; if (Iter != AdjusterSet.end()) { Prefix = "[HEAD]"; AdjusterSet.erase(Iter); } StringRef LeafName = getLeafTypeName(R.Record.Type); uint32_t TI = R.TI.getIndex(); StringRef TypeName = TypeDB.getTypeName(R.TI); outs() << formatv("{0,-6} {1} ({2:x}) {3}\n", Prefix, LeafName, TI, TypeName); } } outs() << "\n"; outs() << "Dumping hash adjustment chains\n"; for (const auto &A : Tpi->getHashAdjusters()) { TypeIndex TI(A.second); StringRef TypeName = TypeDB.getTypeName(TI); const CVType &HeadRecord = TypeDB.getTypeRecord(TI); assert(HeadRecord.Hash.hasValue()); auto CollisionsIter = Hasher.Lookup.find(*HeadRecord.Hash); if (CollisionsIter == Hasher.Lookup.end()) continue; const auto &Collisions = CollisionsIter->second; outs() << TypeName << "\n"; outs() << formatv(" [HEAD] {0:x} {1} {2}\n", A.second, getLeafTypeName(HeadRecord.Type), TypeName); for (const auto &Chain : Collisions) { if (Chain.TI == TI) continue; const CVType &TailRecord = TypeDB.getTypeRecord(Chain.TI); outs() << formatv(" {0:x} {1} {2}\n", Chain.TI.getIndex(), getLeafTypeName(TailRecord.Type), TypeDB.getTypeName(Chain.TI)); } } outs() << formatv("There are {0} orphaned hash adjusters\n", AdjusterSet.size()); for (const auto &Adj : AdjusterSet) { outs() << formatv(" {0}\n", Adj); } uint32_t DistinctHashValues = Hasher.Lookup.size(); outs() << formatv("{0}/{1} hash collisions", Count, DistinctHashValues); return Error::success(); }