[Symbolize] Check if the PE file has a PDB and emit an error if we can't load it

Summary:
Previously we would try to load PDBs for every PE executable we tried to
symbolize. If that failed, we would fall back to DWARF. If there wasn't
any DWARF, we'd print mostly useless symbol information using the export
table.

With this change, we only try to load PDBs for executables that claim to
have them. If that fails, we can now print an error rather than falling
back silently. This should make it a lot easier to diagnose and fix
common symbolization issues, such as not having DIA or not having a PDB.

Reviewers: zturner, eugenis

Subscribers: llvm-commits

Differential Revision: http://reviews.llvm.org/D20982

llvm-svn: 271725
This commit is contained in:
Reid Kleckner 2016-06-03 20:25:09 +00:00
parent 9faa5bcf13
commit f27f3f8491
9 changed files with 161 additions and 86 deletions

View File

@ -49,12 +49,12 @@ public:
flush(); flush();
} }
ErrorOr<DILineInfo> symbolizeCode(const std::string &ModuleName, Expected<DILineInfo> symbolizeCode(const std::string &ModuleName,
uint64_t ModuleOffset); uint64_t ModuleOffset);
ErrorOr<DIInliningInfo> symbolizeInlinedCode(const std::string &ModuleName, Expected<DIInliningInfo> symbolizeInlinedCode(const std::string &ModuleName,
uint64_t ModuleOffset); uint64_t ModuleOffset);
ErrorOr<DIGlobal> symbolizeData(const std::string &ModuleName, Expected<DIGlobal> symbolizeData(const std::string &ModuleName,
uint64_t ModuleOffset); uint64_t ModuleOffset);
void flush(); void flush();
static std::string DemangleName(const std::string &Name, static std::string DemangleName(const std::string &Name,
const SymbolizableModule *ModInfo); const SymbolizableModule *ModInfo);
@ -64,8 +64,13 @@ private:
// corresponding debug info. These objects can be the same. // corresponding debug info. These objects can be the same.
typedef std::pair<ObjectFile*, ObjectFile*> ObjectPair; typedef std::pair<ObjectFile*, ObjectFile*> ObjectPair;
ErrorOr<SymbolizableModule *> /// Returns a SymbolizableModule or an error if loading debug info failed.
/// Only one attempt is made to load a module, and errors during loading are
/// only reported once. Subsequent calls to get module info for a module that
/// failed to load will return nullptr.
Expected<SymbolizableModule *>
getOrCreateModuleInfo(const std::string &ModuleName); getOrCreateModuleInfo(const std::string &ModuleName);
ObjectFile *lookUpDsymFile(const std::string &Path, ObjectFile *lookUpDsymFile(const std::string &Path,
const MachOObjectFile *ExeObj, const MachOObjectFile *ExeObj,
const std::string &ArchName); const std::string &ArchName);
@ -74,27 +79,27 @@ private:
const std::string &ArchName); const std::string &ArchName);
/// \brief Returns pair of pointers to object and debug object. /// \brief Returns pair of pointers to object and debug object.
ErrorOr<ObjectPair> getOrCreateObjectPair(const std::string &Path, Expected<ObjectPair> getOrCreateObjectPair(const std::string &Path,
const std::string &ArchName); const std::string &ArchName);
/// \brief Return a pointer to object file at specified path, for a specified /// \brief Return a pointer to object file at specified path, for a specified
/// architecture (e.g. if path refers to a Mach-O universal binary, only one /// architecture (e.g. if path refers to a Mach-O universal binary, only one
/// object file from it will be returned). /// object file from it will be returned).
ErrorOr<ObjectFile *> getOrCreateObject(const std::string &Path, Expected<ObjectFile *> getOrCreateObject(const std::string &Path,
const std::string &ArchName); const std::string &ArchName);
std::map<std::string, ErrorOr<std::unique_ptr<SymbolizableModule>>> Modules; std::map<std::string, std::unique_ptr<SymbolizableModule>> Modules;
/// \brief Contains cached results of getOrCreateObjectPair(). /// \brief Contains cached results of getOrCreateObjectPair().
std::map<std::pair<std::string, std::string>, ErrorOr<ObjectPair>> std::map<std::pair<std::string, std::string>, ObjectPair>
ObjectPairForPathArch; ObjectPairForPathArch;
/// \brief Contains parsed binary for each path, or parsing error. /// \brief Contains parsed binary for each path, or parsing error.
std::map<std::string, ErrorOr<OwningBinary<Binary>>> BinaryForPath; std::map<std::string, OwningBinary<Binary>> BinaryForPath;
/// \brief Parsed object file for path/architecture pair, where "path" refers /// \brief Parsed object file for path/architecture pair, where "path" refers
/// to Mach-O universal binary. /// to Mach-O universal binary.
std::map<std::pair<std::string, std::string>, ErrorOr<std::unique_ptr<ObjectFile>>> std::map<std::pair<std::string, std::string>, std::unique_ptr<ObjectFile>>
ObjectForUBPathAndArch; ObjectForUBPathAndArch;
Options Opts; Options Opts;

View File

@ -870,10 +870,18 @@ public:
std::error_code getHintName(uint32_t Rva, uint16_t &Hint, std::error_code getHintName(uint32_t Rva, uint16_t &Hint,
StringRef &Name) const; StringRef &Name) const;
/// Get PDB information out of a codeview debug directory entry.
std::error_code getDebugPDBInfo(const debug_directory *DebugDir, std::error_code getDebugPDBInfo(const debug_directory *DebugDir,
const debug_pdb_info *&Info, const debug_pdb_info *&Info,
StringRef &PDBFileName) const; StringRef &PDBFileName) const;
/// Get PDB information from an executable. If the information is not present,
/// Info will be set to nullptr and PDBFileName will be empty. An error is
/// returned only on corrupt object files. Convenience accessor that can be
/// used if the debug directory is not already handy.
std::error_code getDebugPDBInfo(const debug_pdb_info *&Info,
StringRef &PDBFileName) const;
bool isRelocatableObject() const override; bool isRelocatableObject() const override;
bool is64() const { return PE32PlusHeader; } bool is64() const { return PE32PlusHeader; }

View File

@ -50,12 +50,18 @@
namespace llvm { namespace llvm {
namespace symbolize { namespace symbolize {
ErrorOr<DILineInfo> LLVMSymbolizer::symbolizeCode(const std::string &ModuleName, Expected<DILineInfo> LLVMSymbolizer::symbolizeCode(const std::string &ModuleName,
uint64_t ModuleOffset) { uint64_t ModuleOffset) {
auto InfoOrErr = getOrCreateModuleInfo(ModuleName); SymbolizableModule *Info;
if (auto EC = InfoOrErr.getError()) if (auto InfoOrErr = getOrCreateModuleInfo(ModuleName))
return EC; Info = InfoOrErr.get();
SymbolizableModule *Info = InfoOrErr.get(); else
return InfoOrErr.takeError();
// A null module means an error has already been reported. Return an empty
// result.
if (!Info)
return DILineInfo();
// If the user is giving us relative addresses, add the preferred base of the // If the user is giving us relative addresses, add the preferred base of the
// object to the offset before we do the query. It's what DIContext expects. // object to the offset before we do the query. It's what DIContext expects.
@ -69,13 +75,19 @@ ErrorOr<DILineInfo> LLVMSymbolizer::symbolizeCode(const std::string &ModuleName,
return LineInfo; return LineInfo;
} }
ErrorOr<DIInliningInfo> Expected<DIInliningInfo>
LLVMSymbolizer::symbolizeInlinedCode(const std::string &ModuleName, LLVMSymbolizer::symbolizeInlinedCode(const std::string &ModuleName,
uint64_t ModuleOffset) { uint64_t ModuleOffset) {
auto InfoOrErr = getOrCreateModuleInfo(ModuleName); SymbolizableModule *Info;
if (auto EC = InfoOrErr.getError()) if (auto InfoOrErr = getOrCreateModuleInfo(ModuleName))
return EC; Info = InfoOrErr.get();
SymbolizableModule *Info = InfoOrErr.get(); else
return InfoOrErr.takeError();
// A null module means an error has already been reported. Return an empty
// result.
if (!Info)
return DIInliningInfo();
// If the user is giving us relative addresses, add the preferred base of the // If the user is giving us relative addresses, add the preferred base of the
// object to the offset before we do the query. It's what DIContext expects. // object to the offset before we do the query. It's what DIContext expects.
@ -93,12 +105,18 @@ LLVMSymbolizer::symbolizeInlinedCode(const std::string &ModuleName,
return InlinedContext; return InlinedContext;
} }
ErrorOr<DIGlobal> LLVMSymbolizer::symbolizeData(const std::string &ModuleName, Expected<DIGlobal> LLVMSymbolizer::symbolizeData(const std::string &ModuleName,
uint64_t ModuleOffset) { uint64_t ModuleOffset) {
auto InfoOrErr = getOrCreateModuleInfo(ModuleName); SymbolizableModule *Info;
if (auto EC = InfoOrErr.getError()) if (auto InfoOrErr = getOrCreateModuleInfo(ModuleName))
return EC; Info = InfoOrErr.get();
SymbolizableModule *Info = InfoOrErr.get(); else
return InfoOrErr.takeError();
// A null module means an error has already been reported. Return an empty
// result.
if (!Info)
return DIGlobal();
// If the user is giving us relative addresses, add the preferred base of // If the user is giving us relative addresses, add the preferred base of
// the object to the offset before we do the query. It's what DIContext // the object to the offset before we do the query. It's what DIContext
@ -232,9 +250,14 @@ ObjectFile *LLVMSymbolizer::lookUpDsymFile(const std::string &ExePath,
} }
for (const auto &Path : DsymPaths) { for (const auto &Path : DsymPaths) {
auto DbgObjOrErr = getOrCreateObject(Path, ArchName); auto DbgObjOrErr = getOrCreateObject(Path, ArchName);
if (!DbgObjOrErr) if (!DbgObjOrErr) {
// Ignore errors, the file might not exist.
consumeError(DbgObjOrErr.takeError());
continue; continue;
}
ObjectFile *DbgObj = DbgObjOrErr.get(); ObjectFile *DbgObj = DbgObjOrErr.get();
if (!DbgObj)
continue;
const MachOObjectFile *MachDbgObj = dyn_cast<const MachOObjectFile>(DbgObj); const MachOObjectFile *MachDbgObj = dyn_cast<const MachOObjectFile>(DbgObj);
if (!MachDbgObj) if (!MachDbgObj)
continue; continue;
@ -255,23 +278,27 @@ ObjectFile *LLVMSymbolizer::lookUpDebuglinkObject(const std::string &Path,
if (!findDebugBinary(Path, DebuglinkName, CRCHash, DebugBinaryPath)) if (!findDebugBinary(Path, DebuglinkName, CRCHash, DebugBinaryPath))
return nullptr; return nullptr;
auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName); auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName);
if (!DbgObjOrErr) if (!DbgObjOrErr) {
// Ignore errors, the file might not exist.
consumeError(DbgObjOrErr.takeError());
return nullptr; return nullptr;
}
return DbgObjOrErr.get(); return DbgObjOrErr.get();
} }
ErrorOr<LLVMSymbolizer::ObjectPair> Expected<LLVMSymbolizer::ObjectPair>
LLVMSymbolizer::getOrCreateObjectPair(const std::string &Path, LLVMSymbolizer::getOrCreateObjectPair(const std::string &Path,
const std::string &ArchName) { const std::string &ArchName) {
const auto &I = ObjectPairForPathArch.find(std::make_pair(Path, ArchName)); const auto &I = ObjectPairForPathArch.find(std::make_pair(Path, ArchName));
if (I != ObjectPairForPathArch.end()) if (I != ObjectPairForPathArch.end()) {
return I->second; return I->second;
}
auto ObjOrErr = getOrCreateObject(Path, ArchName); auto ObjOrErr = getOrCreateObject(Path, ArchName);
if (auto EC = ObjOrErr.getError()) { if (!ObjOrErr) {
ObjectPairForPathArch.insert( ObjectPairForPathArch.insert(std::make_pair(std::make_pair(Path, ArchName),
std::make_pair(std::make_pair(Path, ArchName), EC)); ObjectPair(nullptr, nullptr)));
return EC; return ObjOrErr.takeError();
} }
ObjectFile *Obj = ObjOrErr.get(); ObjectFile *Obj = ObjOrErr.get();
@ -290,7 +317,7 @@ LLVMSymbolizer::getOrCreateObjectPair(const std::string &Path,
return Res; return Res;
} }
ErrorOr<ObjectFile *> Expected<ObjectFile *>
LLVMSymbolizer::getOrCreateObject(const std::string &Path, LLVMSymbolizer::getOrCreateObject(const std::string &Path,
const std::string &ArchName) { const std::string &ArchName) {
const auto &I = BinaryForPath.find(Path); const auto &I = BinaryForPath.find(Path);
@ -298,34 +325,29 @@ LLVMSymbolizer::getOrCreateObject(const std::string &Path,
if (I == BinaryForPath.end()) { if (I == BinaryForPath.end()) {
Expected<OwningBinary<Binary>> BinOrErr = createBinary(Path); Expected<OwningBinary<Binary>> BinOrErr = createBinary(Path);
if (!BinOrErr) { if (!BinOrErr) {
auto EC = errorToErrorCode(BinOrErr.takeError()); BinaryForPath.insert({Path, OwningBinary<Binary>()});
BinaryForPath.insert(std::make_pair(Path, EC)); return BinOrErr.takeError();
return EC;
} }
Bin = BinOrErr->getBinary(); Bin = BinOrErr->getBinary();
BinaryForPath.insert(std::make_pair(Path, std::move(BinOrErr.get()))); BinaryForPath.insert(std::make_pair(Path, std::move(BinOrErr.get())));
} else if (auto EC = I->second.getError()) {
return EC;
} else { } else {
Bin = I->second->getBinary(); Bin = I->second.getBinary();
} }
assert(Bin != nullptr); if (!Bin)
return static_cast<ObjectFile *>(nullptr);
if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(Bin)) { if (MachOUniversalBinary *UB = dyn_cast_or_null<MachOUniversalBinary>(Bin)) {
const auto &I = ObjectForUBPathAndArch.find(std::make_pair(Path, ArchName)); const auto &I = ObjectForUBPathAndArch.find(std::make_pair(Path, ArchName));
if (I != ObjectForUBPathAndArch.end()) { if (I != ObjectForUBPathAndArch.end()) {
if (auto EC = I->second.getError()) return I->second.get();
return EC;
return I->second->get();
} }
Expected<std::unique_ptr<ObjectFile>> ObjOrErr = Expected<std::unique_ptr<ObjectFile>> ObjOrErr =
UB->getObjectForArch(ArchName); UB->getObjectForArch(ArchName);
if (!ObjOrErr) { if (!ObjOrErr) {
auto EC = errorToErrorCode(ObjOrErr.takeError()); ObjectForUBPathAndArch.insert(std::make_pair(
ObjectForUBPathAndArch.insert( std::make_pair(Path, ArchName), std::unique_ptr<ObjectFile>()));
std::make_pair(std::make_pair(Path, ArchName), EC)); return ObjOrErr.takeError();
return EC;
} }
ObjectFile *Res = ObjOrErr->get(); ObjectFile *Res = ObjOrErr->get();
ObjectForUBPathAndArch.insert(std::make_pair(std::make_pair(Path, ArchName), ObjectForUBPathAndArch.insert(std::make_pair(std::make_pair(Path, ArchName),
@ -335,17 +357,14 @@ LLVMSymbolizer::getOrCreateObject(const std::string &Path,
if (Bin->isObject()) { if (Bin->isObject()) {
return cast<ObjectFile>(Bin); return cast<ObjectFile>(Bin);
} }
return object_error::arch_not_found; return errorCodeToError(object_error::arch_not_found);
} }
ErrorOr<SymbolizableModule *> Expected<SymbolizableModule *>
LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) { LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) {
const auto &I = Modules.find(ModuleName); const auto &I = Modules.find(ModuleName);
if (I != Modules.end()) { if (I != Modules.end()) {
auto &InfoOrErr = I->second; return I->second.get();
if (auto EC = InfoOrErr.getError())
return EC;
return InfoOrErr->get();
} }
std::string BinaryName = ModuleName; std::string BinaryName = ModuleName;
std::string ArchName = Opts.DefaultArch; std::string ArchName = Opts.DefaultArch;
@ -359,27 +378,31 @@ LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) {
} }
} }
auto ObjectsOrErr = getOrCreateObjectPair(BinaryName, ArchName); auto ObjectsOrErr = getOrCreateObjectPair(BinaryName, ArchName);
if (auto EC = ObjectsOrErr.getError()) { if (!ObjectsOrErr) {
// Failed to find valid object file. // Failed to find valid object file.
Modules.insert(std::make_pair(ModuleName, EC)); Modules.insert(
return EC; std::make_pair(ModuleName, std::unique_ptr<SymbolizableModule>()));
return ObjectsOrErr.takeError();
} }
ObjectPair Objects = ObjectsOrErr.get(); ObjectPair Objects = ObjectsOrErr.get();
std::unique_ptr<DIContext> Context; std::unique_ptr<DIContext> Context;
// If this is a COFF object containing PDB info, use a PDBContext to
// symbolize. Otherwise, use DWARF.
if (auto CoffObject = dyn_cast<COFFObjectFile>(Objects.first)) { if (auto CoffObject = dyn_cast<COFFObjectFile>(Objects.first)) {
using namespace pdb; const debug_pdb_info *PDBInfo;
// If this is a COFF object, assume it contains PDB debug information. If StringRef PDBFileName;
// we don't find any we will fall back to the DWARF case. auto EC = CoffObject->getDebugPDBInfo(PDBInfo, PDBFileName);
std::unique_ptr<IPDBSession> Session; if (!EC && PDBInfo != nullptr) {
auto Error = loadDataForEXE( using namespace pdb;
PDB_ReaderType::DIA, Objects.first->getFileName(), Session); std::unique_ptr<IPDBSession> Session;
if (!Error) { if (auto Err = loadDataForEXE(PDB_ReaderType::DIA,
Objects.first->getFileName(), Session)) {
Modules.insert(
std::make_pair(ModuleName, std::unique_ptr<SymbolizableModule>()));
return std::move(Err);
}
Context.reset(new PDBContext(*CoffObject, std::move(Session))); Context.reset(new PDBContext(*CoffObject, std::move(Session)));
} else {
// Drop error
handleAllErrors(std::move(Error),
[](const ErrorInfoBase &) { return Error::success(); });
} }
} }
if (!Context) if (!Context)
@ -387,12 +410,15 @@ LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) {
assert(Context); assert(Context);
auto InfoOrErr = auto InfoOrErr =
SymbolizableObjectFile::create(Objects.first, std::move(Context)); SymbolizableObjectFile::create(Objects.first, std::move(Context));
std::unique_ptr<SymbolizableModule> SymMod;
if (InfoOrErr)
SymMod = std::move(InfoOrErr.get());
auto InsertResult = auto InsertResult =
Modules.insert(std::make_pair(ModuleName, std::move(InfoOrErr))); Modules.insert(std::make_pair(ModuleName, std::move(SymMod)));
assert(InsertResult.second); assert(InsertResult.second);
if (auto EC = InsertResult.first->second.getError()) if (auto EC = InfoOrErr.getError())
return EC; return errorCodeToError(EC);
return InsertResult.first->second->get(); return InsertResult.first->second.get();
} }
namespace { namespace {

View File

@ -505,6 +505,17 @@ std::error_code COFFObjectFile::getDebugPDBInfo(const debug_directory *DebugDir,
return std::error_code(); return std::error_code();
} }
std::error_code COFFObjectFile::getDebugPDBInfo(const debug_pdb_info *&PDBInfo,
StringRef &PDBFileName) const {
for (const debug_directory &D : debug_directories())
if (D.Type == COFF::IMAGE_DEBUG_TYPE_CODEVIEW)
return getDebugPDBInfo(&D, PDBInfo, PDBFileName);
// If we get here, there is no PDB info to return.
PDBInfo = nullptr;
PDBFileName = StringRef();
return std::error_code();
}
// Find the import table. // Find the import table.
std::error_code COFFObjectFile::initImportTablePtr() { std::error_code COFFObjectFile::initImportTablePtr() {
// First, we get the RVA of the import table. If the file lacks a pointer to // First, we get the RVA of the import table. If the file lacks a pointer to

View File

@ -0,0 +1,17 @@
RUN: grep '^ADDR:' %s | sed -s 's/ADDR: //' \
RUN: | llvm-symbolizer -obj="%p/Inputs/missing_pdb.exe" 2>%t.err \
RUN: | FileCheck %s
RUN: FileCheck --check-prefix=ERROR %s < %t.err
ADDR: 0x401000
ADDR: 0x401001
llvm-symbolizer should print one error and two unknown line info records.
ERROR: LLVMSymbolizer: error reading file: PDB Error: Unable to load PDB. Make sure the file exists and is readable.
ERROR-NOT: error reading file
CHECK: ??
CHECK: ??:0:0
CHECK: ??
CHECK: ??:0:0

View File

@ -86,10 +86,12 @@ static cl::opt<int> ClPrintSourceContextLines(
"print-source-context-lines", cl::init(0), "print-source-context-lines", cl::init(0),
cl::desc("Print N number of source file context")); cl::desc("Print N number of source file context"));
static bool error(std::error_code ec) { template<typename T>
if (!ec) static bool error(Expected<T> &ResOrErr) {
if (ResOrErr)
return false; return false;
errs() << "LLVMSymbolizer: error reading file: " << ec.message() << ".\n"; logAllUnhandledErrors(ResOrErr.takeError(), errs(),
"LLVMSymbolizer: error reading file: ");
return true; return true;
} }
@ -185,14 +187,14 @@ int main(int argc, char **argv) {
} }
if (IsData) { if (IsData) {
auto ResOrErr = Symbolizer.symbolizeData(ModuleName, ModuleOffset); auto ResOrErr = Symbolizer.symbolizeData(ModuleName, ModuleOffset);
Printer << (error(ResOrErr.getError()) ? DIGlobal() : ResOrErr.get()); Printer << (error(ResOrErr) ? DIGlobal() : ResOrErr.get());
} else if (ClPrintInlining) { } else if (ClPrintInlining) {
auto ResOrErr = Symbolizer.symbolizeInlinedCode(ModuleName, ModuleOffset); auto ResOrErr = Symbolizer.symbolizeInlinedCode(ModuleName, ModuleOffset);
Printer << (error(ResOrErr.getError()) ? DIInliningInfo() Printer << (error(ResOrErr) ? DIInliningInfo()
: ResOrErr.get()); : ResOrErr.get());
} else { } else {
auto ResOrErr = Symbolizer.symbolizeCode(ModuleName, ModuleOffset); auto ResOrErr = Symbolizer.symbolizeCode(ModuleName, ModuleOffset);
Printer << (error(ResOrErr.getError()) ? DILineInfo() : ResOrErr.get()); Printer << (error(ResOrErr) ? DILineInfo() : ResOrErr.get());
} }
outs() << "\n"; outs() << "\n";
outs().flush(); outs().flush();

View File

@ -135,6 +135,12 @@ template <typename T> static void FailIfError(const ErrorOr<T> &E) {
FailIfError(E.getError()); FailIfError(E.getError());
} }
template <typename T> static void FailIfError(Expected<T> &E) {
if (E)
return;
logAllUnhandledErrors(E.takeError(), errs(), "Error: ");
}
static void FailIfNotEmpty(const llvm::Twine &E) { static void FailIfNotEmpty(const llvm::Twine &E) {
if (E.str().empty()) if (E.str().empty())
return; return;

View File

@ -76,12 +76,12 @@ const char *ReadModule(char SizeofPtr, const char *Begin, const char *End) {
if (Begin == End) if (Begin == End)
return nullptr; return nullptr;
ErrorOr<DILineInfo> LineInfo = Symbolizer.symbolizeCode(Filename, Addr); if (Expected<DILineInfo> LineInfo =
if (LineInfo) { Symbolizer.symbolizeCode(Filename, Addr)) {
llvm::outs() << LineInfo->FileName << ':' << LineInfo->Line << ' ' llvm::outs() << LineInfo->FileName << ':' << LineInfo->Line << ' '
<< LineInfo->FunctionName << ' '; << LineInfo->FunctionName << ' ';
} else { } else {
llvm::outs() << "<error> "; logAllUnhandledErrors(LineInfo.takeError(), llvm::outs(), "<error> ");
} }
switch (KindFromData(Data, SizeofPtr)) { switch (KindFromData(Data, SizeofPtr)) {