This is the beginning of an effort to move the codeview yaml reader / writer into ObjectYAML so that it can be shared. Currently the only consumer / producer of CodeView YAML is llvm-pdbdump, but CodeView can exist outside of PDB files, and indeed is put into object files and passed to the linker to produce PDB files. Furthermore, there are subtle differences in the types of records that show up in object file CodeView vs PDB file CodeView, but they are otherwise 99% the same. By having this code in ObjectYAML, we can have llvm-pdbdump reuse this code, while teaching obj2yaml and yaml2obj to use this syntax for dealing with object files that can contain CodeView. This patch only adds support for CodeView type information to ObjectYAML. Subsequent patches will add support for CodeView symbol information. llvm-svn: 304248
153 lines
4.7 KiB
C++
153 lines
4.7 KiB
C++
//===- 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/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 <list>
|
|
|
|
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/CodeViewTypes.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<uint32_t, std::list<Entry>> 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<uint32_t> 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();
|
|
}
|