diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-04-16 16:01:22 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-04-16 16:01:22 +0000 |
commit | 71d5a2540a98c81f5bcaeb48805e0e2881f530ef (patch) | |
tree | 5343938942df402b49ec7300a1c25a2d4ccd5821 /tools | |
parent | 31bbf64f3a4974a2d6c8b3b27ad2f519caf74057 (diff) |
Notes
Diffstat (limited to 'tools')
122 files changed, 6124 insertions, 2940 deletions
diff --git a/tools/bugpoint/CrashDebugger.cpp b/tools/bugpoint/CrashDebugger.cpp index 0cae0669477f2..2fd8699c5fc8f 100644 --- a/tools/bugpoint/CrashDebugger.cpp +++ b/tools/bugpoint/CrashDebugger.cpp @@ -731,7 +731,8 @@ bool ReduceCrashingInstructions::TestInsts( for (BasicBlock::iterator I = FI->begin(), E = FI->end(); I != E;) { Instruction *Inst = &*I++; if (!Instructions.count(Inst) && !isa<TerminatorInst>(Inst) && - !Inst->isEHPad() && !Inst->getType()->isTokenTy()) { + !Inst->isEHPad() && !Inst->getType()->isTokenTy() && + !Inst->isSwiftError()) { if (!Inst->getType()->isVoidTy()) Inst->replaceAllUsesWith(UndefValue::get(Inst->getType())); Inst->eraseFromParent(); @@ -1015,7 +1016,8 @@ static Error ReduceInsts(BugDriver &BD, // TODO: Should this be some kind of interrupted error? return Error::success(); - if (I->isEHPad() || I->getType()->isTokenTy()) + if (I->isEHPad() || I->getType()->isTokenTy() || + I->isSwiftError()) continue; outs() << "Checking instruction: " << *I; @@ -1111,7 +1113,7 @@ static Error DebugACrash(BugDriver &BD, BD.EmitProgressBitcode(BD.getProgram(), "reduced-blocks"); } - if (!DisableSimplifyCFG & !BugpointIsInterrupted) { + if (!DisableSimplifyCFG && !BugpointIsInterrupted) { std::vector<const BasicBlock *> Blocks; for (Function &F : *BD.getProgram()) for (BasicBlock &BB : F) diff --git a/tools/bugpoint/ExtractFunction.cpp b/tools/bugpoint/ExtractFunction.cpp index d57613ec5e376..82c61b6e1be7a 100644 --- a/tools/bugpoint/ExtractFunction.cpp +++ b/tools/bugpoint/ExtractFunction.cpp @@ -209,6 +209,7 @@ static void eliminateAliases(GlobalValue *GV) { void llvm::DeleteGlobalInitializer(GlobalVariable *GV) { eliminateAliases(GV); GV->setInitializer(nullptr); + GV->setComdat(nullptr); } // DeleteFunctionBody - "Remove" the function by deleting all of its basic diff --git a/tools/bugpoint/FindBugs.cpp b/tools/bugpoint/FindBugs.cpp index 156f4d0d78fe1..3093169ba8b00 100644 --- a/tools/bugpoint/FindBugs.cpp +++ b/tools/bugpoint/FindBugs.cpp @@ -21,6 +21,7 @@ #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <ctime> +#include <random> using namespace llvm; Error @@ -39,14 +40,13 @@ BugDriver::runManyPasses(const std::vector<std::string> &AllPasses) { return E; } - srand(time(nullptr)); - + std::mt19937 randomness(std::random_device{}()); unsigned num = 1; while (1) { // // Step 1: Randomize the order of the optimizer passes. // - std::random_shuffle(PassesToRun.begin(), PassesToRun.end()); + std::shuffle(PassesToRun.begin(), PassesToRun.end(), randomness); // // Step 2: Run optimizer passes on the program and check for success. diff --git a/tools/bugpoint/ListReducer.h b/tools/bugpoint/ListReducer.h index dcfa11d06927f..0f9db022d555e 100644 --- a/tools/bugpoint/ListReducer.h +++ b/tools/bugpoint/ListReducer.h @@ -19,6 +19,7 @@ #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cstdlib> +#include <random> #include <vector> namespace llvm { @@ -46,7 +47,7 @@ template <typename ElTy> struct ListReducer { /// that bugpoint does. Expected<bool> reduceList(std::vector<ElTy> &TheList) { std::vector<ElTy> empty; - std::srand(0x6e5ea738); // Seed the random number generator + std::mt19937 randomness(0x6e5ea738); // Seed the random number generator Expected<TestResult> Result = doTest(TheList, empty); if (Error E = Result.takeError()) return std::move(E); @@ -92,7 +93,7 @@ template <typename ElTy> struct ListReducer { // distribution (improving the speed of convergence). if (ShufflingEnabled && NumOfIterationsWithoutProgress > MaxIterations) { std::vector<ElTy> ShuffledList(TheList); - std::random_shuffle(ShuffledList.begin(), ShuffledList.end()); + std::shuffle(ShuffledList.begin(), ShuffledList.end(), randomness); errs() << "\n\n*** Testing shuffled set...\n\n"; // Check that random shuffle doesn't lose the bug Expected<TestResult> Result = doTest(ShuffledList, empty); diff --git a/tools/bugpoint/Miscompilation.cpp b/tools/bugpoint/Miscompilation.cpp index 792fab07bf11e..80f4cea234815 100644 --- a/tools/bugpoint/Miscompilation.cpp +++ b/tools/bugpoint/Miscompilation.cpp @@ -225,19 +225,22 @@ public: /// output is different. If the DeleteInputs argument is set to true then this /// function deletes both input modules before it returns. /// -static Expected<std::unique_ptr<Module>> -testMergedProgram(const BugDriver &BD, std::unique_ptr<Module> M1, - std::unique_ptr<Module> M2, bool &Broken) { - if (Linker::linkModules(*M1, std::move(M2))) +static Expected<std::unique_ptr<Module>> testMergedProgram(const BugDriver &BD, + const Module &M1, + const Module &M2, + bool &Broken) { + // Resulting merge of M1 and M2. + auto Merged = CloneModule(&M1); + if (Linker::linkModules(*Merged, CloneModule(&M2))) // TODO: Shouldn't we thread the error up instead of exiting? exit(1); // Execute the program. - Expected<bool> Diff = BD.diffProgram(M1.get(), "", "", false); + Expected<bool> Diff = BD.diffProgram(Merged.get(), "", "", false); if (Error E = Diff.takeError()) return std::move(E); Broken = *Diff; - return std::move(M1); + return std::move(Merged); } /// TestFuncs - split functions in a Module into two groups: those that are @@ -335,9 +338,8 @@ ExtractLoops(BugDriver &BD, // extraction. AbstractInterpreter *AI = BD.switchToSafeInterpreter(); bool Failure; - Expected<std::unique_ptr<Module>> New = - testMergedProgram(BD, std::move(ToOptimizeLoopExtracted), - std::move(ToNotOptimize), Failure); + Expected<std::unique_ptr<Module>> New = testMergedProgram( + BD, *ToOptimizeLoopExtracted, *ToNotOptimize, Failure); if (Error E = New.takeError()) return std::move(E); if (!*New) @@ -726,8 +728,7 @@ static Expected<bool> TestOptimizer(BugDriver &BD, std::unique_ptr<Module> Test, outs() << " Checking to see if the merged program executes correctly: "; bool Broken; - auto Result = - testMergedProgram(BD, std::move(Optimized), std::move(Safe), Broken); + auto Result = testMergedProgram(BD, *Optimized, *Safe, Broken); if (Error E = Result.takeError()) return std::move(E); if (auto New = std::move(*Result)) { @@ -840,7 +841,7 @@ static void CleanupAndPrepareModules(BugDriver &BD, // Prototype: void *getPointerToNamedFunction(const char* Name) Constant *resolverFunc = Safe->getOrInsertFunction( "getPointerToNamedFunction", Type::getInt8PtrTy(Safe->getContext()), - Type::getInt8PtrTy(Safe->getContext()), (Type *)nullptr); + Type::getInt8PtrTy(Safe->getContext())); // Use the function we just added to get addresses of functions we need. for (Module::iterator F = Safe->begin(), E = Safe->end(); F != E; ++F) { diff --git a/tools/bugpoint/ToolRunner.cpp b/tools/bugpoint/ToolRunner.cpp index 4633d64373368..10532ef8395b8 100644 --- a/tools/bugpoint/ToolRunner.cpp +++ b/tools/bugpoint/ToolRunner.cpp @@ -355,37 +355,62 @@ Expected<int> CustomExecutor::ExecuteProgram( // Tokenize the CommandLine to the command and the args to allow // defining a full command line as the command instead of just the // executed program. We cannot just pass the whole string after the command -// as a single argument because then program sees only a single +// as a single argument because then the program sees only a single // command line argument (with spaces in it: "foo bar" instead // of "foo" and "bar"). // -// code borrowed from: -// http://oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html +// Spaces are used as a delimiter; however repeated, leading, and trailing +// whitespace are ignored. Simple escaping is allowed via the '\' +// character, as seen below: +// +// Two consecutive '\' evaluate to a single '\'. +// A space after a '\' evaluates to a space that is not interpreted as a +// delimiter. +// Any other instances of the '\' character are removed. +// +// Example: +// '\\' -> '\' +// '\ ' -> ' ' +// 'exa\mple' -> 'example' +// static void lexCommand(std::string &Message, const std::string &CommandLine, std::string &CmdPath, std::vector<std::string> &Args) { - std::string Command = ""; - std::string delimiters = " "; - - std::string::size_type lastPos = CommandLine.find_first_not_of(delimiters, 0); - std::string::size_type pos = CommandLine.find_first_of(delimiters, lastPos); - - while (std::string::npos != pos || std::string::npos != lastPos) { - std::string token = CommandLine.substr(lastPos, pos - lastPos); - if (Command == "") - Command = token; - else - Args.push_back(token); - // Skip delimiters. Note the "not_of" - lastPos = CommandLine.find_first_not_of(delimiters, pos); - // Find next "non-delimiter" - pos = CommandLine.find_first_of(delimiters, lastPos); + std::string Token; + std::string Command; + bool FoundPath = false; + + // first argument is the PATH. + // Skip repeated whitespace, leading whitespace and trailing whitespace. + for (std::size_t Pos = 0u; Pos <= CommandLine.size(); ++Pos) { + if ('\\' == CommandLine[Pos]) { + if (Pos + 1 < CommandLine.size()) + Token.push_back(CommandLine[++Pos]); + + continue; + } + if (' ' == CommandLine[Pos] || CommandLine.size() == Pos) { + if (Token.empty()) + continue; + + if (!FoundPath) { + Command = Token; + FoundPath = true; + Token.clear(); + continue; + } + + Args.push_back(Token); + Token.clear(); + continue; + } + Token.push_back(CommandLine[Pos]); } auto Path = sys::findProgramByName(Command); if (!Path) { - Message = std::string("Cannot find '") + Command + "' in PATH: " + - Path.getError().message() + "\n"; + Message = std::string("Cannot find '") + Command + + "' in PATH: " + Path.getError().message() + "\n"; return; } CmdPath = *Path; diff --git a/tools/bugpoint/bugpoint.cpp b/tools/bugpoint/bugpoint.cpp index a5de953b2b75b..85c1ddd8277d9 100644 --- a/tools/bugpoint/bugpoint.cpp +++ b/tools/bugpoint/bugpoint.cpp @@ -181,7 +181,8 @@ int main(int argc, char **argv) { if (OptLevelO1) Builder.Inliner = createAlwaysInlinerLegacyPass(); else if (OptLevelOs || OptLevelO2) - Builder.Inliner = createFunctionInliningPass(2, OptLevelOs ? 1 : 0); + Builder.Inliner = createFunctionInliningPass( + 2, OptLevelOs ? 1 : 0, false); else Builder.Inliner = createFunctionInliningPass(275); Builder.populateFunctionPassManager(PM); diff --git a/tools/dsymutil/DwarfLinker.cpp b/tools/dsymutil/DwarfLinker.cpp index f1ec8a6226712..25f1a0f271223 100644 --- a/tools/dsymutil/DwarfLinker.cpp +++ b/tools/dsymutil/DwarfLinker.cpp @@ -197,30 +197,34 @@ public: CompileUnit(DWARFUnit &OrigUnit, unsigned ID, bool CanUseODR, StringRef ClangModuleName) - : OrigUnit(OrigUnit), ID(ID), NewUnit(OrigUnit.getVersion(), - OrigUnit.getAddressByteSize(), - OrigUnit.getUnitDIE().getTag()), - LowPc(UINT64_MAX), HighPc(0), RangeAlloc(), Ranges(RangeAlloc), - ClangModuleName(ClangModuleName) { + : OrigUnit(OrigUnit), ID(ID), LowPc(UINT64_MAX), HighPc(0), RangeAlloc(), + Ranges(RangeAlloc), ClangModuleName(ClangModuleName) { Info.resize(OrigUnit.getNumDIEs()); auto CUDie = OrigUnit.getUnitDIE(false); - unsigned Lang = - CUDie.getAttributeValueAsUnsignedConstant(dwarf::DW_AT_language) - .getValueOr(0); - HasODR = CanUseODR && (Lang == dwarf::DW_LANG_C_plus_plus || - Lang == dwarf::DW_LANG_C_plus_plus_03 || - Lang == dwarf::DW_LANG_C_plus_plus_11 || - Lang == dwarf::DW_LANG_C_plus_plus_14 || - Lang == dwarf::DW_LANG_ObjC_plus_plus); + if (auto Lang = dwarf::toUnsigned(CUDie.find(dwarf::DW_AT_language))) + HasODR = CanUseODR && (*Lang == dwarf::DW_LANG_C_plus_plus || + *Lang == dwarf::DW_LANG_C_plus_plus_03 || + *Lang == dwarf::DW_LANG_C_plus_plus_11 || + *Lang == dwarf::DW_LANG_C_plus_plus_14 || + *Lang == dwarf::DW_LANG_ObjC_plus_plus); + else + HasODR = false; } DWARFUnit &getOrigUnit() const { return OrigUnit; } unsigned getUniqueID() const { return ID; } + void createOutputDIE() { + NewUnit.emplace(OrigUnit.getVersion(), OrigUnit.getAddressByteSize(), + OrigUnit.getUnitDIE().getTag()); + } + DIE *getOutputUnitDIE() const { - return &const_cast<DIEUnit &>(NewUnit).getUnitDie(); + if (NewUnit) + return &const_cast<DIEUnit &>(*NewUnit).getUnitDie(); + return nullptr; } bool hasODR() const { return HasODR; } @@ -329,7 +333,7 @@ private: DWARFUnit &OrigUnit; unsigned ID; std::vector<DIEInfo> Info; ///< DIE info indexed by DIE index. - DIEUnit NewUnit; + Optional<DIEUnit> NewUnit; uint64_t StartOffset; uint64_t NextUnitOffset; @@ -359,7 +363,7 @@ private: Optional<PatchLocation> UnitRangeAttribute; /// @} - /// \brief Location attributes that need to be transfered from th + /// \brief Location attributes that need to be transferred from the /// original debug_loc section to the liked one. They are stored /// along with the PC offset that is to be applied to their /// function's address. @@ -397,7 +401,8 @@ uint64_t CompileUnit::computeNextUnitOffset() { // The root DIE might be null, meaning that the Unit had nothing to // contribute to the linked output. In that case, we will emit the // unit header without any actual DIE. - NextUnitOffset += NewUnit.getUnitDie().getSize(); + if (NewUnit) + NextUnitOffset += NewUnit->getUnitDie().getSize(); return NextUnitOffset; } @@ -843,8 +848,7 @@ void DwarfStreamer::emitLocationsForUnit(const CompileUnit &Unit, DWARFUnit &OrigUnit = Unit.getOrigUnit(); auto OrigUnitDie = OrigUnit.getUnitDIE(false); int64_t UnitPcOffset = 0; - auto OrigLowPc = OrigUnitDie.getAttributeValueAsAddress(dwarf::DW_AT_low_pc); - if (OrigLowPc) + if (auto OrigLowPc = dwarf::toAddress(OrigUnitDie.find(dwarf::DW_AT_low_pc))) UnitPcOffset = int64_t(*OrigLowPc) - Unit.getLowPc(); for (const auto &Attr : Attributes) { @@ -1080,7 +1084,7 @@ void DwarfStreamer::emitCIE(StringRef CIEBytes) { /// \brief Emit a FDE into the debug_frame section. \p FDEBytes /// contains the FDE data without the length, CIE offset and address -/// which will be replaced with the paramter values. +/// which will be replaced with the parameter values. void DwarfStreamer::emitFDE(uint32_t CIEOffset, uint32_t AddrSize, uint32_t Address, StringRef FDEBytes) { MS->SwitchSection(MC->getObjectFileInfo()->getDwarfFrameSection()); @@ -1558,8 +1562,7 @@ PointerIntPair<DeclContext *, 1> DeclContextTree::getChildDeclContext( // Do not unique anything inside CU local functions. if ((Context.getTag() == dwarf::DW_TAG_namespace || Context.getTag() == dwarf::DW_TAG_compile_unit) && - !DIE.getAttributeValueAsUnsignedConstant(dwarf::DW_AT_external) - .getValueOr(0)) + !dwarf::toUnsigned(DIE.find(dwarf::DW_AT_external), 0)) return PointerIntPair<DeclContext *, 1>(nullptr); LLVM_FALLTHROUGH; case dwarf::DW_TAG_member: @@ -1573,8 +1576,7 @@ PointerIntPair<DeclContext *, 1> DeclContextTree::getChildDeclContext( // created on demand. For example implicitely defined constructors // are ambiguous because of the way we identify contexts, and they // won't be generated everytime everywhere. - if (DIE.getAttributeValueAsUnsignedConstant(dwarf::DW_AT_artificial) - .getValueOr(0)) + if (dwarf::toUnsigned(DIE.find(dwarf::DW_AT_artificial), 0)) return PointerIntPair<DeclContext *, 1>(nullptr); break; } @@ -1614,12 +1616,9 @@ PointerIntPair<DeclContext *, 1> DeclContextTree::getChildDeclContext( // namespaces, use these additional data points to make the process // safer. This is disabled for clang modules, because forward // declarations of module-defined types do not have a file and line. - ByteSize = DIE.getAttributeValueAsUnsignedConstant(dwarf::DW_AT_byte_size) - .getValueOr(UINT64_MAX); + ByteSize = dwarf::toUnsigned(DIE.find(dwarf::DW_AT_byte_size), UINT64_MAX); if (Tag != dwarf::DW_TAG_namespace || !Name) { - if (unsigned FileNum = - DIE.getAttributeValueAsUnsignedConstant(dwarf::DW_AT_decl_file) - .getValueOr(0)) { + if (unsigned FileNum = dwarf::toUnsigned(DIE.find(dwarf::DW_AT_decl_file), 0)) { if (const auto *LT = U.getOrigUnit().getContext().getLineTableForUnit( &U.getOrigUnit())) { // FIXME: dsymutil-classic compatibility. I'd rather not @@ -1632,9 +1631,7 @@ PointerIntPair<DeclContext *, 1> DeclContextTree::getChildDeclContext( // instead of "" would allow more uniquing, but for now, do // it this way to match dsymutil-classic. if (LT->hasFileAtIndex(FileNum)) { - Line = - DIE.getAttributeValueAsUnsignedConstant(dwarf::DW_AT_decl_line) - .getValueOr(0); + Line = dwarf::toUnsigned(DIE.find(dwarf::DW_AT_decl_line), 0); // Cache the resolved paths, because calling realpath is expansive. StringRef ResolvedPath = U.getResolvedPath(FileNum); if (!ResolvedPath.empty()) { @@ -1782,8 +1779,7 @@ static bool analyzeContextInfo(const DWARFDie &DIE, // // We treat non-C++ modules like namespaces for this reason. if (DIE.getTag() == dwarf::DW_TAG_module && ParentIdx == 0 && - DIE.getAttributeValueAsString(dwarf::DW_AT_name, - "") != CU.getClangModuleName()) { + dwarf::toString(DIE.find(dwarf::DW_AT_name), "") != CU.getClangModuleName()) { InImportedModule = true; } @@ -1811,8 +1807,7 @@ static bool analyzeContextInfo(const DWARFDie &DIE, // forward declarations. Info.Prune &= (DIE.getTag() == dwarf::DW_TAG_module) || - DIE.getAttributeValueAsUnsignedConstant(dwarf::DW_AT_declaration) - .getValueOr(0); + dwarf::toUnsigned(DIE.find(dwarf::DW_AT_declaration), 0); // Don't prune it if there is no definition for the DIE. Info.Prune &= Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset(); @@ -2129,7 +2124,7 @@ unsigned DwarfLinker::shouldKeepSubprogramDIE( std::tie(LowPcOffset, LowPcEndOffset) = getAttributeOffsets(Abbrev, *LowPcIdx, Offset, OrigUnit); - auto LowPc = DIE.getAttributeValueAsAddress(dwarf::DW_AT_low_pc); + auto LowPc = dwarf::toAddress(DIE.find(dwarf::DW_AT_low_pc)); assert(LowPc.hasValue() && "low_pc attribute is not an address."); if (!LowPc || !RelocMgr.hasValidRelocation(LowPcOffset, LowPcEndOffset, MyInfo)) @@ -2746,14 +2741,11 @@ DIE *DwarfLinker::DIECloner::cloneDIE( // file might be start address of another function which got moved // independantly by the linker). The computation of the actual // high_pc value is done in cloneAddressAttribute(). - AttrInfo.OrigHighPc = - InputDIE.getAttributeValueAsAddress(dwarf::DW_AT_high_pc).getValueOr(0); + AttrInfo.OrigHighPc = dwarf::toAddress(InputDIE.find(dwarf::DW_AT_high_pc), 0); // Also store the low_pc. It might get relocated in an // inline_subprogram that happens at the beginning of its // inlining function. - AttrInfo.OrigLowPc = - InputDIE.getAttributeValueAsAddress(dwarf::DW_AT_low_pc) - .getValueOr(UINT64_MAX); + AttrInfo.OrigLowPc = dwarf::toAddress(InputDIE.find(dwarf::DW_AT_low_pc), UINT64_MAX); } // Reset the Offset to 0 as we will be working on the local copy of @@ -2872,9 +2864,7 @@ void DwarfLinker::patchRangesForUnit(const CompileUnit &Unit, auto InvalidRange = FunctionRanges.end(), CurrRange = InvalidRange; DWARFUnit &OrigUnit = Unit.getOrigUnit(); auto OrigUnitDie = OrigUnit.getUnitDIE(false); - uint64_t OrigLowPc = - OrigUnitDie.getAttributeValueAsAddress(dwarf::DW_AT_low_pc) - .getValueOr(-1ULL); + uint64_t OrigLowPc = dwarf::toAddress(OrigUnitDie.find(dwarf::DW_AT_low_pc), -1ULL); // Ranges addresses are based on the unit's low_pc. Compute the // offset we need to apply to adapt to the new unit's low_pc. int64_t UnitPcOffset = 0; @@ -2969,7 +2959,7 @@ static void patchStmtList(DIE &Die, DIEInteger Offset) { void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit, DWARFContext &OrigDwarf) { DWARFDie CUDie = Unit.getOrigUnit().getUnitDIE(); - auto StmtList = CUDie.getAttributeValueAsSectionOffset(dwarf::DW_AT_stmt_list); + auto StmtList = dwarf::toSectionOffset(CUDie.find(dwarf::DW_AT_stmt_list)); if (!StmtList) return; @@ -3081,7 +3071,7 @@ void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit, if (LineTable.Prologue.Version != 2 || LineTable.Prologue.DefaultIsStmt != DWARF2_LINE_DEFAULT_IS_STMT || LineTable.Prologue.OpcodeBase > 13) - reportWarning("line table paramters mismatch. Cannot emit."); + reportWarning("line table parameters mismatch. Cannot emit."); else { MCDwarfLineTableParams Params; Params.DWARF2LineOpcodeBase = LineTable.Prologue.OpcodeBase; @@ -3201,10 +3191,8 @@ void DwarfLinker::DIECloner::copyAbbrev( static uint64_t getDwoId(const DWARFDie &CUDie, const DWARFUnit &Unit) { - auto DwoId = CUDie.getAttributeValueAsUnsignedConstant(dwarf::DW_AT_dwo_id); - if (DwoId) - return *DwoId; - DwoId = CUDie.getAttributeValueAsUnsignedConstant(dwarf::DW_AT_GNU_dwo_id); + auto DwoId = dwarf::toUnsigned(CUDie.find({dwarf::DW_AT_dwo_id, + dwarf::DW_AT_GNU_dwo_id})); if (DwoId) return *DwoId; return 0; @@ -3214,20 +3202,16 @@ bool DwarfLinker::registerModuleReference( const DWARFDie &CUDie, const DWARFUnit &Unit, DebugMap &ModuleMap, unsigned Indent) { std::string PCMfile = - CUDie.getAttributeValueAsString(dwarf::DW_AT_dwo_name, ""); - if (PCMfile.empty()) - PCMfile = - CUDie.getAttributeValueAsString(dwarf::DW_AT_GNU_dwo_name, ""); + dwarf::toString(CUDie.find({dwarf::DW_AT_dwo_name, + dwarf::DW_AT_GNU_dwo_name}), ""); if (PCMfile.empty()) return false; // Clang module DWARF skeleton CUs abuse this for the path to the module. - std::string PCMpath = - CUDie.getAttributeValueAsString(dwarf::DW_AT_comp_dir, ""); + std::string PCMpath = dwarf::toString(CUDie.find(dwarf::DW_AT_comp_dir), ""); uint64_t DwoId = getDwoId(CUDie, Unit); - std::string Name = - CUDie.getAttributeValueAsString(dwarf::DW_AT_name, ""); + std::string Name = dwarf::toString(CUDie.find(dwarf::DW_AT_name), ""); if (Name.empty()) { reportWarning("Anonymous module skeleton CU for " + PCMfile); return true; @@ -3378,12 +3362,13 @@ void DwarfLinker::DIECloner::cloneAllCompileUnits( for (auto &CurrentUnit : CompileUnits) { auto InputDIE = CurrentUnit->getOrigUnit().getUnitDIE(); CurrentUnit->setStartOffset(Linker.OutputDebugInfoSize); - // Clonse the InputDIE into your Unit DIE in our compile unit since it - // already has a DIE inside of it. - if (!cloneDIE(InputDIE, *CurrentUnit, 0 /* PC offset */, - 11 /* Unit Header size */, 0, - CurrentUnit->getOutputUnitDIE())) - continue; + if (CurrentUnit->getInfo(0).Keep) { + // Clone the InputDIE into your Unit DIE in our compile unit since it + // already has a DIE inside of it. + CurrentUnit->createOutputDIE(); + cloneDIE(InputDIE, *CurrentUnit, 0 /* PC offset */, + 11 /* Unit Header size */, 0, CurrentUnit->getOutputUnitDIE()); + } Linker.OutputDebugInfoSize = CurrentUnit->computeNextUnitOffset(); if (Linker.Options.NoOutput) continue; diff --git a/tools/dsymutil/MachOUtils.cpp b/tools/dsymutil/MachOUtils.cpp index 8a730a1d0c8a2..ea6f113e4fae6 100644 --- a/tools/dsymutil/MachOUtils.cpp +++ b/tools/dsymutil/MachOUtils.cpp @@ -220,7 +220,7 @@ getSection(const object::MachOObjectFile &Obj, // The function also tries to find a hole in the address map to fit the __DWARF // segment of \a DwarfSegmentSize size. \a EndAddress is updated to point at the // highest segment address. -// When the __LINKEDIT segment is transfered, its offset and size are set resp. +// When the __LINKEDIT segment is transferred, its offset and size are set resp. // to \a LinkeditOffset and \a LinkeditSize. template <typename SegmentTy> static void transferSegmentAndSections( @@ -236,6 +236,8 @@ static void transferSegmentAndSections( if (StringRef("__LINKEDIT") == Segment.segname) { Segment.fileoff = LinkeditOffset; Segment.filesize = LinkeditSize; + // Resize vmsize by rounding to the page size. + Segment.vmsize = alignTo(LinkeditSize, 0x1000); } // Check if the end address of the last segment and our current diff --git a/tools/gold/gold-plugin.cpp b/tools/gold/gold-plugin.cpp index 50e102b045953..9b783d19a2834 100644 --- a/tools/gold/gold-plugin.cpp +++ b/tools/gold/gold-plugin.cpp @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/Statistic.h" #include "llvm/Bitcode/BitcodeReader.h" #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/CodeGen/CommandFlags.h" @@ -20,7 +21,9 @@ #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/LTO/Caching.h" #include "llvm/LTO/LTO.h" +#include "llvm/Object/Error.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" @@ -105,8 +108,6 @@ static std::list<claimed_file> Modules; static DenseMap<int, void *> FDToLeaderHandle; static StringMap<ResolutionInfo> ResInfo; static std::vector<std::string> Cleanup; -static llvm::TargetOptions TargetOpts; -static size_t MaxTasks; namespace options { enum OutputType { @@ -165,6 +166,12 @@ namespace options { // corresponding bitcode file, will use a path formed by replacing the // bitcode file's path prefix matching oldprefix with newprefix. static std::string thinlto_prefix_replace; + // Option to control the name of modules encoded in the individual index + // files for a distributed backend. This enables the use of minimized + // bitcode files for the thin link, assuming the name of the full bitcode + // file used in the backend differs just in some part of the file suffix. + // If specified, expects a string of the form "oldsuffix:newsuffix". + static std::string thinlto_object_suffix_replace; // Optional path to a directory for caching ThinLTO objects. static std::string cache_dir; // Additional options to pass into the code generator. @@ -207,6 +214,12 @@ namespace options { thinlto_prefix_replace = opt.substr(strlen("thinlto-prefix-replace=")); if (thinlto_prefix_replace.find(';') == std::string::npos) message(LDPL_FATAL, "thinlto-prefix-replace expects 'old;new' format"); + } else if (opt.startswith("thinlto-object-suffix-replace=")) { + thinlto_object_suffix_replace = + opt.substr(strlen("thinlto-object-suffix-replace=")); + if (thinlto_object_suffix_replace.find(';') == std::string::npos) + message(LDPL_FATAL, + "thinlto-object-suffix-replace expects 'old;new' format"); } else if (opt.startswith("cache-dir=")) { cache_dir = opt.substr(strlen("cache-dir=")); } else if (opt.size() == 2 && opt[0] == 'O') { @@ -452,7 +465,7 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, EC == object::object_error::bitcode_section_not_found) *claimed = 0; else - message(LDPL_ERROR, + message(LDPL_FATAL, "LLVM gold plugin has failed to create LTO module: %s", EI.message().c_str()); }); @@ -485,8 +498,6 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, sys::path::filename(Obj->getSourceFileName()).str(); for (auto &Sym : Obj->symbols()) { - uint32_t Symflags = Sym.getFlags(); - cf.syms.push_back(ld_plugin_symbol()); ld_plugin_symbol &sym = cf.syms.back(); sym.version = nullptr; @@ -512,20 +523,20 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, break; } - if (Symflags & object::BasicSymbolRef::SF_Undefined) { + if (Sym.isUndefined()) { sym.def = LDPK_UNDEF; - if (Symflags & object::BasicSymbolRef::SF_Weak) + if (Sym.isWeak()) sym.def = LDPK_WEAKUNDEF; - } else if (Symflags & object::BasicSymbolRef::SF_Common) + } else if (Sym.isCommon()) sym.def = LDPK_COMMON; - else if (Symflags & object::BasicSymbolRef::SF_Weak) + else if (Sym.isWeak()) sym.def = LDPK_WEAKDEF; else sym.def = LDPK_DEF; sym.size = 0; sym.comdat_key = nullptr; - int CI = check(Sym.getComdatIndex()); + int CI = Sym.getComdatIndex(); if (CI != -1) { StringRef C = Obj->getComdatTable()[CI]; sym.comdat_key = strdup(C.str().c_str()); @@ -567,8 +578,35 @@ static const void *getSymbolsAndView(claimed_file &F) { return View; } -static void addModule(LTO &Lto, claimed_file &F, const void *View) { - MemoryBufferRef BufferRef(StringRef((const char *)View, F.filesize), F.name); +/// Parse the thinlto-object-suffix-replace option into the \p OldSuffix and +/// \p NewSuffix strings, if it was specified. +static void getThinLTOOldAndNewSuffix(std::string &OldSuffix, + std::string &NewSuffix) { + assert(options::thinlto_object_suffix_replace.empty() || + options::thinlto_object_suffix_replace.find(";") != StringRef::npos); + StringRef SuffixReplace = options::thinlto_object_suffix_replace; + std::pair<StringRef, StringRef> Split = SuffixReplace.split(";"); + OldSuffix = Split.first.str(); + NewSuffix = Split.second.str(); +} + +/// Given the original \p Path to an output file, replace any filename +/// suffix matching \p OldSuffix with \p NewSuffix. +static std::string getThinLTOObjectFileName(const std::string Path, + const std::string &OldSuffix, + const std::string &NewSuffix) { + if (OldSuffix.empty() && NewSuffix.empty()) + return Path; + StringRef NewPath = Path; + NewPath.consume_back(OldSuffix); + std::string NewNewPath = NewPath.str() + NewSuffix; + return NewPath.str() + NewSuffix; +} + +static void addModule(LTO &Lto, claimed_file &F, const void *View, + StringRef Filename) { + MemoryBufferRef BufferRef(StringRef((const char *)View, F.filesize), + Filename); Expected<std::unique_ptr<InputFile>> ObjOrErr = InputFile::create(BufferRef); if (!ObjOrErr) @@ -637,7 +675,7 @@ static void recordFile(std::string Filename, bool TempOutFile) { /// indicating whether a temp file should be generated, and an optional task id. /// The new filename generated is returned in \p NewFilename. static void getOutputFileName(SmallString<128> InFilename, bool TempOutFile, - SmallString<128> &NewFilename, int TaskID = -1) { + SmallString<128> &NewFilename, int TaskID) { if (TempOutFile) { std::error_code EC = sys::fs::createTemporaryFile("lto-llvm", "o", NewFilename); @@ -646,7 +684,7 @@ static void getOutputFileName(SmallString<128> InFilename, bool TempOutFile, EC.message().c_str()); } else { NewFilename = InFilename; - if (TaskID >= 0) + if (TaskID > 0) NewFilename += utostr(TaskID); } } @@ -790,19 +828,31 @@ static ld_plugin_status allSymbolsReadHook() { if (options::thinlto_index_only) getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix); + std::string OldSuffix, NewSuffix; + getThinLTOOldAndNewSuffix(OldSuffix, NewSuffix); + // Set for owning string objects used as buffer identifiers. + StringSet<> ObjectFilenames; + for (claimed_file &F : Modules) { if (options::thinlto && !HandleToInputFile.count(F.leader_handle)) HandleToInputFile.insert(std::make_pair( F.leader_handle, llvm::make_unique<PluginInputFile>(F.handle))); const void *View = getSymbolsAndView(F); + // In case we are thin linking with a minimized bitcode file, ensure + // the module paths encoded in the index reflect where the backends + // will locate the full bitcode files for compiling/importing. + std::string Identifier = + getThinLTOObjectFileName(F.name, OldSuffix, NewSuffix); + auto ObjFilename = ObjectFilenames.insert(Identifier); + assert(ObjFilename.second); if (!View) { if (options::thinlto_index_only) // Write empty output files that may be expected by the distributed // build system. - writeEmptyDistributedBuildOutputs(F.name, OldPrefix, NewPrefix); + writeEmptyDistributedBuildOutputs(Identifier, OldPrefix, NewPrefix); continue; } - addModule(*Lto, F, View); + addModule(*Lto, F, View, ObjFilename.first->first()); } SmallString<128> Filename; @@ -813,7 +863,7 @@ static ld_plugin_status allSymbolsReadHook() { Filename = output_name + ".o"; bool SaveTemps = !Filename.empty(); - MaxTasks = Lto->getMaxTasks(); + size_t MaxTasks = Lto->getMaxTasks(); std::vector<uintptr_t> IsTemporary(MaxTasks); std::vector<SmallString<128>> Filenames(MaxTasks); @@ -821,21 +871,26 @@ static ld_plugin_status allSymbolsReadHook() { [&](size_t Task) -> std::unique_ptr<lto::NativeObjectStream> { IsTemporary[Task] = !SaveTemps; getOutputFileName(Filename, /*TempOutFile=*/!SaveTemps, Filenames[Task], - MaxTasks > 1 ? Task : -1); + Task); int FD; std::error_code EC = sys::fs::openFileForWrite(Filenames[Task], FD, sys::fs::F_None); if (EC) - message(LDPL_FATAL, "Could not open file: %s", EC.message().c_str()); + message(LDPL_FATAL, "Could not open file %s: %s", Filenames[Task].c_str(), + EC.message().c_str()); return llvm::make_unique<lto::NativeObjectStream>( llvm::make_unique<llvm::raw_fd_ostream>(FD, true)); }; - auto AddFile = [&](size_t Task, StringRef Path) { Filenames[Task] = Path; }; + auto AddBuffer = [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) { + // Note that this requires that the memory buffers provided to AddBuffer are + // backed by a file. + Filenames[Task] = MB->getBufferIdentifier(); + }; NativeObjectCache Cache; if (!options::cache_dir.empty()) - Cache = localCache(options::cache_dir, AddFile); + Cache = check(localCache(options::cache_dir, AddBuffer)); check(Lto->run(AddStream, Cache)); @@ -844,6 +899,8 @@ static ld_plugin_status allSymbolsReadHook() { return LDPS_OK; if (options::thinlto_index_only) { + if (llvm::AreStatisticsEnabled()) + llvm::PrintStatistics(); cleanup_hook(); exit(0); } diff --git a/tools/llc/CMakeLists.txt b/tools/llc/CMakeLists.txt index 5a98122a233e7..4f8181a1b6577 100644 --- a/tools/llc/CMakeLists.txt +++ b/tools/llc/CMakeLists.txt @@ -12,6 +12,7 @@ set(LLVM_LINK_COMPONENTS Support Target TransformUtils + Vectorize ) # Support plugins. diff --git a/tools/llc/LLVMBuild.txt b/tools/llc/LLVMBuild.txt index c1f5cebea859d..014dcac176106 100644 --- a/tools/llc/LLVMBuild.txt +++ b/tools/llc/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = llc parent = Tools -required_libraries = AsmParser BitReader IRReader MIRParser TransformUtils all-targets +required_libraries = AsmParser BitReader IRReader MIRParser TransformUtils Scalar Vectorize all-targets diff --git a/tools/llc/llc.cpp b/tools/llc/llc.cpp index a76d3249674ff..43f97f112f6bc 100644 --- a/tools/llc/llc.cpp +++ b/tools/llc/llc.cpp @@ -136,6 +136,16 @@ static cl::opt<std::string> StartAfter("start-after", static cl::list<std::string> IncludeDirs("I", cl::desc("include search path")); +static cl::opt<bool> PassRemarksWithHotness( + "pass-remarks-with-hotness", + cl::desc("With PGO, include profile count in optimization remarks"), + cl::Hidden); + +static cl::opt<std::string> + RemarksFilename("pass-remarks-output", + cl::desc("YAML output filename for pass remarks"), + cl::value_desc("filename")); + namespace { static ManagedStatic<std::vector<std::string>> RunPassNames; @@ -233,12 +243,29 @@ static void DiagnosticHandler(const DiagnosticInfo &DI, void *Context) { if (DI.getSeverity() == DS_Error) *HasError = true; + if (auto *Remark = dyn_cast<DiagnosticInfoOptimizationBase>(&DI)) + if (!Remark->isEnabled()) + return; + DiagnosticPrinterRawOStream DP(errs()); errs() << LLVMContext::getDiagnosticMessagePrefix(DI.getSeverity()) << ": "; DI.print(DP); errs() << "\n"; } +static void InlineAsmDiagHandler(const SMDiagnostic &SMD, void *Context, + unsigned LocCookie) { + bool *HasError = static_cast<bool *>(Context); + if (SMD.getKind() == SourceMgr::DK_Error) + *HasError = true; + + SMD.print(nullptr, errs()); + + // For testing purposes, we print the LocCookie here. + if (LocCookie) + errs() << "note: !srcloc = " << LocCookie << "\n"; +} + // main - Entry point for the llc compiler. // int main(int argc, char **argv) { @@ -267,6 +294,8 @@ int main(int argc, char **argv) { initializeCountingFunctionInserterPass(*Registry); initializeUnreachableBlockElimLegacyPassPass(*Registry); initializeConstantHoistingLegacyPassPass(*Registry); + initializeScalarOpts(*Registry); + initializeVectorization(*Registry); // Register the target printer for --version. cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); @@ -278,12 +307,32 @@ int main(int argc, char **argv) { // Set a diagnostic handler that doesn't exit on the first error bool HasError = false; Context.setDiagnosticHandler(DiagnosticHandler, &HasError); + Context.setInlineAsmDiagnosticHandler(InlineAsmDiagHandler, &HasError); + + if (PassRemarksWithHotness) + Context.setDiagnosticHotnessRequested(true); + + std::unique_ptr<tool_output_file> YamlFile; + if (RemarksFilename != "") { + std::error_code EC; + YamlFile = llvm::make_unique<tool_output_file>(RemarksFilename, EC, + sys::fs::F_None); + if (EC) { + errs() << EC.message() << '\n'; + return 1; + } + Context.setDiagnosticsOutputFile( + llvm::make_unique<yaml::Output>(YamlFile->os())); + } // Compile the module TimeCompilations times to give better compile time // metrics. for (unsigned I = TimeCompilations; I; --I) if (int RetVal = compileModule(argv, Context)) return RetVal; + + if (YamlFile) + YamlFile->keep(); return 0; } diff --git a/tools/lli/OrcLazyJIT.h b/tools/lli/OrcLazyJIT.h index 05319c345484f..56e7d36d05fb4 100644 --- a/tools/lli/OrcLazyJIT.h +++ b/tools/lli/OrcLazyJIT.h @@ -21,7 +21,7 @@ #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" #include "llvm/ExecutionEngine/Orc/IRTransformLayer.h" -#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" +#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" #include "llvm/ExecutionEngine/RTDyldMemoryManager.h" namespace llvm { @@ -30,7 +30,7 @@ class OrcLazyJIT { public: typedef orc::JITCompileCallbackManager CompileCallbackMgr; - typedef orc::ObjectLinkingLayer<> ObjLayerT; + typedef orc::RTDyldObjectLinkingLayer<> ObjLayerT; typedef orc::IRCompileLayer<ObjLayerT> CompileLayerT; typedef std::function<std::unique_ptr<Module>(std::unique_ptr<Module>)> TransformFtor; diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp index 0823ff469de6d..f228a36194573 100644 --- a/tools/lli/lli.cpp +++ b/tools/lli/lli.cpp @@ -606,8 +606,7 @@ int main(int argc, char **argv, char * const *envp) { // If the program doesn't explicitly call exit, we will need the Exit // function later on to make an explicit call, so get the function now. Constant *Exit = Mod->getOrInsertFunction("exit", Type::getVoidTy(Context), - Type::getInt32Ty(Context), - nullptr); + Type::getInt32Ty(Context)); // Run static constructors. if (!ForceInterpreter) { diff --git a/tools/llvm-ar/llvm-ar.cpp b/tools/llvm-ar/llvm-ar.cpp index b99a396da62ac..1519464521dd0 100644 --- a/tools/llvm-ar/llvm-ar.cpp +++ b/tools/llvm-ar/llvm-ar.cpp @@ -52,7 +52,7 @@ static StringRef ToolName; // Show the error message and exit. LLVM_ATTRIBUTE_NORETURN static void fail(Twine Error) { - outs() << ToolName << ": " << Error << ".\n"; + errs() << ToolName << ": " << Error << ".\n"; exit(1); } @@ -87,13 +87,14 @@ static cl::opt<bool> MRI("M", cl::desc("")); static cl::opt<std::string> Plugin("plugin", cl::desc("plugin (ignored for compatibility")); namespace { -enum Format { Default, GNU, BSD }; +enum Format { Default, GNU, BSD, DARWIN }; } static cl::opt<Format> FormatOpt("format", cl::desc("Archive format to create"), cl::values(clEnumValN(Default, "default", "default"), clEnumValN(GNU, "gnu", "gnu"), + clEnumValN(DARWIN, "darwin", "darwin"), clEnumValN(BSD, "bsd", "bsd"))); static std::string Options; @@ -167,7 +168,7 @@ LLVM_ATTRIBUTE_NORETURN static void show_help(const std::string &msg) { errs() << ToolName << ": " << msg << "\n\n"; cl::PrintHelpMessage(); - std::exit(1); + exit(1); } // Extract the member filename from the command line for the [relpos] argument @@ -376,7 +377,9 @@ static void doExtract(StringRef Name, const object::Archive::Child &C) { sys::fs::perms Mode = ModeOrErr.get(); int FD; - failIfError(sys::fs::openFileForWrite(Name, FD, sys::fs::F_None, Mode), Name); + failIfError(sys::fs::openFileForWrite(sys::path::filename(Name), FD, + sys::fs::F_None, Mode), + Name); { raw_fd_ostream file(FD, false); @@ -462,7 +465,7 @@ static void performReadOperation(ArchiveOperation Operation, return; for (StringRef Name : Members) errs() << Name << " was not found\n"; - std::exit(1); + exit(1); } static void addMember(std::vector<NewArchiveMember> &Members, @@ -623,8 +626,9 @@ computeNewArchiveMembers(ArchiveOperation Operation, } static object::Archive::Kind getDefaultForHost() { - return Triple(sys::getProcessTriple()).isOSDarwin() ? object::Archive::K_BSD - : object::Archive::K_GNU; + return Triple(sys::getProcessTriple()).isOSDarwin() + ? object::Archive::K_DARWIN + : object::Archive::K_GNU; } static object::Archive::Kind getKindFromMember(const NewArchiveMember &Member) { @@ -633,7 +637,7 @@ static object::Archive::Kind getKindFromMember(const NewArchiveMember &Member) { if (OptionalObject) return isa<object::MachOObjectFile>(**OptionalObject) - ? object::Archive::K_BSD + ? object::Archive::K_DARWIN : object::Archive::K_GNU; // squelch the error in case we had a non-object file @@ -672,6 +676,11 @@ performWriteOperation(ArchiveOperation Operation, fail("Only the gnu format has a thin mode"); Kind = object::Archive::K_BSD; break; + case DARWIN: + if (Thin) + fail("Only the gnu format has a thin mode"); + Kind = object::Archive::K_DARWIN; + break; } std::pair<StringRef, std::error_code> Result = diff --git a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp index b84c4a83dee45..abc6fa27a0e05 100644 --- a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp +++ b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp @@ -171,7 +171,6 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID, STRINGIFY_CODE(MODULE_CODE, GLOBALVAR) STRINGIFY_CODE(MODULE_CODE, FUNCTION) STRINGIFY_CODE(MODULE_CODE, ALIAS) - STRINGIFY_CODE(MODULE_CODE, PURGEVALS) STRINGIFY_CODE(MODULE_CODE, GCNAME) STRINGIFY_CODE(MODULE_CODE, VSTOFFSET) STRINGIFY_CODE(MODULE_CODE, METADATA_VALUES_UNUSED) @@ -312,6 +311,10 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID, STRINGIFY_CODE(FS, COMBINED_ORIGINAL_NAME) STRINGIFY_CODE(FS, VERSION) STRINGIFY_CODE(FS, TYPE_TESTS) + STRINGIFY_CODE(FS, TYPE_TEST_ASSUME_VCALLS) + STRINGIFY_CODE(FS, TYPE_CHECKED_LOAD_VCALLS) + STRINGIFY_CODE(FS, TYPE_TEST_ASSUME_CONST_VCALL) + STRINGIFY_CODE(FS, TYPE_CHECKED_LOAD_CONST_VCALL) } case bitc::METADATA_ATTACHMENT_ID: switch(CodeID) { diff --git a/tools/llvm-c-test/echo.cpp b/tools/llvm-c-test/echo.cpp index 72ff138c74e32..52ce85c577821 100644 --- a/tools/llvm-c-test/echo.cpp +++ b/tools/llvm-c-test/echo.cpp @@ -591,7 +591,7 @@ struct FunCloner { break; } case LLVMPHI: { - // We need to agressively set things here because of loops. + // We need to aggressively set things here because of loops. VMap[Src] = Dst = LLVMBuildPhi(Builder, CloneType(Src), Name); SmallVector<LLVMValueRef, 8> Values; diff --git a/tools/llvm-cat/llvm-cat.cpp b/tools/llvm-cat/llvm-cat.cpp index d884970309b44..4d62099094bb8 100644 --- a/tools/llvm-cat/llvm-cat.cpp +++ b/tools/llvm-cat/llvm-cat.cpp @@ -40,7 +40,7 @@ int main(int argc, char **argv) { SmallVector<char, 0> Buffer; BitcodeWriter Writer(Buffer); if (BinaryCat) { - for (std::string InputFilename : InputFilenames) { + for (const auto &InputFilename : InputFilenames) { std::unique_ptr<MemoryBuffer> MB = ExitOnErr( errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputFilename))); std::vector<BitcodeModule> Mods = ExitOnErr(getBitcodeModuleList(*MB)); @@ -49,7 +49,7 @@ int main(int argc, char **argv) { BitcodeMod.getBuffer().end()); } } else { - for (std::string InputFilename : InputFilenames) { + for (const auto &InputFilename : InputFilenames) { SMDiagnostic Err; std::unique_ptr<Module> M = parseIRFile(InputFilename, Err, Context); if (!M) { diff --git a/tools/llvm-cov/CodeCoverage.cpp b/tools/llvm-cov/CodeCoverage.cpp index 0a9807ab00334..6179c760d5b20 100644 --- a/tools/llvm-cov/CodeCoverage.cpp +++ b/tools/llvm-cov/CodeCoverage.cpp @@ -15,6 +15,7 @@ #include "CoverageFilters.h" #include "CoverageReport.h" +#include "CoverageSummaryInfo.h" #include "CoverageViewOptions.h" #include "RenderingSupport.h" #include "SourceCoverageView.h" @@ -98,9 +99,6 @@ private: /// \brief If a demangler is available, demangle all symbol names. void demangleSymbols(const CoverageMapping &Coverage); - /// \brief Demangle \p Sym if possible. Otherwise, just return \p Sym. - StringRef getSymbolForHumans(StringRef Sym) const; - /// \brief Write out a source file view to the filesystem. void writeSourceFileView(StringRef SourceFile, CoverageMapping *Coverage, CoveragePrinter *Printer, bool ShowFilenames); @@ -136,10 +134,10 @@ private: /// The architecture the coverage mapping data targets. std::string CoverageArch; - /// A cache for demangled symbol names. - StringMap<std::string> DemangledNames; + /// A cache for demangled symbols. + DemangleCache DC; - /// Errors and warnings which have not been printed. + /// A lock which guards printing to stderr. std::mutex ErrsLock; /// A container for input source file buffers. @@ -267,7 +265,7 @@ CodeCoverageTool::createFunctionView(const FunctionRecord &Function, return nullptr; auto Expansions = FunctionCoverage.getExpansions(); - auto View = SourceCoverageView::create(getSymbolForHumans(Function.Name), + auto View = SourceCoverageView::create(DC.demangle(Function.Name), SourceBuffer.get(), ViewOpts, std::move(FunctionCoverage)); attachExpansionSubViews(*View, Expansions, Coverage); @@ -293,7 +291,7 @@ CodeCoverageTool::createSourceFileView(StringRef SourceFile, for (const auto *Function : Coverage.getInstantiations(SourceFile)) { std::unique_ptr<SourceCoverageView> SubView{nullptr}; - StringRef Funcname = getSymbolForHumans(Function->Name); + StringRef Funcname = DC.demangle(Function->Name); if (Function->ExecutionCount > 0) { auto SubViewCoverage = Coverage.getCoverageForFunction(*Function); @@ -453,14 +451,9 @@ void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) { // Cache the demangled names. unsigned I = 0; for (const auto &Function : Coverage.getCoveredFunctions()) - DemangledNames[Function.Name] = Symbols[I++]; -} - -StringRef CodeCoverageTool::getSymbolForHumans(StringRef Sym) const { - const auto DemangledName = DemangledNames.find(Sym); - if (DemangledName == DemangledNames.end()) - return Sym; - return DemangledName->getValue(); + // On Windows, lines in the demangler's output file end with "\r\n". + // Splitting by '\n' keeps '\r's, so cut them now. + DC.DemangledNames[Function.Name] = Symbols[I++].rtrim(); } void CodeCoverageTool::writeSourceFileView(StringRef SourceFile, @@ -817,22 +810,28 @@ int CodeCoverageTool::show(int argc, const char **argv, int CodeCoverageTool::report(int argc, const char **argv, CommandLineParserType commandLineParser) { + cl::opt<bool> ShowFunctionSummaries( + "show-functions", cl::Optional, cl::init(false), + cl::desc("Show coverage summaries for each function")); + auto Err = commandLineParser(argc, argv); if (Err) return Err; - if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) + if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) { error("HTML output for summary reports is not yet supported."); + return 1; + } auto Coverage = load(); if (!Coverage) return 1; CoverageReport Report(ViewOpts, *Coverage.get()); - if (SourceFiles.empty()) + if (!ShowFunctionSummaries) Report.renderFileReports(llvm::outs()); else - Report.renderFunctionReports(SourceFiles, llvm::outs()); + Report.renderFunctionReports(SourceFiles, DC, llvm::outs()); return 0; } @@ -843,6 +842,11 @@ int CodeCoverageTool::export_(int argc, const char **argv, if (Err) return Err; + if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text) { + error("Coverage data can only be exported as textual JSON."); + return 1; + } + auto Coverage = load(); if (!Coverage) { error("Could not load coverage information"); diff --git a/tools/llvm-cov/CoverageReport.cpp b/tools/llvm-cov/CoverageReport.cpp index e88cb186acd66..c68bb9048df1b 100644 --- a/tools/llvm-cov/CoverageReport.cpp +++ b/tools/llvm-cov/CoverageReport.cpp @@ -118,19 +118,51 @@ raw_ostream::Colors determineCoveragePercentageColor(const T &Info) { : raw_ostream::RED; } -/// \brief Determine the length of the longest common prefix of the strings in -/// \p Strings. -unsigned getLongestCommonPrefixLen(ArrayRef<std::string> Strings) { - unsigned LCP = Strings[0].size(); - for (unsigned I = 1, E = Strings.size(); LCP > 0 && I < E; ++I) { - unsigned Cursor; - StringRef S = Strings[I]; - for (Cursor = 0; Cursor < LCP && Cursor < S.size(); ++Cursor) - if (Strings[0][Cursor] != S[Cursor]) +/// \brief Get the number of redundant path components in each path in \p Paths. +unsigned getNumRedundantPathComponents(ArrayRef<std::string> Paths) { + // To start, set the number of redundant path components to the maximum + // possible value. + SmallVector<StringRef, 8> FirstPathComponents{sys::path::begin(Paths[0]), + sys::path::end(Paths[0])}; + unsigned NumRedundant = FirstPathComponents.size(); + + for (unsigned I = 1, E = Paths.size(); NumRedundant > 0 && I < E; ++I) { + StringRef Path = Paths[I]; + for (const auto &Component : + enumerate(make_range(sys::path::begin(Path), sys::path::end(Path)))) { + // Do not increase the number of redundant components: that would remove + // useful parts of already-visited paths. + if (Component.index() >= NumRedundant) break; - LCP = std::min(LCP, Cursor); + + // Lower the number of redundant components when there's a mismatch + // between the first path, and the path under consideration. + if (FirstPathComponents[Component.index()] != Component.value()) { + NumRedundant = Component.index(); + break; + } + } + } + + return NumRedundant; +} + +/// \brief Determine the length of the longest redundant prefix of the paths in +/// \p Paths. +unsigned getRedundantPrefixLen(ArrayRef<std::string> Paths) { + // If there's at most one path, no path components are redundant. + if (Paths.size() <= 1) + return 0; + + unsigned PrefixLen = 0; + unsigned NumRedundant = getNumRedundantPathComponents(Paths); + auto Component = sys::path::begin(Paths[0]); + for (unsigned I = 0; I < NumRedundant; ++I) { + auto LastComponent = Component; + ++Component; + PrefixLen += Component - LastComponent; } - return LCP; + return PrefixLen; } } // end anonymous namespace @@ -200,12 +232,14 @@ void CoverageReport::render(const FileCoverageSummary &File, } void CoverageReport::render(const FunctionCoverageSummary &Function, + const DemangleCache &DC, raw_ostream &OS) const { auto FuncCoverageColor = determineCoveragePercentageColor(Function.RegionCoverage); auto LineCoverageColor = determineCoveragePercentageColor(Function.LineCoverage); - OS << column(Function.Name, FunctionReportColumns[0], Column::RightTrim) + OS << column(DC.demangle(Function.Name), FunctionReportColumns[0], + Column::RightTrim) << format("%*u", FunctionReportColumns[1], (unsigned)Function.RegionCoverage.NumRegions); Options.colored_ostream(OS, FuncCoverageColor) @@ -230,6 +264,7 @@ void CoverageReport::render(const FunctionCoverageSummary &Function, } void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files, + const DemangleCache &DC, raw_ostream &OS) { bool isFirst = true; for (StringRef Filename : Files) { @@ -242,7 +277,7 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files, std::vector<StringRef> Funcnames; for (const auto &F : Functions) - Funcnames.emplace_back(F.Name); + Funcnames.emplace_back(DC.demangle(F.Name)); adjustColumnWidths({}, Funcnames); OS << "File '" << Filename << "':\n"; @@ -262,12 +297,12 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files, ++Totals.ExecutionCount; Totals.RegionCoverage += Function.RegionCoverage; Totals.LineCoverage += Function.LineCoverage; - render(Function, OS); + render(Function, DC, OS); } if (Totals.ExecutionCount) { renderDivider(FunctionReportColumns, OS); OS << "\n"; - render(Totals, OS); + render(Totals, DC, OS); } } } @@ -277,9 +312,7 @@ CoverageReport::prepareFileReports(const coverage::CoverageMapping &Coverage, FileCoverageSummary &Totals, ArrayRef<std::string> Files) { std::vector<FileCoverageSummary> FileReports; - unsigned LCP = 0; - if (Files.size() > 1) - LCP = getLongestCommonPrefixLen(Files); + unsigned LCP = getRedundantPrefixLen(Files); for (StringRef Filename : Files) { FileCoverageSummary Summary(Filename.drop_front(LCP)); diff --git a/tools/llvm-cov/CoverageReport.h b/tools/llvm-cov/CoverageReport.h index 7a416497e258e..071be2e21594c 100644 --- a/tools/llvm-cov/CoverageReport.h +++ b/tools/llvm-cov/CoverageReport.h @@ -25,14 +25,16 @@ class CoverageReport { const coverage::CoverageMapping &Coverage; void render(const FileCoverageSummary &File, raw_ostream &OS) const; - void render(const FunctionCoverageSummary &Function, raw_ostream &OS) const; + void render(const FunctionCoverageSummary &Function, const DemangleCache &DC, + raw_ostream &OS) const; public: CoverageReport(const CoverageViewOptions &Options, const coverage::CoverageMapping &Coverage) : Options(Options), Coverage(Coverage) {} - void renderFunctionReports(ArrayRef<std::string> Files, raw_ostream &OS); + void renderFunctionReports(ArrayRef<std::string> Files, + const DemangleCache &DC, raw_ostream &OS); /// Prepare file reports for the files specified in \p Files. static std::vector<FileCoverageSummary> diff --git a/tools/llvm-cov/CoverageSummaryInfo.h b/tools/llvm-cov/CoverageSummaryInfo.h index c04a4d42ccd74..680fc3757686f 100644 --- a/tools/llvm-cov/CoverageSummaryInfo.h +++ b/tools/llvm-cov/CoverageSummaryInfo.h @@ -160,6 +160,19 @@ struct FileCoverageSummary { } }; +/// \brief A cache for demangled symbols. +struct DemangleCache { + StringMap<std::string> DemangledNames; + + /// \brief Demangle \p Sym if possible. Otherwise, just return \p Sym. + StringRef demangle(StringRef Sym) const { + const auto DemangledName = DemangledNames.find(Sym); + if (DemangledName == DemangledNames.end()) + return Sym; + return DemangledName->getValue(); + } +}; + } // namespace llvm #endif // LLVM_COV_COVERAGESUMMARYINFO_H diff --git a/tools/llvm-cov/TestingSupport.cpp b/tools/llvm-cov/TestingSupport.cpp index 72768f4fd583f..4713d75f17dd4 100644 --- a/tools/llvm-cov/TestingSupport.cpp +++ b/tools/llvm-cov/TestingSupport.cpp @@ -48,13 +48,16 @@ int convertForTestingMain(int argc, const char *argv[]) { // Look for the sections that we are interested in. int FoundSectionCount = 0; SectionRef ProfileNames, CoverageMapping; + auto ObjFormat = OF->getTripleObjectFormat(); for (const auto &Section : OF->sections()) { StringRef Name; if (Section.getName(Name)) return 1; - if (Name == llvm::getInstrProfNameSectionName(false)) { + if (Name == llvm::getInstrProfSectionName(IPSK_name, ObjFormat, + /*AddSegmentInfo=*/false)) { ProfileNames = Section; - } else if (Name == llvm::getInstrProfCoverageSectionName(false)) { + } else if (Name == llvm::getInstrProfSectionName( + IPSK_covmap, ObjFormat, /*AddSegmentInfo=*/false)) { CoverageMapping = Section; } else continue; diff --git a/tools/llvm-cov/gcov.cpp b/tools/llvm-cov/gcov.cpp index 4652fed2a384e..4df7f015fd188 100644 --- a/tools/llvm-cov/gcov.cpp +++ b/tools/llvm-cov/gcov.cpp @@ -74,7 +74,7 @@ static void reportCoverage(StringRef SourceFile, StringRef ObjectDir, } if (DumpGCOV) - GF.dump(); + GF.print(errs()); FileInfo FI(Options); GF.collectLineCounts(FI); diff --git a/tools/llvm-cxxfilt/llvm-cxxfilt.cpp b/tools/llvm-cxxfilt/llvm-cxxfilt.cpp index 1e2797ba33343..13024fbeaeaa0 100644 --- a/tools/llvm-cxxfilt/llvm-cxxfilt.cpp +++ b/tools/llvm-cxxfilt/llvm-cxxfilt.cpp @@ -8,29 +8,89 @@ //===----------------------------------------------------------------------===// #include "llvm/Demangle/Demangle.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" #include <cstdlib> #include <iostream> using namespace llvm; +enum Style { + Auto, ///< auto-detect mangling + GNU, ///< GNU + Lucid, ///< Lucid compiler (lcc) + ARM, + HP, ///< HP compiler (xCC) + EDG, ///< EDG compiler + GNUv3, ///< GNU C++ v3 ABI + Java, ///< Java (gcj) + GNAT ///< ADA copiler (gnat) +}; +static cl::opt<Style> + Format("format", cl::desc("decoration style"), + cl::values(clEnumValN(Auto, "auto", "auto-detect style"), + clEnumValN(GNU, "gnu", "GNU (itanium) style")), + cl::init(Auto)); +static cl::alias FormatShort("s", cl::desc("alias for --format"), + cl::aliasopt(Format)); + +static cl::opt<bool> StripUnderscore("strip-underscore", + cl::desc("strip the leading underscore"), + cl::init(false)); +static cl::alias StripUnderscoreShort("_", + cl::desc("alias for --strip-underscore"), + cl::aliasopt(StripUnderscore)); + +static cl::opt<bool> + Types("types", + cl::desc("attempt to demangle types as well as function names"), + cl::init(false)); +static cl::alias TypesShort("t", cl::desc("alias for --types"), + cl::aliasopt(Types)); + +static cl::list<std::string> +Decorated(cl::Positional, cl::desc("<mangled>"), cl::ZeroOrMore); + static void demangle(llvm::raw_ostream &OS, const std::string &Mangled) { int Status; - char *Demangled = nullptr; - if ((Mangled.size() >= 2 && Mangled.compare(0, 2, "_Z")) || - (Mangled.size() >= 4 && Mangled.compare(0, 4, "___Z"))) - Demangled = itaniumDemangle(Mangled.c_str(), nullptr, nullptr, &Status); - OS << (Demangled ? Demangled : Mangled) << '\n'; - free(Demangled); + + const char *Decorated = Mangled.c_str(); + if (StripUnderscore) + if (Decorated[0] == '_') + ++Decorated; + size_t DecoratedLength = strlen(Decorated); + + char *Undecorated = nullptr; + + if (Types || ((DecoratedLength >= 2 && strncmp(Decorated, "_Z", 2) == 0) || + (DecoratedLength >= 4 && strncmp(Decorated, "___Z", 4) == 0))) + Undecorated = itaniumDemangle(Decorated, nullptr, nullptr, &Status); + + if (!Undecorated && + (DecoratedLength > 6 && strncmp(Decorated, "__imp_", 6) == 0)) { + OS << "import thunk for "; + Undecorated = itaniumDemangle(Decorated + 6, nullptr, nullptr, &Status); + } + + OS << (Undecorated ? Undecorated : Mangled) << '\n'; + + free(Undecorated); } int main(int argc, char **argv) { - if (argc == 1) + sys::PrintStackTraceOnErrorSignal(argv[0]); + PrettyStackTraceProgram X(argc, argv); + + cl::ParseCommandLineOptions(argc, argv, "llvm symbol undecoration tool\n"); + + if (Decorated.empty()) for (std::string Mangled; std::getline(std::cin, Mangled);) demangle(llvm::outs(), Mangled); else - for (int I = 1; I < argc; ++I) - demangle(llvm::outs(), argv[I]); + for (const auto &Symbol : Decorated) + demangle(llvm::outs(), Symbol); return EXIT_SUCCESS; } diff --git a/tools/llvm-diff/DiffConsumer.cpp b/tools/llvm-diff/DiffConsumer.cpp index 9078013c1c163..e16775010fef4 100644 --- a/tools/llvm-diff/DiffConsumer.cpp +++ b/tools/llvm-diff/DiffConsumer.cpp @@ -15,6 +15,7 @@ #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Debug.h" using namespace llvm; @@ -195,17 +196,17 @@ void DiffConsumer::logd(const DiffLogBuilder &Log) { switch (Log.getLineKind(I)) { case DC_match: out << " "; - Log.getLeft(I)->dump(); + Log.getLeft(I)->print(dbgs()); dbgs() << '\n'; //printValue(Log.getLeft(I), true); break; case DC_left: out << "< "; - Log.getLeft(I)->dump(); + Log.getLeft(I)->print(dbgs()); dbgs() << '\n'; //printValue(Log.getLeft(I), true); break; case DC_right: out << "> "; - Log.getRight(I)->dump(); + Log.getRight(I)->print(dbgs()); dbgs() << '\n'; //printValue(Log.getRight(I), false); break; } diff --git a/tools/llvm-diff/DifferenceEngine.cpp b/tools/llvm-diff/DifferenceEngine.cpp index df208a26ab7df..95a63d7f9c835 100644 --- a/tools/llvm-diff/DifferenceEngine.cpp +++ b/tools/llvm-diff/DifferenceEngine.cpp @@ -315,17 +315,15 @@ class FunctionDifferenceEngine { bool Difference = false; DenseMap<ConstantInt*,BasicBlock*> LCases; - - for (SwitchInst::CaseIt I = LI->case_begin(), E = LI->case_end(); - I != E; ++I) - LCases[I.getCaseValue()] = I.getCaseSuccessor(); - - for (SwitchInst::CaseIt I = RI->case_begin(), E = RI->case_end(); - I != E; ++I) { - ConstantInt *CaseValue = I.getCaseValue(); + for (auto Case : LI->cases()) + LCases[Case.getCaseValue()] = Case.getCaseSuccessor(); + + for (auto Case : RI->cases()) { + ConstantInt *CaseValue = Case.getCaseValue(); BasicBlock *LCase = LCases[CaseValue]; if (LCase) { - if (TryUnify) tryUnify(LCase, I.getCaseSuccessor()); + if (TryUnify) + tryUnify(LCase, Case.getCaseSuccessor()); LCases.erase(CaseValue); } else if (Complain || !Difference) { if (Complain) diff --git a/tools/llvm-dwp/llvm-dwp.cpp b/tools/llvm-dwp/llvm-dwp.cpp index 7418c77bdeca2..0c4af7576608c 100644 --- a/tools/llvm-dwp/llvm-dwp.cpp +++ b/tools/llvm-dwp/llvm-dwp.cpp @@ -27,6 +27,7 @@ #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCTargetOptionsCommandFlags.h" +#include "llvm/Object/Decompressor.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Compression.h" #include "llvm/Support/DataExtractor.h" @@ -334,21 +335,6 @@ writeIndex(MCStreamer &Out, MCSection *Section, writeIndexTable(Out, ContributionOffsets, IndexEntries, &DWARFUnitIndex::Entry::SectionContribution::Length); } -static bool consumeCompressedDebugSectionHeader(StringRef &data, - uint64_t &OriginalSize) { - // Consume "ZLIB" prefix. - if (!data.startswith("ZLIB")) - return false; - data = data.substr(4); - // Consume uncompressed section size (big-endian 8 bytes). - DataExtractor extractor(data, false, 8); - uint32_t Offset = 0; - OriginalSize = extractor.getU64(&Offset); - if (Offset == 0) - return false; - data = data.substr(Offset); - return true; -} std::string buildDWODescription(StringRef Name, StringRef DWPName, StringRef DWOName) { std::string Text = "\'"; @@ -368,22 +354,29 @@ std::string buildDWODescription(StringRef Name, StringRef DWPName, StringRef DWO return Text; } -static Error handleCompressedSection( - std::deque<SmallString<32>> &UncompressedSections, StringRef &Name, - StringRef &Contents) { - if (!Name.startswith("zdebug_")) +static Error createError(StringRef Name, Error E) { + return make_error<DWPError>( + ("failure while decompressing compressed section: '" + Name + "', " + + llvm::toString(std::move(E))) + .str()); +} + +static Error +handleCompressedSection(std::deque<SmallString<32>> &UncompressedSections, + StringRef &Name, StringRef &Contents) { + if (!Decompressor::isGnuStyle(Name)) return Error::success(); + + Expected<Decompressor> Dec = + Decompressor::create(Name, Contents, false /*IsLE*/, false /*Is64Bit*/); + if (!Dec) + return createError(Name, Dec.takeError()); + UncompressedSections.emplace_back(); - uint64_t OriginalSize; - if (!zlib::isAvailable()) - return make_error<DWPError>("zlib not available"); - if (!consumeCompressedDebugSectionHeader(Contents, OriginalSize) || - zlib::uncompress(Contents, UncompressedSections.back(), OriginalSize) != - zlib::StatusOK) - return make_error<DWPError>( - ("failure while decompressing compressed section: '" + Name + "\'") - .str()); - Name = Name.substr(1); + if (Error E = Dec->decompress(UncompressedSections.back())) + return createError(Name, std::move(E)); + + Name = Name.substr(2); // Drop ".z" Contents = UncompressedSections.back(); return Error::success(); } @@ -409,8 +402,6 @@ static Error handleSection( if (std::error_code Err = Section.getName(Name)) return errorCodeToError(Err); - Name = Name.substr(Name.find_first_not_of("._")); - StringRef Contents; if (auto Err = Section.getContents(Contents)) return errorCodeToError(Err); @@ -418,6 +409,8 @@ static Error handleSection( if (auto Err = handleCompressedSection(UncompressedSections, Name, Contents)) return Err; + Name = Name.substr(Name.find_first_not_of("._")); + auto SectionPair = KnownSections.find(Name); if (SectionPair == KnownSections.end()) return Error::success(); diff --git a/tools/llvm-extract/llvm-extract.cpp b/tools/llvm-extract/llvm-extract.cpp index aa1eda2f094a6..d868db7f78ad1 100644 --- a/tools/llvm-extract/llvm-extract.cpp +++ b/tools/llvm-extract/llvm-extract.cpp @@ -17,10 +17,11 @@ #include "llvm/Bitcode/BitcodeWriterPass.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/IRPrintingPasses.h" +#include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/IRReader/IRReader.h" -#include "llvm/IR/LegacyPassManager.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" @@ -50,6 +51,10 @@ Force("f", cl::desc("Enable binary output on terminals")); static cl::opt<bool> DeleteFn("delete", cl::desc("Delete specified Globals from Module")); +static cl::opt<bool> + Recursive("recursive", + cl::desc("Recursively extract all called functions")); + // ExtractFuncs - The functions to extract from the module. static cl::list<std::string> ExtractFuncs("func", cl::desc("Specify function to extract"), @@ -226,6 +231,34 @@ int main(int argc, char **argv) { // Use *argv instead of argv[0] to work around a wrong GCC warning. ExitOnError ExitOnErr(std::string(*argv) + ": error reading input: "); + if (Recursive) { + std::vector<llvm::Function *> Workqueue; + for (GlobalValue *GV : GVs) { + if (auto *F = dyn_cast<Function>(GV)) { + Workqueue.push_back(F); + } + } + while (!Workqueue.empty()) { + Function *F = &*Workqueue.back(); + Workqueue.pop_back(); + ExitOnErr(F->materialize()); + for (auto &BB : *F) { + for (auto &I : BB) { + auto *CI = dyn_cast<CallInst>(&I); + if (!CI) + continue; + Function *CF = CI->getCalledFunction(); + if (!CF) + continue; + if (CF->isDeclaration() || GVs.count(CF)) + continue; + GVs.insert(CF); + Workqueue.push_back(CF); + } + } + } + } + auto Materialize = [&](GlobalValue &GV) { ExitOnErr(GV.materialize()); }; // Materialize requisite global values. diff --git a/tools/llvm-link/llvm-link.cpp b/tools/llvm-link/llvm-link.cpp index e89696e7e7c24..a024b6926d5dd 100644 --- a/tools/llvm-link/llvm-link.cpp +++ b/tools/llvm-link/llvm-link.cpp @@ -34,6 +34,7 @@ #include "llvm/Support/SystemUtils.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Transforms/IPO/FunctionImport.h" +#include "llvm/Transforms/IPO/Internalize.h" #include "llvm/Transforms/Utils/FunctionImportUtils.h" #include <memory> @@ -272,6 +273,8 @@ static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L, unsigned Flags) { // Filter out flags that don't apply to the first file we load. unsigned ApplicableFlags = Flags & Linker::Flags::OverrideFromSrc; + // Similar to some flags, internalization doesn't apply to the first file. + bool InternalizeLinkedSymbols = false; for (const auto &File : Files) { std::unique_ptr<Module> M = loadFile(argv0, File, Context); if (!M.get()) { @@ -311,8 +314,24 @@ static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L, if (Verbose) errs() << "Linking in '" << File << "'\n"; - if (L.linkInModule(std::move(M), ApplicableFlags)) + bool Err = false; + if (InternalizeLinkedSymbols) { + Err = L.linkInModule( + std::move(M), ApplicableFlags, [](Module &M, const StringSet<> &GVS) { + internalizeModule(M, [&GVS](const GlobalValue &GV) { + return !GV.hasName() || (GVS.count(GV.getName()) == 0); + }); + }); + } else { + Err = L.linkInModule(std::move(M), ApplicableFlags); + } + + if (Err) return false; + + // Internalization applies to linking of subsequent files. + InternalizeLinkedSymbols = Internalize; + // All linker flags apply to linking of subsequent files. ApplicableFlags = Flags; } @@ -340,8 +359,6 @@ int main(int argc, char **argv) { Linker L(*Composite); unsigned Flags = Linker::Flags::None; - if (Internalize) - Flags |= Linker::Flags::InternalizeLinkedSymbols; if (OnlyNeeded) Flags |= Linker::Flags::LinkOnlyNeeded; diff --git a/tools/llvm-lto/llvm-lto.cpp b/tools/llvm-lto/llvm-lto.cpp index 475350c8ecf12..2f005412a3b92 100644 --- a/tools/llvm-lto/llvm-lto.cpp +++ b/tools/llvm-lto/llvm-lto.cpp @@ -63,6 +63,10 @@ static cl::opt<bool> DisableLTOVectorization( "disable-lto-vectorization", cl::init(false), cl::desc("Do not run loop or slp vectorization during LTO")); +static cl::opt<bool> EnableFreestanding( + "lto-freestanding", cl::init(false), + cl::desc("Enable Freestanding (disable builtins / TLI) during LTO")); + static cl::opt<bool> UseDiagnosticHandler( "use-diagnostic-handler", cl::init(false), cl::desc("Use a diagnostic handler to test the handler interface")); @@ -433,6 +437,7 @@ public: ThinGenerator.setCodePICModel(getRelocModel()); ThinGenerator.setTargetOptions(Options); ThinGenerator.setCacheDir(ThinLTOCacheDir); + ThinGenerator.setFreestanding(EnableFreestanding); // Add all the exported symbols to the table of symbols to preserve. for (unsigned i = 0; i < ExportedSymbols.size(); ++i) @@ -809,6 +814,7 @@ int main(int argc, char **argv) { CodeGen.setDiagnosticHandler(handleDiagnostics, nullptr); CodeGen.setCodePICModel(getRelocModel()); + CodeGen.setFreestanding(EnableFreestanding); CodeGen.setDebugInfo(LTO_DEBUG_MODEL_DWARF); CodeGen.setTargetOptions(Options); diff --git a/tools/llvm-lto2/llvm-lto2.cpp b/tools/llvm-lto2/llvm-lto2.cpp index c09311a05b90d..3d2643db85bd8 100644 --- a/tools/llvm-lto2/llvm-lto2.cpp +++ b/tools/llvm-lto2/llvm-lto2.cpp @@ -21,12 +21,12 @@ #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/LTO/LTO.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/Threading.h" using namespace llvm; using namespace lto; -using namespace object; static cl::opt<char> OptLevel("O", cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] " @@ -90,11 +90,20 @@ static cl::opt<std::string> DefaultTriple( cl::desc( "Replace unspecified target triples in input files with this triple")); +static cl::opt<std::string> + OptRemarksOutput("pass-remarks-output", + cl::desc("YAML output file for optimization remarks")); + +static cl::opt<bool> OptRemarksWithHotness( + "pass-remarks-with-hotness", + cl::desc("Whether to include hotness informations in the remarks.\n" + "Has effect only if -pass-remarks-output is specified.")); + static void check(Error E, std::string Msg) { if (!E) return; handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) { - errs() << "llvm-lto: " << Msg << ": " << EIB.message().c_str() << '\n'; + errs() << "llvm-lto2: " << Msg << ": " << EIB.message().c_str() << '\n'; }); exit(1); } @@ -117,12 +126,12 @@ template <typename T> static T check(ErrorOr<T> E, std::string Msg) { return T(); } -int main(int argc, char **argv) { - InitializeAllTargets(); - InitializeAllTargetMCs(); - InitializeAllAsmPrinters(); - InitializeAllAsmParsers(); +static int usage() { + errs() << "Available subcommands: dump-symtab run\n"; + return 1; +} +static int run(int argc, char **argv) { cl::ParseCommandLineOptions(argc, argv, "Resolution-based LTO test harness"); // FIXME: Workaround PR30396 which means that a symbol can appear @@ -148,9 +157,11 @@ int main(int argc, char **argv) { Res.FinalDefinitionInLinkageUnit = true; else if (C == 'x') Res.VisibleToRegularObj = true; - else + else { llvm::errs() << "invalid character " << C << " in resolution: " << R << '\n'; + return 1; + } } CommandLineResolutions[{FileName, SymbolName}].push_back(Res); } @@ -176,6 +187,10 @@ int main(int argc, char **argv) { check(Conf.addSaveTemps(OutputFilename + "."), "Config::addSaveTemps failed"); + // Optimization remarks. + Conf.RemarksFilename = OptRemarksOutput; + Conf.RemarksWithHotness = OptRemarksWithHotness; + // Run a custom pipeline, if asked for. Conf.OptPipeline = OptPipeline; Conf.AAPipeline = AAPipeline; @@ -199,6 +214,9 @@ int main(int argc, char **argv) { return 1; } + if (FileType.getNumOccurrences()) + Conf.CGFileType = FileType; + Conf.OverrideTriple = OverrideTriple; Conf.DefaultTriple = DefaultTriple; @@ -257,18 +275,92 @@ int main(int argc, char **argv) { return llvm::make_unique<lto::NativeObjectStream>(std::move(S)); }; - auto AddFile = [&](size_t Task, StringRef Path) { - auto ReloadedBufferOrErr = MemoryBuffer::getFile(Path); - if (auto EC = ReloadedBufferOrErr.getError()) - report_fatal_error(Twine("Can't reload cached file '") + Path + "': " + - EC.message() + "\n"); - - *AddStream(Task)->OS << (*ReloadedBufferOrErr)->getBuffer(); + auto AddBuffer = [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) { + *AddStream(Task)->OS << MB->getBuffer(); }; NativeObjectCache Cache; if (!CacheDir.empty()) - Cache = localCache(CacheDir, AddFile); + Cache = check(localCache(CacheDir, AddBuffer), "failed to create cache"); check(Lto.run(AddStream, Cache), "LTO::run failed"); + return 0; +} + +static int dumpSymtab(int argc, char **argv) { + for (StringRef F : make_range(argv + 1, argv + argc)) { + std::unique_ptr<MemoryBuffer> MB = check(MemoryBuffer::getFile(F), F); + std::unique_ptr<InputFile> Input = + check(InputFile::create(MB->getMemBufferRef()), F); + + outs() << "target triple: " << Input->getTargetTriple() << '\n'; + Triple TT(Input->getTargetTriple()); + + outs() << "source filename: " << Input->getSourceFileName() << '\n'; + + if (TT.isOSBinFormatCOFF()) + outs() << "linker opts: " << Input->getCOFFLinkerOpts() << '\n'; + + std::vector<StringRef> ComdatTable = Input->getComdatTable(); + for (const InputFile::Symbol &Sym : Input->symbols()) { + switch (Sym.getVisibility()) { + case GlobalValue::HiddenVisibility: + outs() << 'H'; + break; + case GlobalValue::ProtectedVisibility: + outs() << 'P'; + break; + case GlobalValue::DefaultVisibility: + outs() << 'D'; + break; + } + + auto PrintBool = [&](char C, bool B) { outs() << (B ? C : '-'); }; + PrintBool('U', Sym.isUndefined()); + PrintBool('C', Sym.isCommon()); + PrintBool('W', Sym.isWeak()); + PrintBool('I', Sym.isIndirect()); + PrintBool('O', Sym.canBeOmittedFromSymbolTable()); + PrintBool('T', Sym.isTLS()); + PrintBool('X', Sym.isExecutable()); + outs() << ' ' << Sym.getName() << '\n'; + + if (Sym.isCommon()) + outs() << " size " << Sym.getCommonSize() << " align " + << Sym.getCommonAlignment() << '\n'; + + int Comdat = Sym.getComdatIndex(); + if (Comdat != -1) + outs() << " comdat " << ComdatTable[Comdat] << '\n'; + + if (TT.isOSBinFormatCOFF() && Sym.isWeak() && Sym.isIndirect()) + outs() << " fallback " << Sym.getCOFFWeakExternalFallback() << '\n'; + } + + outs() << '\n'; + } + + return 0; +} + +int main(int argc, char **argv) { + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllAsmPrinters(); + InitializeAllAsmParsers(); + + // FIXME: This should use llvm::cl subcommands, but it isn't currently + // possible to pass an argument not associated with a subcommand to a + // subcommand (e.g. -lto-use-new-pm). + if (argc < 2) + return usage(); + + StringRef Subcommand = argv[1]; + // Ensure that argv[0] is correct after adjusting argv/argc. + argv[1] = argv[0]; + if (Subcommand == "dump-symtab") + return dumpSymtab(argc - 1, argv + 1); + if (Subcommand == "run") + return run(argc - 1, argv + 1); + return usage(); } diff --git a/tools/llvm-mc-assemble-fuzzer/CMakeLists.txt b/tools/llvm-mc-assemble-fuzzer/CMakeLists.txt new file mode 100644 index 0000000000000..c5fb62166cfd4 --- /dev/null +++ b/tools/llvm-mc-assemble-fuzzer/CMakeLists.txt @@ -0,0 +1,19 @@ +if( LLVM_USE_SANITIZE_COVERAGE ) + include_directories(BEFORE + ${CMAKE_CURRENT_SOURCE_DIR}/../../lib/Fuzzer) + + set(LLVM_LINK_COMPONENTS + AllTargetsAsmPrinters + AllTargetsAsmParsers + AllTargetsDescs + AllTargetsInfos + MC + MCParser + Support + ) + add_llvm_tool(llvm-mc-assemble-fuzzer + llvm-mc-assemble-fuzzer.cpp) + target_link_libraries(llvm-mc-assemble-fuzzer + LLVMFuzzer + ) +endif() diff --git a/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp b/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp new file mode 100644 index 0000000000000..0344d8cd8c9a2 --- /dev/null +++ b/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp @@ -0,0 +1,313 @@ +//===--- llvm-mc-fuzzer.cpp - Fuzzer for the MC layer ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#include "FuzzerInterface.h" +#include "llvm-c/Target.h" +#include "llvm/MC/SubtargetFeature.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCParser/AsmLexer.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/ToolOutputFile.h" + +using namespace llvm; + +static cl::opt<std::string> + TripleName("triple", cl::desc("Target triple to assemble for, " + "see -version for available targets")); + +static cl::opt<std::string> + MCPU("mcpu", + cl::desc("Target a specific cpu type (-mcpu=help for details)"), + cl::value_desc("cpu-name"), cl::init("")); + +// This is useful for variable-length instruction sets. +static cl::opt<unsigned> InsnLimit( + "insn-limit", + cl::desc("Limit the number of instructions to process (0 for no limit)"), + cl::value_desc("count"), cl::init(0)); + +static cl::list<std::string> + MAttrs("mattr", cl::CommaSeparated, + cl::desc("Target specific attributes (-mattr=help for details)"), + cl::value_desc("a1,+a2,-a3,...")); +// The feature string derived from -mattr's values. +std::string FeaturesStr; + +static cl::list<std::string> + FuzzerArgs("fuzzer-args", cl::Positional, + cl::desc("Options to pass to the fuzzer"), cl::ZeroOrMore, + cl::PositionalEatsArgs); +static std::vector<char *> ModifiedArgv; + +enum OutputFileType { + OFT_Null, + OFT_AssemblyFile, + OFT_ObjectFile +}; +static cl::opt<OutputFileType> +FileType("filetype", cl::init(OFT_AssemblyFile), + cl::desc("Choose an output file type:"), + cl::values( + clEnumValN(OFT_AssemblyFile, "asm", + "Emit an assembly ('.s') file"), + clEnumValN(OFT_Null, "null", + "Don't emit anything (for timing purposes)"), + clEnumValN(OFT_ObjectFile, "obj", + "Emit a native object ('.o') file"))); + + +class LLVMFuzzerInputBuffer : public MemoryBuffer +{ + public: + LLVMFuzzerInputBuffer(const uint8_t *data_, size_t size_) + : Data(reinterpret_cast<const char *>(data_)), + Size(size_) { + init(Data, Data+Size, false); + } + + + virtual BufferKind getBufferKind() const { + return MemoryBuffer_Malloc; // it's not disk-backed so I think that's + // the intent ... though AFAIK it + // probably came from an mmap or sbrk + } + + private: + const char *Data; + size_t Size; +}; + +static int AssembleInput(const char *ProgName, const Target *TheTarget, + SourceMgr &SrcMgr, MCContext &Ctx, MCStreamer &Str, + MCAsmInfo &MAI, MCSubtargetInfo &STI, + MCInstrInfo &MCII, MCTargetOptions &MCOptions) { + static const bool NoInitialTextSection = false; + + std::unique_ptr<MCAsmParser> Parser( + createMCAsmParser(SrcMgr, Ctx, Str, MAI)); + + std::unique_ptr<MCTargetAsmParser> TAP( + TheTarget->createMCAsmParser(STI, *Parser, MCII, MCOptions)); + + if (!TAP) { + errs() << ProgName + << ": error: this target '" << TripleName + << "', does not support assembly parsing.\n"; + abort(); + } + + Parser->setTargetParser(*TAP); + + return Parser->Run(NoInitialTextSection); +} + + +int AssembleOneInput(const uint8_t *Data, size_t Size) { + const bool ShowInst = false; + const bool AsmVerbose = false; + const bool UseDwarfDirectory = true; + + Triple TheTriple(Triple::normalize(TripleName)); + + SourceMgr SrcMgr; + + std::unique_ptr<MemoryBuffer> BufferPtr(new LLVMFuzzerInputBuffer(Data, Size)); + + // Tell SrcMgr about this buffer, which is what the parser will pick up. + SrcMgr.AddNewSourceBuffer(std::move(BufferPtr), SMLoc()); + + static const std::vector<std::string> NoIncludeDirs; + SrcMgr.setIncludeDirs(NoIncludeDirs); + + static std::string ArchName; + std::string Error; + const Target *TheTarget = TargetRegistry::lookupTarget(ArchName, TheTriple, + Error); + if (!TheTarget) { + errs() << "error: this target '" << TheTriple.normalize() + << "/" << ArchName << "', was not found: '" << Error << "'\n"; + + abort(); + } + + std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); + if (!MRI) { + errs() << "Unable to create target register info!"; + abort(); + } + + std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, TripleName)); + if (!MAI) { + errs() << "Unable to create target asm info!"; + abort(); + } + + + MCObjectFileInfo MOFI; + MCContext Ctx(MAI.get(), MRI.get(), &MOFI, &SrcMgr); + + static const bool UsePIC = false; + static const CodeModel::Model CMModel = CodeModel::Default; + MOFI.InitMCObjectFileInfo(TheTriple, UsePIC, CMModel, Ctx); + + const unsigned OutputAsmVariant = 0; + std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo()); + MCInstPrinter *IP = TheTarget->createMCInstPrinter(Triple(TripleName), OutputAsmVariant, + *MAI, *MCII, *MRI); + if (!IP) { + errs() + << "error: unable to create instruction printer for target triple '" + << TheTriple.normalize() << "' with assembly variant " + << OutputAsmVariant << ".\n"; + + abort(); + } + + const char *ProgName = "llvm-mc-fuzzer"; + std::unique_ptr<MCSubtargetInfo> STI( + TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr)); + MCCodeEmitter *CE = nullptr; + MCAsmBackend *MAB = nullptr; + + MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); + + std::string OutputString; + raw_string_ostream Out(OutputString); + auto FOut = llvm::make_unique<formatted_raw_ostream>(Out); + + std::unique_ptr<MCStreamer> Str; + + if (FileType == OFT_AssemblyFile) { + Str.reset(TheTarget->createAsmStreamer( + Ctx, std::move(FOut), AsmVerbose, + UseDwarfDirectory, IP, CE, MAB, ShowInst)); + } else { + assert(FileType == OFT_ObjectFile && "Invalid file type!"); + + std::error_code EC; + const std::string OutputFilename = "-"; + auto Out = llvm::make_unique<tool_output_file>(OutputFilename, EC, + sys::fs::F_None); + if (EC) { + errs() << EC.message() << '\n'; + abort(); + } + + // Don't waste memory on names of temp labels. + Ctx.setUseNamesOnTempLabels(false); + + std::unique_ptr<buffer_ostream> BOS; + raw_pwrite_stream *OS = &Out->os(); + if (!Out->os().supportsSeeking()) { + BOS = make_unique<buffer_ostream>(Out->os()); + OS = BOS.get(); + } + + MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx); + MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, MCPU, + MCOptions); + Str.reset(TheTarget->createMCObjectStreamer( + TheTriple, Ctx, *MAB, *OS, CE, *STI, MCOptions.MCRelaxAll, + MCOptions.MCIncrementalLinkerCompatible, + /*DWARFMustBeAtTheEnd*/ false)); + } + const int Res = AssembleInput(ProgName, TheTarget, SrcMgr, Ctx, *Str, *MAI, *STI, + *MCII, MCOptions); + + (void) Res; + + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + return AssembleOneInput(Data, Size); +} + +int LLVMFuzzerInitialize(int *argc, char ***argv) { + // The command line is unusual compared to other fuzzers due to the need to + // specify the target. Options like -triple, -mcpu, and -mattr work like + // their counterparts in llvm-mc, while -fuzzer-args collects options for the + // fuzzer itself. + // + // Examples: + // + // Fuzz the big-endian MIPS32R6 disassembler using 100,000 inputs of up to + // 4-bytes each and use the contents of ./corpus as the test corpus: + // llvm-mc-fuzzer -triple mips-linux-gnu -mcpu=mips32r6 -disassemble \ + // -fuzzer-args -max_len=4 -runs=100000 ./corpus + // + // Infinitely fuzz the little-endian MIPS64R2 disassembler with the MSA + // feature enabled using up to 64-byte inputs: + // llvm-mc-fuzzer -triple mipsel-linux-gnu -mcpu=mips64r2 -mattr=msa \ + // -disassemble -fuzzer-args ./corpus + // + // If your aim is to find instructions that are not tested, then it is + // advisable to constrain the maximum input size to a single instruction + // using -max_len as in the first example. This results in a test corpus of + // individual instructions that test unique paths. Without this constraint, + // there will be considerable redundancy in the corpus. + + char **OriginalArgv = *argv; + + LLVMInitializeAllTargetInfos(); + LLVMInitializeAllTargetMCs(); + LLVMInitializeAllAsmParsers(); + + cl::ParseCommandLineOptions(*argc, OriginalArgv); + + // Rebuild the argv without the arguments llvm-mc-fuzzer consumed so that + // the driver can parse its arguments. + // + // FuzzerArgs cannot provide the non-const pointer that OriginalArgv needs. + // Re-use the strings from OriginalArgv instead of copying FuzzerArg to a + // non-const buffer to avoid the need to clean up when the fuzzer terminates. + ModifiedArgv.push_back(OriginalArgv[0]); + for (const auto &FuzzerArg : FuzzerArgs) { + for (int i = 1; i < *argc; ++i) { + if (FuzzerArg == OriginalArgv[i]) + ModifiedArgv.push_back(OriginalArgv[i]); + } + } + *argc = ModifiedArgv.size(); + *argv = ModifiedArgv.data(); + + // Package up features to be passed to target/subtarget + // We have to pass it via a global since the callback doesn't + // permit any user data. + if (MAttrs.size()) { + SubtargetFeatures Features; + for (unsigned i = 0; i != MAttrs.size(); ++i) + Features.AddFeature(MAttrs[i]); + FeaturesStr = Features.getString(); + } + + if (TripleName.empty()) + TripleName = sys::getDefaultTargetTriple(); + + return 0; +} diff --git a/tools/llvm-mc-fuzzer/CMakeLists.txt b/tools/llvm-mc-disassemble-fuzzer/CMakeLists.txt index b42b3eee3c981..c539f823e57f4 100644 --- a/tools/llvm-mc-fuzzer/CMakeLists.txt +++ b/tools/llvm-mc-disassemble-fuzzer/CMakeLists.txt @@ -3,16 +3,19 @@ if( LLVM_USE_SANITIZE_COVERAGE ) ${CMAKE_CURRENT_SOURCE_DIR}/../../lib/Fuzzer) set(LLVM_LINK_COMPONENTS + AllTargetsAsmPrinters AllTargetsDescs AllTargetsDisassemblers AllTargetsInfos MC MCDisassembler + MCParser Support ) - add_llvm_tool(llvm-mc-fuzzer - llvm-mc-fuzzer.cpp) - target_link_libraries(llvm-mc-fuzzer + add_llvm_tool(llvm-mc-disassemble-fuzzer + llvm-mc-disassemble-fuzzer.cpp) + + target_link_libraries(llvm-mc-disassemble-fuzzer LLVMFuzzer ) endif() diff --git a/tools/llvm-mc-fuzzer/llvm-mc-fuzzer.cpp b/tools/llvm-mc-disassemble-fuzzer/llvm-mc-disassemble-fuzzer.cpp index e31ea762add53..643afe64073e6 100644 --- a/tools/llvm-mc-fuzzer/llvm-mc-fuzzer.cpp +++ b/tools/llvm-mc-disassemble-fuzzer/llvm-mc-disassemble-fuzzer.cpp @@ -20,19 +20,6 @@ using namespace llvm; const unsigned AssemblyTextBufSize = 80; -enum ActionType { - AC_Assemble, - AC_Disassemble -}; - -static cl::opt<ActionType> -Action(cl::desc("Action to perform:"), - cl::init(AC_Assemble), - cl::values(clEnumValN(AC_Assemble, "assemble", - "Assemble a .s file (default)"), - clEnumValN(AC_Disassemble, "disassemble", - "Disassemble strings of hex bytes"))); - static cl::opt<std::string> TripleName("triple", cl::desc("Target triple to assemble for, " "see -version for available targets")); @@ -88,13 +75,7 @@ int DisassembleOneInput(const uint8_t *Data, size_t Size) { } int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Action == AC_Assemble) - errs() << "error: -assemble is not implemented\n"; - else if (Action == AC_Disassemble) - return DisassembleOneInput(Data, Size); - - llvm_unreachable("Unknown action"); - return 0; + return DisassembleOneInput(Data, Size); } int LLVMFuzzerInitialize(int *argc, char ***argv) { @@ -155,5 +136,8 @@ int LLVMFuzzerInitialize(int *argc, char ***argv) { FeaturesStr = Features.getString(); } + if (TripleName.empty()) + TripleName = sys::getDefaultTargetTriple(); + return 0; } diff --git a/tools/llvm-mc/llvm-mc.cpp b/tools/llvm-mc/llvm-mc.cpp index 497fb1987764e..87efac2d33cf7 100644 --- a/tools/llvm-mc/llvm-mc.cpp +++ b/tools/llvm-mc/llvm-mc.cpp @@ -516,7 +516,7 @@ int main(int argc, char **argv) { Ctx.setGenDwarfForAssembly(GenDwarfForAssembly); // Default to 4 for dwarf version. unsigned DwarfVersion = MCOptions.DwarfVersion ? MCOptions.DwarfVersion : 4; - if (DwarfVersion < 2 || DwarfVersion > 4) { + if (DwarfVersion < 2 || DwarfVersion > 5) { errs() << ProgName << ": Dwarf version " << DwarfVersion << " is not supported." << '\n'; return 1; diff --git a/tools/llvm-nm/llvm-nm.cpp b/tools/llvm-nm/llvm-nm.cpp index aa89f89c31d56..a07fcef35ebe5 100644 --- a/tools/llvm-nm/llvm-nm.cpp +++ b/tools/llvm-nm/llvm-nm.cpp @@ -29,6 +29,7 @@ #include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Object/Wasm.h" #include "llvm/Support/COFF.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" @@ -129,6 +130,7 @@ cl::opt<bool> PrintSize("print-size", cl::desc("Show symbol size instead of address")); cl::alias PrintSizeS("S", cl::desc("Alias for --print-size"), cl::aliasopt(PrintSize), cl::Grouping); +bool MachOPrintSizeWarning = false; cl::opt<bool> SizeSort("size-sort", cl::desc("Sort symbols by size")); @@ -269,6 +271,8 @@ static char isSymbolList64Bit(SymbolicFile &Obj) { return Triple(IRObj->getTargetTriple()).isArch64Bit(); if (isa<COFFObjectFile>(Obj)) return false; + if (isa<WasmObjectFile>(Obj)) + return false; if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj)) return MachO->is64Bit(); return cast<ELFObjectFileBase>(Obj).getBytesInAddress() == 8; @@ -882,6 +886,13 @@ static char getSymbolNMTypeChar(MachOObjectFile &Obj, basic_symbol_iterator I) { return '?'; } +static char getSymbolNMTypeChar(WasmObjectFile &Obj, basic_symbol_iterator I) { + uint32_t Flags = I->getFlags(); + if (Flags & SymbolRef::SF_Executable) + return 't'; + return 'd'; +} + static char getSymbolNMTypeChar(IRObjectFile &Obj, basic_symbol_iterator I) { uint32_t Flags = I->getFlags(); // FIXME: should we print 'b'? At the IR level we cannot be sure if this @@ -923,6 +934,8 @@ static char getNMTypeChar(SymbolicFile &Obj, basic_symbol_iterator I) { Ret = getSymbolNMTypeChar(*COFF, I); else if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj)) Ret = getSymbolNMTypeChar(*MachO, I); + else if (WasmObjectFile *Wasm = dyn_cast<WasmObjectFile>(&Obj)) + Ret = getSymbolNMTypeChar(*Wasm, I); else Ret = getSymbolNMTypeChar(cast<ELFObjectFileBase>(Obj), I); @@ -1057,15 +1070,19 @@ static bool checkMachOAndArchFlags(SymbolicFile *O, std::string &Filename) { MachO::mach_header H; MachO::mach_header_64 H_64; Triple T; + const char *McpuDefault, *ArchFlag; if (MachO->is64Bit()) { H_64 = MachO->MachOObjectFile::getHeader64(); - T = MachOObjectFile::getArchTriple(H_64.cputype, H_64.cpusubtype); + T = MachOObjectFile::getArchTriple(H_64.cputype, H_64.cpusubtype, + &McpuDefault, &ArchFlag); } else { H = MachO->MachOObjectFile::getHeader(); - T = MachOObjectFile::getArchTriple(H.cputype, H.cpusubtype); + T = MachOObjectFile::getArchTriple(H.cputype, H.cpusubtype, + &McpuDefault, &ArchFlag); } + const std::string ArchFlagName(ArchFlag); if (none_of(ArchFlags, [&](const std::string &Name) { - return Name == T.getArchName(); + return Name == ArchFlagName; })) { error("No architecture specified", Filename); return false; @@ -1120,6 +1137,11 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { continue; } if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) { + if (!MachOPrintSizeWarning && PrintSize && isa<MachOObjectFile>(O)) { + errs() << ToolName << ": warning sizes with -print-size for Mach-O " + "files are always zero.\n"; + MachOPrintSizeWarning = true; + } if (!checkMachOAndArchFlags(O, Filename)) return; if (!PrintFileName) { @@ -1357,6 +1379,11 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { return; } if (SymbolicFile *O = dyn_cast<SymbolicFile>(&Bin)) { + if (!MachOPrintSizeWarning && PrintSize && isa<MachOObjectFile>(O)) { + errs() << ToolName << ": warning sizes with -print-size for Mach-O files " + "are always zero.\n"; + MachOPrintSizeWarning = true; + } if (!checkMachOAndArchFlags(O, Filename)) return; dumpSymbolNamesFromObject(*O, true); diff --git a/tools/llvm-objdump/MachODump.cpp b/tools/llvm-objdump/MachODump.cpp index 563084856f6f8..9e02951a4a93f 100644 --- a/tools/llvm-objdump/MachODump.cpp +++ b/tools/llvm-objdump/MachODump.cpp @@ -68,9 +68,6 @@ static cl::opt<std::string> DSYMFile("dsym", static cl::opt<bool> FullLeadingAddr("full-leading-addr", cl::desc("Print full leading address")); -static cl::opt<bool> NoLeadingAddr("no-leading-addr", - cl::desc("Print no leading address")); - static cl::opt<bool> NoLeadingHeaders("no-leading-headers", cl::desc("Print no leading headers")); @@ -1142,7 +1139,7 @@ static void DumpInfoPlistSectionContents(StringRef Filename, StringRef BytesStr; Section.getContents(BytesStr); const char *sect = reinterpret_cast<const char *>(BytesStr.data()); - outs() << sect; + outs() << format("%.*s", BytesStr.size(), sect) << "\n"; return; } } @@ -1566,8 +1563,13 @@ void llvm::ParseInputMachO(StringRef Filename) { // Attempt to open the binary. Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(Filename); - if (!BinaryOrErr) - report_error(Filename, BinaryOrErr.takeError()); + if (!BinaryOrErr) { + if (auto E = isNotObjectErrorInvalidFileType(BinaryOrErr.takeError())) + report_error(Filename, std::move(E)); + else + outs() << Filename << ": is not an object file\n"; + return; + } Binary &Bin = *BinaryOrErr.get().getBinary(); if (Archive *A = dyn_cast<Archive>(&Bin)) { @@ -5269,42 +5271,70 @@ static void printObjc2_64bit_MetaData(MachOObjectFile *O, bool verbose) { SectionRef CL = get_section(O, "__OBJC2", "__class_list"); if (CL == SectionRef()) CL = get_section(O, "__DATA", "__objc_classlist"); + if (CL == SectionRef()) + CL = get_section(O, "__DATA_CONST", "__objc_classlist"); + if (CL == SectionRef()) + CL = get_section(O, "__DATA_DIRTY", "__objc_classlist"); info.S = CL; walk_pointer_list_64("class", CL, O, &info, print_class64_t); SectionRef CR = get_section(O, "__OBJC2", "__class_refs"); if (CR == SectionRef()) CR = get_section(O, "__DATA", "__objc_classrefs"); + if (CR == SectionRef()) + CR = get_section(O, "__DATA_CONST", "__objc_classrefs"); + if (CR == SectionRef()) + CR = get_section(O, "__DATA_DIRTY", "__objc_classrefs"); info.S = CR; walk_pointer_list_64("class refs", CR, O, &info, nullptr); SectionRef SR = get_section(O, "__OBJC2", "__super_refs"); if (SR == SectionRef()) SR = get_section(O, "__DATA", "__objc_superrefs"); + if (SR == SectionRef()) + SR = get_section(O, "__DATA_CONST", "__objc_superrefs"); + if (SR == SectionRef()) + SR = get_section(O, "__DATA_DIRTY", "__objc_superrefs"); info.S = SR; walk_pointer_list_64("super refs", SR, O, &info, nullptr); SectionRef CA = get_section(O, "__OBJC2", "__category_list"); if (CA == SectionRef()) CA = get_section(O, "__DATA", "__objc_catlist"); + if (CA == SectionRef()) + CA = get_section(O, "__DATA_CONST", "__objc_catlist"); + if (CA == SectionRef()) + CA = get_section(O, "__DATA_DIRTY", "__objc_catlist"); info.S = CA; walk_pointer_list_64("category", CA, O, &info, print_category64_t); SectionRef PL = get_section(O, "__OBJC2", "__protocol_list"); if (PL == SectionRef()) PL = get_section(O, "__DATA", "__objc_protolist"); + if (PL == SectionRef()) + PL = get_section(O, "__DATA_CONST", "__objc_protolist"); + if (PL == SectionRef()) + PL = get_section(O, "__DATA_DIRTY", "__objc_protolist"); info.S = PL; walk_pointer_list_64("protocol", PL, O, &info, nullptr); SectionRef MR = get_section(O, "__OBJC2", "__message_refs"); if (MR == SectionRef()) MR = get_section(O, "__DATA", "__objc_msgrefs"); + if (MR == SectionRef()) + MR = get_section(O, "__DATA_CONST", "__objc_msgrefs"); + if (MR == SectionRef()) + MR = get_section(O, "__DATA_DIRTY", "__objc_msgrefs"); info.S = MR; print_message_refs64(MR, &info); SectionRef II = get_section(O, "__OBJC2", "__image_info"); if (II == SectionRef()) II = get_section(O, "__DATA", "__objc_imageinfo"); + if (II == SectionRef()) + II = get_section(O, "__DATA_CONST", "__objc_imageinfo"); + if (II == SectionRef()) + II = get_section(O, "__DATA_DIRTY", "__objc_imageinfo"); info.S = II; print_image_info64(II, &info); } @@ -5335,75 +5365,75 @@ static void printObjc2_32bit_MetaData(MachOObjectFile *O, bool verbose) { info.adrp_addr = 0; info.adrp_inst = 0; - const SectionRef CL = get_section(O, "__OBJC2", "__class_list"); - if (CL != SectionRef()) { - info.S = CL; - walk_pointer_list_32("class", CL, O, &info, print_class32_t); - } else { - const SectionRef CL = get_section(O, "__DATA", "__objc_classlist"); - info.S = CL; - walk_pointer_list_32("class", CL, O, &info, print_class32_t); - } + SectionRef CL = get_section(O, "__OBJC2", "__class_list"); + if (CL == SectionRef()) + CL = get_section(O, "__DATA", "__objc_classlist"); + if (CL == SectionRef()) + CL = get_section(O, "__DATA_CONST", "__objc_classlist"); + if (CL == SectionRef()) + CL = get_section(O, "__DATA_DIRTY", "__objc_classlist"); + info.S = CL; + walk_pointer_list_32("class", CL, O, &info, print_class32_t); - const SectionRef CR = get_section(O, "__OBJC2", "__class_refs"); - if (CR != SectionRef()) { - info.S = CR; - walk_pointer_list_32("class refs", CR, O, &info, nullptr); - } else { - const SectionRef CR = get_section(O, "__DATA", "__objc_classrefs"); - info.S = CR; - walk_pointer_list_32("class refs", CR, O, &info, nullptr); - } + SectionRef CR = get_section(O, "__OBJC2", "__class_refs"); + if (CR == SectionRef()) + CR = get_section(O, "__DATA", "__objc_classrefs"); + if (CR == SectionRef()) + CR = get_section(O, "__DATA_CONST", "__objc_classrefs"); + if (CR == SectionRef()) + CR = get_section(O, "__DATA_DIRTY", "__objc_classrefs"); + info.S = CR; + walk_pointer_list_32("class refs", CR, O, &info, nullptr); - const SectionRef SR = get_section(O, "__OBJC2", "__super_refs"); - if (SR != SectionRef()) { - info.S = SR; - walk_pointer_list_32("super refs", SR, O, &info, nullptr); - } else { - const SectionRef SR = get_section(O, "__DATA", "__objc_superrefs"); - info.S = SR; - walk_pointer_list_32("super refs", SR, O, &info, nullptr); - } + SectionRef SR = get_section(O, "__OBJC2", "__super_refs"); + if (SR == SectionRef()) + SR = get_section(O, "__DATA", "__objc_superrefs"); + if (SR == SectionRef()) + SR = get_section(O, "__DATA_CONST", "__objc_superrefs"); + if (SR == SectionRef()) + SR = get_section(O, "__DATA_DIRTY", "__objc_superrefs"); + info.S = SR; + walk_pointer_list_32("super refs", SR, O, &info, nullptr); - const SectionRef CA = get_section(O, "__OBJC2", "__category_list"); - if (CA != SectionRef()) { - info.S = CA; - walk_pointer_list_32("category", CA, O, &info, print_category32_t); - } else { - const SectionRef CA = get_section(O, "__DATA", "__objc_catlist"); - info.S = CA; - walk_pointer_list_32("category", CA, O, &info, print_category32_t); - } + SectionRef CA = get_section(O, "__OBJC2", "__category_list"); + if (CA == SectionRef()) + CA = get_section(O, "__DATA", "__objc_catlist"); + if (CA == SectionRef()) + CA = get_section(O, "__DATA_CONST", "__objc_catlist"); + if (CA == SectionRef()) + CA = get_section(O, "__DATA_DIRTY", "__objc_catlist"); + info.S = CA; + walk_pointer_list_32("category", CA, O, &info, print_category32_t); - const SectionRef PL = get_section(O, "__OBJC2", "__protocol_list"); - if (PL != SectionRef()) { - info.S = PL; - walk_pointer_list_32("protocol", PL, O, &info, nullptr); - } else { - const SectionRef PL = get_section(O, "__DATA", "__objc_protolist"); - info.S = PL; - walk_pointer_list_32("protocol", PL, O, &info, nullptr); - } + SectionRef PL = get_section(O, "__OBJC2", "__protocol_list"); + if (PL == SectionRef()) + PL = get_section(O, "__DATA", "__objc_protolist"); + if (PL == SectionRef()) + PL = get_section(O, "__DATA_CONST", "__objc_protolist"); + if (PL == SectionRef()) + PL = get_section(O, "__DATA_DIRTY", "__objc_protolist"); + info.S = PL; + walk_pointer_list_32("protocol", PL, O, &info, nullptr); - const SectionRef MR = get_section(O, "__OBJC2", "__message_refs"); - if (MR != SectionRef()) { - info.S = MR; - print_message_refs32(MR, &info); - } else { - const SectionRef MR = get_section(O, "__DATA", "__objc_msgrefs"); - info.S = MR; - print_message_refs32(MR, &info); - } + SectionRef MR = get_section(O, "__OBJC2", "__message_refs"); + if (MR == SectionRef()) + MR = get_section(O, "__DATA", "__objc_msgrefs"); + if (MR == SectionRef()) + MR = get_section(O, "__DATA_CONST", "__objc_msgrefs"); + if (MR == SectionRef()) + MR = get_section(O, "__DATA_DIRTY", "__objc_msgrefs"); + info.S = MR; + print_message_refs32(MR, &info); - const SectionRef II = get_section(O, "__OBJC2", "__image_info"); - if (II != SectionRef()) { - info.S = II; - print_image_info32(II, &info); - } else { - const SectionRef II = get_section(O, "__DATA", "__objc_imageinfo"); - info.S = II; - print_image_info32(II, &info); - } + SectionRef II = get_section(O, "__OBJC2", "__image_info"); + if (II == SectionRef()) + II = get_section(O, "__DATA", "__objc_imageinfo"); + if (II == SectionRef()) + II = get_section(O, "__DATA_CONST", "__objc_imageinfo"); + if (II == SectionRef()) + II = get_section(O, "__DATA_DIRTY", "__objc_imageinfo"); + info.S = II; + print_image_info32(II, &info); } static bool printObjc1_32bit_MetaData(MachOObjectFile *O, bool verbose) { @@ -6597,6 +6627,12 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, if (Bytes.size() == 0) return; + // If the section has symbols but no symbol at the start of the section + // these are used to make sure the bytes before the first symbol are + // disassembled. + bool FirstSymbol = true; + bool FirstSymbolAtSectionStart = true; + // Disassemble symbol by symbol. for (unsigned SymIdx = 0; SymIdx != Symbols.size(); SymIdx++) { Expected<StringRef> SymNameOrErr = Symbols[SymIdx].getName(); @@ -6686,11 +6722,29 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, // (i.e. we're not targeting M-class) and the function is Thumb. bool UseThumbTarget = IsThumb && ThumbTarget; - outs() << SymName << ":\n"; + // If we are not specifying a symbol to start disassembly with and this + // is the first symbol in the section but not at the start of the section + // then move the disassembly index to the start of the section and + // don't print the symbol name just yet. This is so the bytes before the + // first symbol are disassembled. + uint64_t SymbolStart = Start; + if (DisSymName.empty() && FirstSymbol && Start != 0) { + FirstSymbolAtSectionStart = false; + Start = 0; + } + else + outs() << SymName << ":\n"; + DILineInfo lastLine; for (uint64_t Index = Start; Index < End; Index += Size) { MCInst Inst; + // If this is the first symbol in the section and it was not at the + // start of the section, see if we are at its Index now and if so print + // the symbol name. + if (FirstSymbol && !FirstSymbolAtSectionStart && Index == SymbolStart) + outs() << SymName << ":\n"; + uint64_t PC = SectAddress + Index; if (!NoLeadingAddr) { if (FullLeadingAddr) { @@ -6783,6 +6837,9 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, } } } + // Now that we are done disassembled the first symbol set the bool that + // were doing this to false. + FirstSymbol = false; } if (!symbolTableWorked) { // Reading the symbol table didn't work, disassemble the whole section. @@ -6793,8 +6850,10 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, MCInst Inst; uint64_t PC = SectAddress + Index; + SmallVector<char, 64> AnnotationsBytes; + raw_svector_ostream Annotations(AnnotationsBytes); if (DisAsm->getInstruction(Inst, InstSize, Bytes.slice(Index), PC, - DebugOut, nulls())) { + DebugOut, Annotations)) { if (!NoLeadingAddr) { if (FullLeadingAddr) { if (MachOOF->is64Bit()) @@ -6809,7 +6868,8 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, outs() << "\t"; dumpBytes(makeArrayRef(Bytes.data() + Index, InstSize), outs()); } - IP->printInst(&Inst, outs(), "", *STI); + StringRef AnnotationsStr = Annotations.str(); + IP->printInst(&Inst, outs(), AnnotationsStr, *STI); outs() << "\n"; } else { unsigned int Arch = MachOOF->getArch(); @@ -8169,6 +8229,51 @@ static void PrintVersionMinLoadCommand(MachO::version_min_command vd) { outs() << "\n"; } +static void PrintNoteLoadCommand(MachO::note_command Nt) { + outs() << " cmd LC_NOTE\n"; + outs() << " cmdsize " << Nt.cmdsize; + if (Nt.cmdsize != sizeof(struct MachO::note_command)) + outs() << " Incorrect size\n"; + else + outs() << "\n"; + const char *d = Nt.data_owner; + outs() << "data_owner " << format("%.16s\n", d); + outs() << " offset " << Nt.offset << "\n"; + outs() << " size " << Nt.size << "\n"; +} + +static void PrintBuildToolVersion(MachO::build_tool_version bv) { + outs() << " tool " << MachOObjectFile::getBuildTool(bv.tool) << "\n"; + outs() << " version " << MachOObjectFile::getVersionString(bv.version) + << "\n"; +} + +static void PrintBuildVersionLoadCommand(const MachOObjectFile *obj, + MachO::build_version_command bd) { + outs() << " cmd LC_BUILD_VERSION\n"; + outs() << " cmdsize " << bd.cmdsize; + if (bd.cmdsize != + sizeof(struct MachO::build_version_command) + + bd.ntools * sizeof(struct MachO::build_tool_version)) + outs() << " Incorrect size\n"; + else + outs() << "\n"; + outs() << " platform " << MachOObjectFile::getBuildPlatform(bd.platform) + << "\n"; + if (bd.sdk) + outs() << " sdk " << MachOObjectFile::getVersionString(bd.sdk) + << "\n"; + else + outs() << " sdk n/a\n"; + outs() << " minos " << MachOObjectFile::getVersionString(bd.minos) + << "\n"; + outs() << " ntools " << bd.ntools << "\n"; + for (unsigned i = 0; i < bd.ntools; ++i) { + MachO::build_tool_version bv = obj->getBuildToolVersion(i); + PrintBuildToolVersion(bv); + } +} + static void PrintSourceVersionCommand(MachO::source_version_command sd) { outs() << " cmd LC_SOURCE_VERSION\n"; outs() << " cmdsize " << sd.cmdsize; @@ -8374,6 +8479,25 @@ static void PrintRoutinesCommand64(MachO::routines_command_64 r) { outs() << " reserved6 " << r.reserved6 << "\n"; } +static void Print_x86_thread_state32_t(MachO::x86_thread_state32_t &cpu32) { + outs() << "\t eax " << format("0x%08" PRIx32, cpu32.eax); + outs() << " ebx " << format("0x%08" PRIx32, cpu32.ebx); + outs() << " ecx " << format("0x%08" PRIx32, cpu32.ecx); + outs() << " edx " << format("0x%08" PRIx32, cpu32.edx) << "\n"; + outs() << "\t edi " << format("0x%08" PRIx32, cpu32.edi); + outs() << " esi " << format("0x%08" PRIx32, cpu32.esi); + outs() << " ebp " << format("0x%08" PRIx32, cpu32.ebp); + outs() << " esp " << format("0x%08" PRIx32, cpu32.esp) << "\n"; + outs() << "\t ss " << format("0x%08" PRIx32, cpu32.ss); + outs() << " eflags " << format("0x%08" PRIx32, cpu32.eflags); + outs() << " eip " << format("0x%08" PRIx32, cpu32.eip); + outs() << " cs " << format("0x%08" PRIx32, cpu32.cs) << "\n"; + outs() << "\t ds " << format("0x%08" PRIx32, cpu32.ds); + outs() << " es " << format("0x%08" PRIx32, cpu32.es); + outs() << " fs " << format("0x%08" PRIx32, cpu32.fs); + outs() << " gs " << format("0x%08" PRIx32, cpu32.gs) << "\n"; +} + static void Print_x86_thread_state64_t(MachO::x86_thread_state64_t &cpu64) { outs() << " rax " << format("0x%016" PRIx64, cpu64.rax); outs() << " rbx " << format("0x%016" PRIx64, cpu64.rbx); @@ -8611,7 +8735,85 @@ static void PrintThreadCommand(MachO::thread_command t, const char *Ptr, const char *begin = Ptr + sizeof(struct MachO::thread_command); const char *end = Ptr + t.cmdsize; uint32_t flavor, count, left; - if (cputype == MachO::CPU_TYPE_X86_64) { + if (cputype == MachO::CPU_TYPE_I386) { + while (begin < end) { + if (end - begin > (ptrdiff_t)sizeof(uint32_t)) { + memcpy((char *)&flavor, begin, sizeof(uint32_t)); + begin += sizeof(uint32_t); + } else { + flavor = 0; + begin = end; + } + if (isLittleEndian != sys::IsLittleEndianHost) + sys::swapByteOrder(flavor); + if (end - begin > (ptrdiff_t)sizeof(uint32_t)) { + memcpy((char *)&count, begin, sizeof(uint32_t)); + begin += sizeof(uint32_t); + } else { + count = 0; + begin = end; + } + if (isLittleEndian != sys::IsLittleEndianHost) + sys::swapByteOrder(count); + if (flavor == MachO::x86_THREAD_STATE32) { + outs() << " flavor i386_THREAD_STATE\n"; + if (count == MachO::x86_THREAD_STATE32_COUNT) + outs() << " count i386_THREAD_STATE_COUNT\n"; + else + outs() << " count " << count + << " (not x86_THREAD_STATE32_COUNT)\n"; + MachO::x86_thread_state32_t cpu32; + left = end - begin; + if (left >= sizeof(MachO::x86_thread_state32_t)) { + memcpy(&cpu32, begin, sizeof(MachO::x86_thread_state32_t)); + begin += sizeof(MachO::x86_thread_state32_t); + } else { + memset(&cpu32, '\0', sizeof(MachO::x86_thread_state32_t)); + memcpy(&cpu32, begin, left); + begin += left; + } + if (isLittleEndian != sys::IsLittleEndianHost) + swapStruct(cpu32); + Print_x86_thread_state32_t(cpu32); + } else if (flavor == MachO::x86_THREAD_STATE) { + outs() << " flavor x86_THREAD_STATE\n"; + if (count == MachO::x86_THREAD_STATE_COUNT) + outs() << " count x86_THREAD_STATE_COUNT\n"; + else + outs() << " count " << count + << " (not x86_THREAD_STATE_COUNT)\n"; + struct MachO::x86_thread_state_t ts; + left = end - begin; + if (left >= sizeof(MachO::x86_thread_state_t)) { + memcpy(&ts, begin, sizeof(MachO::x86_thread_state_t)); + begin += sizeof(MachO::x86_thread_state_t); + } else { + memset(&ts, '\0', sizeof(MachO::x86_thread_state_t)); + memcpy(&ts, begin, left); + begin += left; + } + if (isLittleEndian != sys::IsLittleEndianHost) + swapStruct(ts); + if (ts.tsh.flavor == MachO::x86_THREAD_STATE32) { + outs() << "\t tsh.flavor x86_THREAD_STATE32 "; + if (ts.tsh.count == MachO::x86_THREAD_STATE32_COUNT) + outs() << "tsh.count x86_THREAD_STATE32_COUNT\n"; + else + outs() << "tsh.count " << ts.tsh.count + << " (not x86_THREAD_STATE32_COUNT\n"; + Print_x86_thread_state32_t(ts.uts.ts32); + } else { + outs() << "\t tsh.flavor " << ts.tsh.flavor << " tsh.count " + << ts.tsh.count << "\n"; + } + } else { + outs() << " flavor " << flavor << " (unknown)\n"; + outs() << " count " << count << "\n"; + outs() << " state (unknown)\n"; + begin += count * sizeof(uint32_t); + } + } + } else if (cputype == MachO::CPU_TYPE_X86_64) { while (begin < end) { if (end - begin > (ptrdiff_t)sizeof(uint32_t)) { memcpy((char *)&flavor, begin, sizeof(uint32_t)); @@ -9014,6 +9216,13 @@ static void PrintLoadCommands(const MachOObjectFile *Obj, uint32_t filetype, Command.C.cmd == MachO::LC_VERSION_MIN_WATCHOS) { MachO::version_min_command Vd = Obj->getVersionMinLoadCommand(Command); PrintVersionMinLoadCommand(Vd); + } else if (Command.C.cmd == MachO::LC_NOTE) { + MachO::note_command Nt = Obj->getNoteLoadCommand(Command); + PrintNoteLoadCommand(Nt); + } else if (Command.C.cmd == MachO::LC_BUILD_VERSION) { + MachO::build_version_command Bv = + Obj->getBuildVersionLoadCommand(Command); + PrintBuildVersionLoadCommand(Obj, Bv); } else if (Command.C.cmd == MachO::LC_SOURCE_VERSION) { MachO::source_version_command Sd = Obj->getSourceVersionCommand(Command); PrintSourceVersionCommand(Sd); @@ -9182,117 +9391,21 @@ void llvm::printMachOExportsTrie(const object::MachOObjectFile *Obj) { // rebase table dumping //===----------------------------------------------------------------------===// -namespace { -class SegInfo { -public: - SegInfo(const object::MachOObjectFile *Obj); - - StringRef segmentName(uint32_t SegIndex); - StringRef sectionName(uint32_t SegIndex, uint64_t SegOffset); - uint64_t address(uint32_t SegIndex, uint64_t SegOffset); - bool isValidSegIndexAndOffset(uint32_t SegIndex, uint64_t SegOffset); - -private: - struct SectionInfo { - uint64_t Address; - uint64_t Size; - StringRef SectionName; - StringRef SegmentName; - uint64_t OffsetInSegment; - uint64_t SegmentStartAddress; - uint32_t SegmentIndex; - }; - const SectionInfo &findSection(uint32_t SegIndex, uint64_t SegOffset); - SmallVector<SectionInfo, 32> Sections; -}; -} - -SegInfo::SegInfo(const object::MachOObjectFile *Obj) { - // Build table of sections so segIndex/offset pairs can be translated. - uint32_t CurSegIndex = Obj->hasPageZeroSegment() ? 1 : 0; - StringRef CurSegName; - uint64_t CurSegAddress; - for (const SectionRef &Section : Obj->sections()) { - SectionInfo Info; - error(Section.getName(Info.SectionName)); - Info.Address = Section.getAddress(); - Info.Size = Section.getSize(); - Info.SegmentName = - Obj->getSectionFinalSegmentName(Section.getRawDataRefImpl()); - if (!Info.SegmentName.equals(CurSegName)) { - ++CurSegIndex; - CurSegName = Info.SegmentName; - CurSegAddress = Info.Address; - } - Info.SegmentIndex = CurSegIndex - 1; - Info.OffsetInSegment = Info.Address - CurSegAddress; - Info.SegmentStartAddress = CurSegAddress; - Sections.push_back(Info); - } -} - -StringRef SegInfo::segmentName(uint32_t SegIndex) { - for (const SectionInfo &SI : Sections) { - if (SI.SegmentIndex == SegIndex) - return SI.SegmentName; - } - llvm_unreachable("invalid segIndex"); -} - -bool SegInfo::isValidSegIndexAndOffset(uint32_t SegIndex, - uint64_t OffsetInSeg) { - for (const SectionInfo &SI : Sections) { - if (SI.SegmentIndex != SegIndex) - continue; - if (SI.OffsetInSegment > OffsetInSeg) - continue; - if (OffsetInSeg >= (SI.OffsetInSegment + SI.Size)) - continue; - return true; - } - return false; -} - -const SegInfo::SectionInfo &SegInfo::findSection(uint32_t SegIndex, - uint64_t OffsetInSeg) { - for (const SectionInfo &SI : Sections) { - if (SI.SegmentIndex != SegIndex) - continue; - if (SI.OffsetInSegment > OffsetInSeg) - continue; - if (OffsetInSeg >= (SI.OffsetInSegment + SI.Size)) - continue; - return SI; - } - llvm_unreachable("segIndex and offset not in any section"); -} - -StringRef SegInfo::sectionName(uint32_t SegIndex, uint64_t OffsetInSeg) { - return findSection(SegIndex, OffsetInSeg).SectionName; -} - -uint64_t SegInfo::address(uint32_t SegIndex, uint64_t OffsetInSeg) { - const SectionInfo &SI = findSection(SegIndex, OffsetInSeg); - return SI.SegmentStartAddress + OffsetInSeg; -} - -void llvm::printMachORebaseTable(const object::MachOObjectFile *Obj) { - // Build table of sections so names can used in final output. - SegInfo sectionTable(Obj); - +void llvm::printMachORebaseTable(object::MachOObjectFile *Obj) { outs() << "segment section address type\n"; - for (const llvm::object::MachORebaseEntry &Entry : Obj->rebaseTable()) { - uint32_t SegIndex = Entry.segmentIndex(); - uint64_t OffsetInSeg = Entry.segmentOffset(); - StringRef SegmentName = sectionTable.segmentName(SegIndex); - StringRef SectionName = sectionTable.sectionName(SegIndex, OffsetInSeg); - uint64_t Address = sectionTable.address(SegIndex, OffsetInSeg); + Error Err = Error::success(); + for (const llvm::object::MachORebaseEntry &Entry : Obj->rebaseTable(Err)) { + StringRef SegmentName = Entry.segmentName(); + StringRef SectionName = Entry.sectionName(); + uint64_t Address = Entry.address(); // Table lines look like: __DATA __nl_symbol_ptr 0x0000F00C pointer outs() << format("%-8s %-18s 0x%08" PRIX64 " %s\n", SegmentName.str().c_str(), SectionName.str().c_str(), Address, Entry.typeName().str().c_str()); } + if (Err) + report_error(Obj->getFileName(), std::move(Err)); } static StringRef ordinalName(const object::MachOObjectFile *Obj, int Ordinal) { @@ -9320,18 +9433,15 @@ static StringRef ordinalName(const object::MachOObjectFile *Obj, int Ordinal) { // bind table dumping //===----------------------------------------------------------------------===// -void llvm::printMachOBindTable(const object::MachOObjectFile *Obj) { +void llvm::printMachOBindTable(object::MachOObjectFile *Obj) { // Build table of sections so names can used in final output. - SegInfo sectionTable(Obj); - outs() << "segment section address type " "addend dylib symbol\n"; - for (const llvm::object::MachOBindEntry &Entry : Obj->bindTable()) { - uint32_t SegIndex = Entry.segmentIndex(); - uint64_t OffsetInSeg = Entry.segmentOffset(); - StringRef SegmentName = sectionTable.segmentName(SegIndex); - StringRef SectionName = sectionTable.sectionName(SegIndex, OffsetInSeg); - uint64_t Address = sectionTable.address(SegIndex, OffsetInSeg); + Error Err = Error::success(); + for (const llvm::object::MachOBindEntry &Entry : Obj->bindTable(Err)) { + StringRef SegmentName = Entry.segmentName(); + StringRef SectionName = Entry.sectionName(); + uint64_t Address = Entry.address(); // Table lines look like: // __DATA __got 0x00012010 pointer 0 libSystem ___stack_chk_guard @@ -9346,24 +9456,22 @@ void llvm::printMachOBindTable(const object::MachOObjectFile *Obj) { << left_justify(ordinalName(Obj, Entry.ordinal()), 16) << " " << Entry.symbolName() << Attr << "\n"; } + if (Err) + report_error(Obj->getFileName(), std::move(Err)); } //===----------------------------------------------------------------------===// // lazy bind table dumping //===----------------------------------------------------------------------===// -void llvm::printMachOLazyBindTable(const object::MachOObjectFile *Obj) { - // Build table of sections so names can used in final output. - SegInfo sectionTable(Obj); - +void llvm::printMachOLazyBindTable(object::MachOObjectFile *Obj) { outs() << "segment section address " "dylib symbol\n"; - for (const llvm::object::MachOBindEntry &Entry : Obj->lazyBindTable()) { - uint32_t SegIndex = Entry.segmentIndex(); - uint64_t OffsetInSeg = Entry.segmentOffset(); - StringRef SegmentName = sectionTable.segmentName(SegIndex); - StringRef SectionName = sectionTable.sectionName(SegIndex, OffsetInSeg); - uint64_t Address = sectionTable.address(SegIndex, OffsetInSeg); + Error Err = Error::success(); + for (const llvm::object::MachOBindEntry &Entry : Obj->lazyBindTable(Err)) { + StringRef SegmentName = Entry.segmentName(); + StringRef SectionName = Entry.sectionName(); + uint64_t Address = Entry.address(); // Table lines look like: // __DATA __got 0x00012010 libSystem ___stack_chk_guard @@ -9373,30 +9481,28 @@ void llvm::printMachOLazyBindTable(const object::MachOObjectFile *Obj) { << left_justify(ordinalName(Obj, Entry.ordinal()), 16) << " " << Entry.symbolName() << "\n"; } + if (Err) + report_error(Obj->getFileName(), std::move(Err)); } //===----------------------------------------------------------------------===// // weak bind table dumping //===----------------------------------------------------------------------===// -void llvm::printMachOWeakBindTable(const object::MachOObjectFile *Obj) { - // Build table of sections so names can used in final output. - SegInfo sectionTable(Obj); - +void llvm::printMachOWeakBindTable(object::MachOObjectFile *Obj) { outs() << "segment section address " "type addend symbol\n"; - for (const llvm::object::MachOBindEntry &Entry : Obj->weakBindTable()) { + Error Err = Error::success(); + for (const llvm::object::MachOBindEntry &Entry : Obj->weakBindTable(Err)) { // Strong symbols don't have a location to update. if (Entry.flags() & MachO::BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) { outs() << " strong " << Entry.symbolName() << "\n"; continue; } - uint32_t SegIndex = Entry.segmentIndex(); - uint64_t OffsetInSeg = Entry.segmentOffset(); - StringRef SegmentName = sectionTable.segmentName(SegIndex); - StringRef SectionName = sectionTable.sectionName(SegIndex, OffsetInSeg); - uint64_t Address = sectionTable.address(SegIndex, OffsetInSeg); + StringRef SegmentName = Entry.segmentName(); + StringRef SectionName = Entry.sectionName(); + uint64_t Address = Entry.address(); // Table lines look like: // __DATA __data 0x00001000 pointer 0 _foo @@ -9407,6 +9513,8 @@ void llvm::printMachOWeakBindTable(const object::MachOObjectFile *Obj) { << format_decimal(Entry.addend(), 8) << " " << Entry.symbolName() << "\n"; } + if (Err) + report_error(Obj->getFileName(), std::move(Err)); } // get_dyld_bind_info_symbolname() is used for disassembly and passed an @@ -9417,17 +9525,15 @@ static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue, struct DisassembleInfo *info) { if (info->bindtable == nullptr) { info->bindtable = llvm::make_unique<SymbolAddressMap>(); - SegInfo sectionTable(info->O); - for (const llvm::object::MachOBindEntry &Entry : info->O->bindTable()) { - uint32_t SegIndex = Entry.segmentIndex(); - uint64_t OffsetInSeg = Entry.segmentOffset(); - if (!sectionTable.isValidSegIndexAndOffset(SegIndex, OffsetInSeg)) - continue; - uint64_t Address = sectionTable.address(SegIndex, OffsetInSeg); + Error Err = Error::success(); + for (const llvm::object::MachOBindEntry &Entry : info->O->bindTable(Err)) { + uint64_t Address = Entry.address(); StringRef name = Entry.symbolName(); if (!name.empty()) (*info->bindtable)[Address] = name; } + if (Err) + report_error(info->O->getFileName(), std::move(Err)); } auto name = info->bindtable->lookup(ReferenceValue); return !name.empty() ? name.data() : nullptr; diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp index 8373f0ce3f1ef..613d0643b4335 100644 --- a/tools/llvm-objdump/llvm-objdump.cpp +++ b/tools/llvm-objdump/llvm-objdump.cpp @@ -158,6 +158,8 @@ cl::opt<bool> llvm::NoShowRawInsn("no-show-raw-insn", cl::desc("When disassembling " "instructions, do not print " "the instruction bytes.")); +cl::opt<bool> +llvm::NoLeadingAddr("no-leading-addr", cl::desc("Print no leading address")); cl::opt<bool> llvm::UnwindInfo("unwind-info", cl::desc("Display unwind information")); @@ -213,6 +215,8 @@ cl::opt<unsigned long long> cl::value_desc("address"), cl::init(UINT64_MAX)); static StringRef ToolName; +typedef std::vector<std::tuple<uint64_t, StringRef, uint8_t>> SectionSymbolsTy; + namespace { typedef std::function<bool(llvm::object::SectionRef const &)> FilterPredicate; @@ -357,7 +361,16 @@ static const Target *getTarget(const ObjectFile *Obj = nullptr) { llvm::Triple TheTriple("unknown-unknown-unknown"); if (TripleName.empty()) { if (Obj) { - TheTriple.setArch(Triple::ArchType(Obj->getArch())); + auto Arch = Obj->getArch(); + TheTriple.setArch(Triple::ArchType(Arch)); + + // For ARM targets, try to use the build attributes to build determine + // the build target. Target features are also added, but later during + // disassembly. + if (Arch == Triple::arm || Arch == Triple::armeb) { + Obj->setARMSubArch(TheTriple); + } + // TheTriple defaults to ELF, and COFF doesn't have an environment: // the best we can do here is indicate that it is mach-o. if (Obj->isMachO()) @@ -369,8 +382,16 @@ static const Target *getTarget(const ObjectFile *Obj = nullptr) { TheTriple.setTriple("thumbv7-windows"); } } - } else + } else { TheTriple.setTriple(Triple::normalize(TripleName)); + // Use the triple, but also try to combine with ARM build attributes. + if (Obj) { + auto Arch = Obj->getArch(); + if (Arch == Triple::arm || Arch == Triple::armeb) { + Obj->setARMSubArch(TheTriple); + } + } + } // Get the target specific parser. std::string Error; @@ -491,7 +512,8 @@ public: MCSubtargetInfo const &STI, SourcePrinter *SP) { if (SP && (PrintSource || PrintLines)) SP->printSourceLine(OS, Address); - OS << format("%8" PRIx64 ":", Address); + if (!NoLeadingAddr) + OS << format("%8" PRIx64 ":", Address); if (!NoShowRawInsn) { OS << "\t"; dumpBytes(Bytes, OS); @@ -509,7 +531,8 @@ public: raw_ostream &OS) { uint32_t opcode = (Bytes[3] << 24) | (Bytes[2] << 16) | (Bytes[1] << 8) | Bytes[0]; - OS << format("%8" PRIx64 ":", Address); + if (!NoLeadingAddr) + OS << format("%8" PRIx64 ":", Address); if (!NoShowRawInsn) { OS << "\t"; dumpBytes(Bytes.slice(0, 4), OS); @@ -570,6 +593,9 @@ public: void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes, uint64_t Address, raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP) override { + if (SP && (PrintSource || PrintLines)) + SP->printSourceLine(OS, Address); + if (!MI) { OS << " <unknown>"; return; @@ -601,7 +627,8 @@ public: MCSubtargetInfo const &STI, SourcePrinter *SP) override { if (SP && (PrintSource || PrintLines)) SP->printSourceLine(OS, Address); - OS << format("%8" PRId64 ":", Address / 8); + if (!NoLeadingAddr) + OS << format("%8" PRId64 ":", Address / 8); if (!NoShowRawInsn) { OS << "\t"; dumpBytes(Bytes, OS); @@ -1091,6 +1118,52 @@ static uint8_t getElfSymbolType(const ObjectFile *Obj, const SymbolRef &Sym) { llvm_unreachable("Unsupported binary format"); } +template <class ELFT> static void +addDynamicElfSymbols(const ELFObjectFile<ELFT> *Obj, + std::map<SectionRef, SectionSymbolsTy> &AllSymbols) { + for (auto Symbol : Obj->getDynamicSymbolIterators()) { + uint8_t SymbolType = Symbol.getELFType(); + if (SymbolType != ELF::STT_FUNC || Symbol.getSize() == 0) + continue; + + Expected<uint64_t> AddressOrErr = Symbol.getAddress(); + if (!AddressOrErr) + report_error(Obj->getFileName(), AddressOrErr.takeError()); + uint64_t Address = *AddressOrErr; + + Expected<StringRef> Name = Symbol.getName(); + if (!Name) + report_error(Obj->getFileName(), Name.takeError()); + if (Name->empty()) + continue; + + Expected<section_iterator> SectionOrErr = Symbol.getSection(); + if (!SectionOrErr) + report_error(Obj->getFileName(), SectionOrErr.takeError()); + section_iterator SecI = *SectionOrErr; + if (SecI == Obj->section_end()) + continue; + + AllSymbols[*SecI].emplace_back(Address, *Name, SymbolType); + } +} + +static void +addDynamicElfSymbols(const ObjectFile *Obj, + std::map<SectionRef, SectionSymbolsTy> &AllSymbols) { + assert(Obj->isELF()); + if (auto *Elf32LEObj = dyn_cast<ELF32LEObjectFile>(Obj)) + addDynamicElfSymbols(Elf32LEObj, AllSymbols); + else if (auto *Elf64LEObj = dyn_cast<ELF64LEObjectFile>(Obj)) + addDynamicElfSymbols(Elf64LEObj, AllSymbols); + else if (auto *Elf32BEObj = dyn_cast<ELF32BEObjectFile>(Obj)) + addDynamicElfSymbols(Elf32BEObj, AllSymbols); + else if (auto *Elf64BEObj = cast<ELF64BEObjectFile>(Obj)) + addDynamicElfSymbols(Elf64BEObj, AllSymbols); + else + llvm_unreachable("Unsupported binary format"); +} + static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { if (StartAddress > StopAddress) error("Start address should be less than stop address"); @@ -1165,7 +1238,6 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { // Create a mapping from virtual address to symbol name. This is used to // pretty print the symbols while disassembling. - typedef std::vector<std::tuple<uint64_t, StringRef, uint8_t>> SectionSymbolsTy; std::map<SectionRef, SectionSymbolsTy> AllSymbols; for (const SymbolRef &Symbol : Obj->symbols()) { Expected<uint64_t> AddressOrErr = Symbol.getAddress(); @@ -1193,6 +1265,8 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { AllSymbols[*SecI].emplace_back(Address, *Name, SymbolType); } + if (AllSymbols.empty() && Obj->isELF()) + addDynamicElfSymbols(Obj, AllSymbols); // Create a mapping from virtual address to section. std::vector<std::pair<uint64_t, SectionRef>> SectionAddresses; @@ -1811,9 +1885,9 @@ void llvm::printExportsTrie(const ObjectFile *o) { } } -void llvm::printRebaseTable(const ObjectFile *o) { +void llvm::printRebaseTable(ObjectFile *o) { outs() << "Rebase table:\n"; - if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) + if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachORebaseTable(MachO); else { errs() << "This operation is only currently supported " @@ -1822,9 +1896,9 @@ void llvm::printRebaseTable(const ObjectFile *o) { } } -void llvm::printBindTable(const ObjectFile *o) { +void llvm::printBindTable(ObjectFile *o) { outs() << "Bind table:\n"; - if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) + if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOBindTable(MachO); else { errs() << "This operation is only currently supported " @@ -1833,9 +1907,9 @@ void llvm::printBindTable(const ObjectFile *o) { } } -void llvm::printLazyBindTable(const ObjectFile *o) { +void llvm::printLazyBindTable(ObjectFile *o) { outs() << "Lazy bind table:\n"; - if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) + if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOLazyBindTable(MachO); else { errs() << "This operation is only currently supported " @@ -1844,9 +1918,9 @@ void llvm::printLazyBindTable(const ObjectFile *o) { } } -void llvm::printWeakBindTable(const ObjectFile *o) { +void llvm::printWeakBindTable(ObjectFile *o) { outs() << "Weak bind table:\n"; - if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) + if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOWeakBindTable(MachO); else { errs() << "This operation is only currently supported " @@ -1944,7 +2018,7 @@ static void printPrivateFileHeaders(const ObjectFile *o, bool onlyFirst) { report_error(o->getFileName(), "Invalid/Unsupported object file format"); } -static void DumpObject(const ObjectFile *o, const Archive *a = nullptr) { +static void DumpObject(ObjectFile *o, const Archive *a = nullptr) { StringRef ArchiveName = a != nullptr ? a->getFileName() : ""; // Avoid other output when using a raw option. if (!RawClangAST) { diff --git a/tools/llvm-objdump/llvm-objdump.h b/tools/llvm-objdump/llvm-objdump.h index dace82af3d07d..2fcd506884b18 100644 --- a/tools/llvm-objdump/llvm-objdump.h +++ b/tools/llvm-objdump/llvm-objdump.h @@ -35,6 +35,7 @@ extern cl::list<std::string> FilterSections; extern cl::opt<bool> Disassemble; extern cl::opt<bool> DisassembleAll; extern cl::opt<bool> NoShowRawInsn; +extern cl::opt<bool> NoLeadingAddr; extern cl::opt<bool> PrivateHeaders; extern cl::opt<bool> FirstPrivateHeader; extern cl::opt<bool> ExportsTrie; @@ -69,10 +70,10 @@ void ParseInputMachO(StringRef Filename); void printCOFFUnwindInfo(const object::COFFObjectFile* o); void printMachOUnwindInfo(const object::MachOObjectFile* o); void printMachOExportsTrie(const object::MachOObjectFile* o); -void printMachORebaseTable(const object::MachOObjectFile* o); -void printMachOBindTable(const object::MachOObjectFile* o); -void printMachOLazyBindTable(const object::MachOObjectFile* o); -void printMachOWeakBindTable(const object::MachOObjectFile* o); +void printMachORebaseTable(object::MachOObjectFile* o); +void printMachOBindTable(object::MachOObjectFile* o); +void printMachOLazyBindTable(object::MachOObjectFile* o); +void printMachOWeakBindTable(object::MachOObjectFile* o); void printELFFileHeader(const object::ObjectFile *o); void printCOFFFileHeader(const object::ObjectFile *o); void printCOFFSymbolTable(const object::COFFImportFile *i); @@ -81,10 +82,10 @@ void printMachOFileHeader(const object::ObjectFile *o); void printMachOLoadCommands(const object::ObjectFile *o); void printWasmFileHeader(const object::ObjectFile *o); void printExportsTrie(const object::ObjectFile *o); -void printRebaseTable(const object::ObjectFile *o); -void printBindTable(const object::ObjectFile *o); -void printLazyBindTable(const object::ObjectFile *o); -void printWeakBindTable(const object::ObjectFile *o); +void printRebaseTable(object::ObjectFile *o); +void printBindTable(object::ObjectFile *o); +void printLazyBindTable(object::ObjectFile *o); +void printWeakBindTable(object::ObjectFile *o); void printRawClangAST(const object::ObjectFile *o); void PrintRelocations(const object::ObjectFile *o); void PrintSectionHeaders(const object::ObjectFile *o); diff --git a/tools/llvm-pdbdump/Analyze.cpp b/tools/llvm-pdbdump/Analyze.cpp new file mode 100644 index 0000000000000..b65dd40d25ff1 --- /dev/null +++ b/tools/llvm-pdbdump/Analyze.cpp @@ -0,0 +1,164 @@ +//===- 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 <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/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<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; + TypeDatabaseVisitor DBV(TypeDB); + TypeDeserializer Deserializer; + TypeVisitorCallbackPipeline Pipeline; + HashLookupVisitor Hasher(*Tpi); + // Deserialize the types + Pipeline.addCallbackToPipeline(Deserializer); + // Add them to the database + Pipeline.addCallbackToPipeline(DBV); + // Store their hash values + Pipeline.addCallbackToPipeline(Hasher); + + CVTypeVisitor Visitor(Pipeline); + + bool Error = false; + for (auto Item : Tpi->types(&Error)) { + if (auto EC = Visitor.visitTypeRecord(Item)) + return EC; + } + if (Error) + return make_error<RawError>(raw_error_code::corrupt_file, + "TPI stream contained corrupt record"); + + 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(); +} diff --git a/tools/llvm-pdbdump/Analyze.h b/tools/llvm-pdbdump/Analyze.h new file mode 100644 index 0000000000000..7230ae45b0c8c --- /dev/null +++ b/tools/llvm-pdbdump/Analyze.h @@ -0,0 +1,30 @@ +//===- Analyze.h - 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. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_ANALYSIS_H +#define LLVM_TOOLS_LLVMPDBDUMP_ANALYSIS_H + +#include "OutputStyle.h" + +namespace llvm { +namespace pdb { +class PDBFile; +class AnalysisStyle : public OutputStyle { +public: + explicit AnalysisStyle(PDBFile &File); + + Error dump() override; + +private: + PDBFile &File; +}; +} +} + +#endif diff --git a/tools/llvm-pdbdump/CMakeLists.txt b/tools/llvm-pdbdump/CMakeLists.txt index 7c46171941f7d..37c76ab697b46 100644 --- a/tools/llvm-pdbdump/CMakeLists.txt +++ b/tools/llvm-pdbdump/CMakeLists.txt @@ -7,6 +7,9 @@ set(LLVM_LINK_COMPONENTS ) add_llvm_tool(llvm-pdbdump + Analyze.cpp + Diff.cpp + CompactTypeDumpVisitor.cpp llvm-pdbdump.cpp YamlSymbolDumper.cpp YamlTypeDumper.cpp @@ -15,6 +18,8 @@ add_llvm_tool(llvm-pdbdump PdbYaml.cpp PrettyBuiltinDumper.cpp PrettyClassDefinitionDumper.cpp + PrettyClassLayoutTextDumper.cpp + PrettyClassLayoutGraphicalDumper.cpp PrettyCompilandDumper.cpp PrettyEnumDumper.cpp PrettyExternalSymbolDumper.cpp @@ -22,6 +27,7 @@ add_llvm_tool(llvm-pdbdump PrettyTypeDumper.cpp PrettyTypedefDumper.cpp PrettyVariableDumper.cpp + StreamUtil.cpp YAMLOutputStyle.cpp ) diff --git a/tools/llvm-pdbdump/CompactTypeDumpVisitor.cpp b/tools/llvm-pdbdump/CompactTypeDumpVisitor.cpp new file mode 100644 index 0000000000000..1fc8dd5d51f0a --- /dev/null +++ b/tools/llvm-pdbdump/CompactTypeDumpVisitor.cpp @@ -0,0 +1,57 @@ +//===-- CompactTypeDumpVisitor.cpp - CodeView type info dumper --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CompactTypeDumpVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeDatabase.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +static const EnumEntry<TypeLeafKind> LeafTypeNames[] = { +#define CV_TYPE(enum, val) {#enum, enum}, +#include "llvm/DebugInfo/CodeView/TypeRecords.def" +}; + +static StringRef getLeafName(TypeLeafKind K) { + for (const auto &E : LeafTypeNames) { + if (E.Value == K) + return E.Name; + } + return StringRef(); +} + +CompactTypeDumpVisitor::CompactTypeDumpVisitor(TypeDatabase &TypeDB, + ScopedPrinter *W) + : W(W), TI(TypeIndex::None()), Offset(0), TypeDB(TypeDB) {} + +Error CompactTypeDumpVisitor::visitTypeBegin(CVType &Record) { + if (TI == TypeIndex::None()) + TI.setIndex(TypeIndex::FirstNonSimpleIndex); + else + TI.setIndex(TI.getIndex() + 1); + + return Error::success(); +} + +Error CompactTypeDumpVisitor::visitTypeEnd(CVType &Record) { + uint32_t I = TI.getIndex(); + StringRef Leaf = getLeafName(Record.Type); + StringRef Name = TypeDB.getTypeName(TI); + W->printString( + llvm::formatv("Index: {0:x} ({1:N} bytes, offset {2:N}) {3} \"{4}\"", I, + Record.length(), Offset, Leaf, Name) + .str()); + + Offset += Record.length(); + + return Error::success(); +} diff --git a/tools/llvm-pdbdump/CompactTypeDumpVisitor.h b/tools/llvm-pdbdump/CompactTypeDumpVisitor.h new file mode 100644 index 0000000000000..180eea7b8d6a1 --- /dev/null +++ b/tools/llvm-pdbdump/CompactTypeDumpVisitor.h @@ -0,0 +1,47 @@ +//===-- CompactTypeDumpVisitor.h - CodeView type info dumper ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_COMPACTTYPEDUMPVISITOR_H +#define LLVM_DEBUGINFO_CODEVIEW_COMPACTTYPEDUMPVISITOR_H + +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" + +namespace llvm { +class ScopedPrinter; +namespace codeview { +class TypeDatabase; +} + +namespace pdb { + +/// Dumper for CodeView type streams found in COFF object files and PDB files. +/// Dumps records on a single line, and ignores member records. +class CompactTypeDumpVisitor : public codeview::TypeVisitorCallbacks { +public: + CompactTypeDumpVisitor(codeview::TypeDatabase &TypeDB, ScopedPrinter *W); + + /// Paired begin/end actions for all types. Receives all record data, + /// including the fixed-length record prefix. + Error visitTypeBegin(codeview::CVType &Record) override; + Error visitTypeEnd(codeview::CVType &Record) override; + +private: + ScopedPrinter *W; + + codeview::TypeIndex TI; + uint32_t Offset; + codeview::TypeDatabase &TypeDB; +}; + +} // end namespace pdb +} // end namespace llvm + +#endif diff --git a/tools/llvm-pdbdump/Diff.cpp b/tools/llvm-pdbdump/Diff.cpp new file mode 100644 index 0000000000000..8c02d36044d82 --- /dev/null +++ b/tools/llvm-pdbdump/Diff.cpp @@ -0,0 +1,523 @@ +//===- Diff.cpp - PDB diff utility ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Diff.h" + +#include "StreamUtil.h" +#include "llvm-pdbdump.h" + +#include "llvm/DebugInfo/PDB/Native/Formatters.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/StringTable.h" + +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatProviders.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::pdb; + +namespace llvm { +template <> struct format_provider<PdbRaw_FeatureSig> { + static void format(const PdbRaw_FeatureSig &Sig, raw_ostream &Stream, + StringRef Style) { + switch (Sig) { + case PdbRaw_FeatureSig::MinimalDebugInfo: + Stream << "MinimalDebugInfo"; + break; + case PdbRaw_FeatureSig::NoTypeMerge: + Stream << "NoTypeMerge"; + break; + case PdbRaw_FeatureSig::VC110: + Stream << "VC110"; + break; + case PdbRaw_FeatureSig::VC140: + Stream << "VC140"; + break; + } + } +}; +} + +template <typename R> using ValueOfRange = llvm::detail::ValueOfRange<R>; + +template <typename Range, typename Comp> +static void set_differences(Range &&R1, Range &&R2, + SmallVectorImpl<ValueOfRange<Range>> *OnlyLeft, + SmallVectorImpl<ValueOfRange<Range>> *OnlyRight, + SmallVectorImpl<ValueOfRange<Range>> *Intersection, + Comp Comparator) { + + std::sort(R1.begin(), R1.end(), Comparator); + std::sort(R2.begin(), R2.end(), Comparator); + + if (OnlyLeft) { + OnlyLeft->reserve(R1.size()); + auto End = std::set_difference(R1.begin(), R1.end(), R2.begin(), R2.end(), + OnlyLeft->begin(), Comparator); + OnlyLeft->set_size(std::distance(OnlyLeft->begin(), End)); + } + if (OnlyRight) { + OnlyLeft->reserve(R2.size()); + auto End = std::set_difference(R2.begin(), R2.end(), R1.begin(), R1.end(), + OnlyRight->begin(), Comparator); + OnlyRight->set_size(std::distance(OnlyRight->begin(), End)); + } + if (Intersection) { + Intersection->reserve(std::min(R1.size(), R2.size())); + auto End = std::set_intersection(R1.begin(), R1.end(), R2.begin(), R2.end(), + Intersection->begin(), Comparator); + Intersection->set_size(std::distance(Intersection->begin(), End)); + } +} + +template <typename Range> +static void +set_differences(Range &&R1, Range &&R2, + SmallVectorImpl<ValueOfRange<Range>> *OnlyLeft, + SmallVectorImpl<ValueOfRange<Range>> *OnlyRight, + SmallVectorImpl<ValueOfRange<Range>> *Intersection = nullptr) { + std::less<ValueOfRange<Range>> Comp; + set_differences(std::forward<Range>(R1), std::forward<Range>(R2), OnlyLeft, + OnlyRight, Intersection, Comp); +} + +DiffStyle::DiffStyle(PDBFile &File1, PDBFile &File2) + : File1(File1), File2(File2) {} + +Error DiffStyle::dump() { + if (auto EC = diffSuperBlock()) + return EC; + + if (auto EC = diffFreePageMap()) + return EC; + + if (auto EC = diffStreamDirectory()) + return EC; + + if (auto EC = diffStringTable()) + return EC; + + if (auto EC = diffInfoStream()) + return EC; + + if (auto EC = diffDbiStream()) + return EC; + + if (auto EC = diffSectionContribs()) + return EC; + + if (auto EC = diffSectionMap()) + return EC; + + if (auto EC = diffFpoStream()) + return EC; + + if (auto EC = diffTpiStream(StreamTPI)) + return EC; + + if (auto EC = diffTpiStream(StreamIPI)) + return EC; + + if (auto EC = diffPublics()) + return EC; + + if (auto EC = diffGlobals()) + return EC; + + return Error::success(); +} + +template <typename T> +static bool diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2, T V1, + T V2) { + if (V1 == V2) { + outs() << formatv(" {0}: No differences detected!\n", Label); + return false; + } + + outs().indent(2) << Label << "\n"; + outs().indent(4) << formatv("{0}: {1}\n", File1.getFilePath(), V1); + outs().indent(4) << formatv("{0}: {1}\n", File2.getFilePath(), V2); + return true; +} + +template <typename T> +static bool diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2, + ArrayRef<T> V1, ArrayRef<T> V2) { + if (V1 == V2) { + outs() << formatv(" {0}: No differences detected!\n", Label); + return false; + } + + outs().indent(2) << Label << "\n"; + outs().indent(4) << formatv("{0}: {1}\n", File1.getFilePath(), + make_range(V1.begin(), V1.end())); + outs().indent(4) << formatv("{0}: {1}\n", File2.getFilePath(), + make_range(V2.begin(), V2.end())); + return true; +} + +template <typename T> +static bool printSymmetricDifferences(PDBFile &File1, PDBFile &File2, + T &&OnlyRange1, T &&OnlyRange2, + StringRef Label) { + bool HasDiff = false; + if (!OnlyRange1.empty()) { + HasDiff = true; + outs() << formatv(" {0} {1}(s) only in ({2})\n", OnlyRange1.size(), Label, + File1.getFilePath()); + for (const auto &Item : OnlyRange1) + outs() << formatv(" {0}\n", Label, Item); + } + if (!OnlyRange2.empty()) { + HasDiff = true; + outs() << formatv(" {0} {1}(s) only in ({2})\n", OnlyRange2.size(), + File2.getFilePath()); + for (const auto &Item : OnlyRange2) + outs() << formatv(" {0}\n", Item); + } + return HasDiff; +} + +Error DiffStyle::diffSuperBlock() { + outs() << "MSF Super Block: Searching for differences...\n"; + bool Diffs = false; + + Diffs |= diffAndPrint("Block Size", File1, File2, File1.getBlockSize(), + File2.getBlockSize()); + Diffs |= diffAndPrint("Block Count", File1, File2, File1.getBlockCount(), + File2.getBlockCount()); + Diffs |= diffAndPrint("Unknown 1", File1, File2, File1.getUnknown1(), + File2.getUnknown1()); + + if (opts::diff::Pedantic) { + Diffs |= diffAndPrint("Free Block Map", File1, File2, + File1.getFreeBlockMapBlock(), + File2.getFreeBlockMapBlock()); + Diffs |= diffAndPrint("Directory Size", File1, File2, + File1.getNumDirectoryBytes(), + File2.getNumDirectoryBytes()); + Diffs |= diffAndPrint("Block Map Addr", File1, File2, + File1.getBlockMapOffset(), File2.getBlockMapOffset()); + } + if (!Diffs) + outs() << "MSF Super Block: No differences detected...\n"; + return Error::success(); +} + +Error DiffStyle::diffStreamDirectory() { + SmallVector<std::string, 32> P; + SmallVector<std::string, 32> Q; + discoverStreamPurposes(File1, P); + discoverStreamPurposes(File2, Q); + outs() << "Stream Directory: Searching for differences...\n"; + + bool HasDifferences = false; + if (opts::diff::Pedantic) { + size_t Min = std::min(P.size(), Q.size()); + for (size_t I = 0; I < Min; ++I) { + StringRef Names[] = {P[I], Q[I]}; + uint32_t Sizes[] = {File1.getStreamByteSize(I), + File2.getStreamByteSize(I)}; + bool NamesDiffer = Names[0] != Names[1]; + bool SizesDiffer = Sizes[0] != Sizes[1]; + if (NamesDiffer) { + HasDifferences = true; + outs().indent(2) << formatv("Stream {0} - {1}: {2}, {3}: {4}\n", I, + File1.getFilePath(), Names[0], + File2.getFilePath(), Names[1]); + continue; + } + if (SizesDiffer) { + HasDifferences = true; + outs().indent(2) << formatv( + "Stream {0} ({1}): {2}: {3} bytes, {4}: {5} bytes\n", I, Names[0], + File1.getFilePath(), Sizes[0], File2.getFilePath(), Sizes[1]); + continue; + } + } + + ArrayRef<std::string> MaxNames = (P.size() > Q.size() ? P : Q); + size_t Max = std::max(P.size(), Q.size()); + PDBFile &MaxFile = (P.size() > Q.size() ? File1 : File2); + StringRef MinFileName = + (P.size() < Q.size() ? File1.getFilePath() : File2.getFilePath()); + for (size_t I = Min; I < Max; ++I) { + HasDifferences = true; + StringRef StreamName = MaxNames[I]; + + outs().indent(2) << formatv( + "Stream {0} - {1}: <not present>, {2}: Index {3}, {4} bytes\n", + StreamName, MinFileName, MaxFile.getFilePath(), I, + MaxFile.getStreamByteSize(I)); + } + if (!HasDifferences) + outs() << "Stream Directory: No differences detected...\n"; + } else { + auto PI = to_vector<32>(enumerate(P)); + auto QI = to_vector<32>(enumerate(Q)); + + typedef decltype(PI) ContainerType; + typedef typename ContainerType::value_type value_type; + + auto Comparator = [](const value_type &I1, const value_type &I2) { + return I1.value() < I2.value(); + }; + + decltype(PI) OnlyP; + decltype(QI) OnlyQ; + decltype(PI) Common; + + set_differences(PI, QI, &OnlyP, &OnlyQ, &Common, Comparator); + + if (!OnlyP.empty()) { + HasDifferences = true; + outs().indent(2) << formatv("{0} Stream(s) only in ({1})\n", OnlyP.size(), + File1.getFilePath()); + for (auto &Item : OnlyP) { + outs().indent(4) << formatv("Stream {0} - {1}\n", Item.index(), + Item.value()); + } + } + + if (!OnlyQ.empty()) { + HasDifferences = true; + outs().indent(2) << formatv("{0} Streams(s) only in ({1})\n", + OnlyQ.size(), File2.getFilePath()); + for (auto &Item : OnlyQ) { + outs().indent(4) << formatv("Stream {0} - {1}\n", Item.index(), + Item.value()); + } + } + if (!Common.empty()) { + outs().indent(2) << formatv("Found {0} common streams. Searching for " + "intra-stream differences.\n", + Common.size()); + bool HasCommonDifferences = false; + for (const auto &Left : Common) { + // Left was copied from the first range so its index refers to a stream + // index in the first file. Find the corresponding stream index in the + // second file. + auto Range = + std::equal_range(QI.begin(), QI.end(), Left, + [](const value_type &L, const value_type &R) { + return L.value() < R.value(); + }); + const auto &Right = *Range.first; + assert(Left.value() == Right.value()); + uint32_t LeftSize = File1.getStreamByteSize(Left.index()); + uint32_t RightSize = File2.getStreamByteSize(Right.index()); + if (LeftSize != RightSize) { + HasDifferences = true; + HasCommonDifferences = true; + outs().indent(4) << formatv("{0} ({1}: {2} bytes, {3}: {4} bytes)\n", + Left.value(), File1.getFilePath(), + LeftSize, File2.getFilePath(), RightSize); + } + } + if (!HasCommonDifferences) + outs().indent(2) << "Common Streams: No differences detected!\n"; + } + if (!HasDifferences) + outs() << "Stream Directory: No differences detected!\n"; + } + + return Error::success(); +} + +Error DiffStyle::diffStringTable() { + auto ExpectedST1 = File1.getStringTable(); + auto ExpectedST2 = File2.getStringTable(); + outs() << "String Table: Searching for differences...\n"; + bool Has1 = !!ExpectedST1; + bool Has2 = !!ExpectedST2; + if (!(Has1 && Has2)) { + // If one has a string table and the other doesn't, we can print less + // output. + if (Has1 != Has2) { + if (Has1) { + outs() << formatv(" {0}: ({1} strings)\n", File1.getFilePath(), + ExpectedST1->getNameCount()); + outs() << formatv(" {0}: (string table not present)\n", + File2.getFilePath()); + } else { + outs() << formatv(" {0}: (string table not present)\n", + File1.getFilePath()); + outs() << formatv(" {0}: ({1})\n", File2.getFilePath(), + ExpectedST2->getNameCount()); + } + } + consumeError(ExpectedST1.takeError()); + consumeError(ExpectedST2.takeError()); + return Error::success(); + } + + bool HasDiff = false; + auto &ST1 = *ExpectedST1; + auto &ST2 = *ExpectedST2; + + if (ST1.getByteSize() != ST2.getByteSize()) { + outs() << " Stream Size\n"; + outs() << formatv(" {0} - {1} byte(s)\n", File1.getFilePath(), + ST1.getByteSize()); + outs() << formatv(" {0} - {1} byte(s)\n", File2.getFilePath(), + ST2.getByteSize()); + outs() << formatv(" Difference: {0} bytes\n", + AbsoluteDifference(ST1.getByteSize(), ST2.getByteSize())); + HasDiff = true; + } + HasDiff |= diffAndPrint("Hash Version", File1, File2, ST1.getHashVersion(), + ST1.getHashVersion()); + HasDiff |= diffAndPrint("Signature", File1, File2, ST1.getSignature(), + ST1.getSignature()); + + // Both have a valid string table, dive in and compare individual strings. + + auto IdList1 = ST1.name_ids(); + auto IdList2 = ST2.name_ids(); + if (opts::diff::Pedantic) { + // In pedantic mode, we compare index by index (i.e. the strings are in the + // same order + // in both tables. + uint32_t Max = std::max(IdList1.size(), IdList2.size()); + for (uint32_t I = 0; I < Max; ++I) { + Optional<uint32_t> Id1, Id2; + StringRef S1, S2; + if (I < IdList1.size()) { + Id1 = IdList1[I]; + S1 = ST1.getStringForID(*Id1); + } + if (I < IdList2.size()) { + Id2 = IdList2[I]; + S2 = ST2.getStringForID(*Id2); + } + if (Id1 == Id2 && S1 == S2) + continue; + + std::string OutId1 = + Id1 ? formatv("{0}", *Id1).str() : "(index not present)"; + std::string OutId2 = + Id2 ? formatv("{0}", *Id2).str() : "(index not present)"; + outs() << formatv(" String {0}\n", I); + outs() << formatv(" {0}: Hash - {1}, Value - {2}\n", + File1.getFilePath(), OutId1, S1); + outs() << formatv(" {0}: Hash - {1}, Value - {2}\n", + File2.getFilePath(), OutId2, S2); + HasDiff = true; + } + } else { + std::vector<StringRef> Strings1, Strings2; + Strings1.reserve(IdList1.size()); + Strings2.reserve(IdList2.size()); + for (auto ID : IdList1) + Strings1.push_back(ST1.getStringForID(ID)); + for (auto ID : IdList2) + Strings2.push_back(ST2.getStringForID(ID)); + + SmallVector<StringRef, 64> OnlyP; + SmallVector<StringRef, 64> OnlyQ; + auto End1 = std::remove(Strings1.begin(), Strings1.end(), ""); + auto End2 = std::remove(Strings2.begin(), Strings2.end(), ""); + uint32_t Empty1 = std::distance(End1, Strings1.end()); + uint32_t Empty2 = std::distance(End2, Strings2.end()); + Strings1.erase(End1, Strings1.end()); + Strings2.erase(End2, Strings2.end()); + set_differences(Strings1, Strings2, &OnlyP, &OnlyQ); + printSymmetricDifferences(File1, File2, OnlyP, OnlyQ, "String"); + + if (Empty1 != Empty2) { + PDBFile &MoreF = (Empty1 > Empty2) ? File1 : File2; + PDBFile &LessF = (Empty1 < Empty2) ? File1 : File2; + uint32_t Difference = AbsoluteDifference(Empty1, Empty2); + outs() << formatv(" {0} had {1} more empty strings than {2}\n", + MoreF.getFilePath(), Difference, LessF.getFilePath()); + } + } + if (!HasDiff) + outs() << "String Table: No differences detected!\n"; + return Error::success(); +} + +Error DiffStyle::diffFreePageMap() { return Error::success(); } + +Error DiffStyle::diffInfoStream() { + auto ExpectedInfo1 = File1.getPDBInfoStream(); + auto ExpectedInfo2 = File2.getPDBInfoStream(); + + outs() << "PDB Stream: Searching for differences...\n"; + bool Has1 = !!ExpectedInfo1; + bool Has2 = !!ExpectedInfo2; + if (!(Has1 && Has2)) { + if (Has1 != Has2) + outs() << formatv("{0} does not have a PDB Stream!\n", + Has1 ? File1.getFilePath() : File2.getFilePath()); + consumeError(ExpectedInfo2.takeError()); + consumeError(ExpectedInfo2.takeError()); + return Error::success(); + } + + bool HasDiff = false; + auto &IS1 = *ExpectedInfo1; + auto &IS2 = *ExpectedInfo2; + if (IS1.getStreamSize() != IS2.getStreamSize()) { + outs() << " Stream Size\n"; + outs() << formatv(" {0} - {1} byte(s)\n", File1.getFilePath(), + IS1.getStreamSize()); + outs() << formatv(" {0} - {1} byte(s)\n", File2.getFilePath(), + IS2.getStreamSize()); + outs() << formatv( + " Difference: {0} bytes\n", + AbsoluteDifference(IS1.getStreamSize(), IS2.getStreamSize())); + HasDiff = true; + } + HasDiff |= diffAndPrint("Age", File1, File2, IS1.getAge(), IS2.getAge()); + HasDiff |= diffAndPrint("Guid", File1, File2, IS1.getGuid(), IS2.getGuid()); + HasDiff |= diffAndPrint("Signature", File1, File2, IS1.getSignature(), + IS2.getSignature()); + HasDiff |= + diffAndPrint("Version", File1, File2, IS1.getVersion(), IS2.getVersion()); + HasDiff |= diffAndPrint("Features", File1, File2, IS1.getFeatureSignatures(), + IS2.getFeatureSignatures()); + HasDiff |= diffAndPrint("Named Stream Byte Size", File1, File2, + IS1.getNamedStreamMapByteSize(), + IS2.getNamedStreamMapByteSize()); + SmallVector<StringRef, 4> NS1; + SmallVector<StringRef, 4> NS2; + for (const auto &X : IS1.getNamedStreams().entries()) + NS1.push_back(X.getKey()); + for (const auto &X : IS2.getNamedStreams().entries()) + NS2.push_back(X.getKey()); + SmallVector<StringRef, 4> OnlyP; + SmallVector<StringRef, 4> OnlyQ; + set_differences(NS1, NS2, &OnlyP, &OnlyQ); + printSymmetricDifferences(File1, File2, OnlyP, OnlyQ, "Named Streams"); + if (!HasDiff) + outs() << "PDB Stream: No differences detected!\n"; + + return Error::success(); +} + +Error DiffStyle::diffDbiStream() { return Error::success(); } + +Error DiffStyle::diffSectionContribs() { return Error::success(); } + +Error DiffStyle::diffSectionMap() { return Error::success(); } + +Error DiffStyle::diffFpoStream() { return Error::success(); } + +Error DiffStyle::diffTpiStream(int Index) { return Error::success(); } + +Error DiffStyle::diffModuleInfoStream(int Index) { return Error::success(); } + +Error DiffStyle::diffPublics() { return Error::success(); } + +Error DiffStyle::diffGlobals() { return Error::success(); } diff --git a/tools/llvm-pdbdump/Diff.h b/tools/llvm-pdbdump/Diff.h new file mode 100644 index 0000000000000..6037576e21bb6 --- /dev/null +++ b/tools/llvm-pdbdump/Diff.h @@ -0,0 +1,45 @@ +//===- Diff.h - PDB diff utility --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_DIFF_H +#define LLVM_TOOLS_LLVMPDBDUMP_DIFF_H + +#include "OutputStyle.h" + +namespace llvm { +namespace pdb { +class PDBFile; +class DiffStyle : public OutputStyle { +public: + explicit DiffStyle(PDBFile &File1, PDBFile &File2); + + Error dump() override; + +private: + Error diffSuperBlock(); + Error diffStreamDirectory(); + Error diffStringTable(); + Error diffFreePageMap(); + Error diffInfoStream(); + Error diffDbiStream(); + Error diffSectionContribs(); + Error diffSectionMap(); + Error diffFpoStream(); + Error diffTpiStream(int Index); + Error diffModuleInfoStream(int Index); + Error diffPublics(); + Error diffGlobals(); + + PDBFile &File1; + PDBFile &File2; +}; +} +} + +#endif diff --git a/tools/llvm-pdbdump/LLVMOutputStyle.cpp b/tools/llvm-pdbdump/LLVMOutputStyle.cpp index 629ba40b113c5..8348751703f14 100644 --- a/tools/llvm-pdbdump/LLVMOutputStyle.cpp +++ b/tools/llvm-pdbdump/LLVMOutputStyle.cpp @@ -9,7 +9,10 @@ #include "LLVMOutputStyle.h" +#include "CompactTypeDumpVisitor.h" +#include "StreamUtil.h" #include "llvm-pdbdump.h" + #include "llvm/DebugInfo/CodeView/CVTypeDumper.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" #include "llvm/DebugInfo/CodeView/EnumTables.h" @@ -20,20 +23,21 @@ #include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" -#include "llvm/DebugInfo/MSF/StreamReader.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/EnumTables.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/ModInfo.h" +#include "llvm/DebugInfo/PDB/Native/ModStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/DebugInfo/PDB/PDBExtras.h" -#include "llvm/DebugInfo/PDB/Raw/DbiStream.h" -#include "llvm/DebugInfo/PDB/Raw/EnumTables.h" -#include "llvm/DebugInfo/PDB/Raw/GlobalsStream.h" -#include "llvm/DebugInfo/PDB/Raw/ISectionContribVisitor.h" -#include "llvm/DebugInfo/PDB/Raw/InfoStream.h" -#include "llvm/DebugInfo/PDB/Raw/ModInfo.h" -#include "llvm/DebugInfo/PDB/Raw/ModStream.h" -#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" -#include "llvm/DebugInfo/PDB/Raw/PublicsStream.h" -#include "llvm/DebugInfo/PDB/Raw/RawError.h" -#include "llvm/DebugInfo/PDB/Raw/TpiStream.h" #include "llvm/Object/COFF.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/FormatVariadic.h" #include <unordered_map> @@ -110,6 +114,9 @@ Error LLVMOutputStyle::dump() { if (auto EC = dumpStreamBytes()) return EC; + if (auto EC = dumpStringTable()) + return EC; + if (auto EC = dumpInfoStream()) return EC; @@ -166,124 +173,12 @@ Error LLVMOutputStyle::dumpFileHeaders() { return Error::success(); } -void LLVMOutputStyle::discoverStreamPurposes() { - if (!StreamPurposes.empty()) - return; - - // It's OK if we fail to load some of these streams, we still attempt to print - // what we can. - auto Dbi = File.getPDBDbiStream(); - auto Tpi = File.getPDBTpiStream(); - auto Ipi = File.getPDBIpiStream(); - auto Info = File.getPDBInfoStream(); - - uint32_t StreamCount = File.getNumStreams(); - std::unordered_map<uint16_t, const ModuleInfoEx *> ModStreams; - std::unordered_map<uint16_t, std::string> NamedStreams; - - if (Dbi) { - for (auto &ModI : Dbi->modules()) { - uint16_t SN = ModI.Info.getModuleStreamIndex(); - ModStreams[SN] = &ModI; - } - } - if (Info) { - for (auto &NSE : Info->named_streams()) { - NamedStreams[NSE.second] = NSE.first(); - } - } - - StreamPurposes.resize(StreamCount); - for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { - std::string Value; - if (StreamIdx == OldMSFDirectory) - Value = "Old MSF Directory"; - else if (StreamIdx == StreamPDB) - Value = "PDB Stream"; - else if (StreamIdx == StreamDBI) - Value = "DBI Stream"; - else if (StreamIdx == StreamTPI) - Value = "TPI Stream"; - else if (StreamIdx == StreamIPI) - Value = "IPI Stream"; - else if (Dbi && StreamIdx == Dbi->getGlobalSymbolStreamIndex()) - Value = "Global Symbol Hash"; - else if (Dbi && StreamIdx == Dbi->getPublicSymbolStreamIndex()) - Value = "Public Symbol Hash"; - else if (Dbi && StreamIdx == Dbi->getSymRecordStreamIndex()) - Value = "Public Symbol Records"; - else if (Tpi && StreamIdx == Tpi->getTypeHashStreamIndex()) - Value = "TPI Hash"; - else if (Tpi && StreamIdx == Tpi->getTypeHashStreamAuxIndex()) - Value = "TPI Aux Hash"; - else if (Ipi && StreamIdx == Ipi->getTypeHashStreamIndex()) - Value = "IPI Hash"; - else if (Ipi && StreamIdx == Ipi->getTypeHashStreamAuxIndex()) - Value = "IPI Aux Hash"; - else if (Dbi && - StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Exception)) - Value = "Exception Data"; - else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Fixup)) - Value = "Fixup Data"; - else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::FPO)) - Value = "FPO Data"; - else if (Dbi && - StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::NewFPO)) - Value = "New FPO Data"; - else if (Dbi && - StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapFromSrc)) - Value = "Omap From Source Data"; - else if (Dbi && - StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapToSrc)) - Value = "Omap To Source Data"; - else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Pdata)) - Value = "Pdata"; - else if (Dbi && - StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdr)) - Value = "Section Header Data"; - else if (Dbi && - StreamIdx == - Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdrOrig)) - Value = "Section Header Original Data"; - else if (Dbi && - StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::TokenRidMap)) - Value = "Token Rid Data"; - else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Xdata)) - Value = "Xdata"; - else { - auto ModIter = ModStreams.find(StreamIdx); - auto NSIter = NamedStreams.find(StreamIdx); - if (ModIter != ModStreams.end()) { - Value = "Module \""; - Value += ModIter->second->Info.getModuleName().str(); - Value += "\""; - } else if (NSIter != NamedStreams.end()) { - Value = "Named Stream \""; - Value += NSIter->second; - Value += "\""; - } else { - Value = "???"; - } - } - StreamPurposes[StreamIdx] = Value; - } - - // Consume errors from missing streams. - if (!Dbi) - consumeError(Dbi.takeError()); - if (!Tpi) - consumeError(Tpi.takeError()); - if (!Ipi) - consumeError(Ipi.takeError()); - if (!Info) - consumeError(Info.takeError()); -} - Error LLVMOutputStyle::dumpStreamSummary() { if (!opts::raw::DumpStreamSummary) return Error::success(); - discoverStreamPurposes(); + if (StreamPurposes.empty()) + discoverStreamPurposes(File, StreamPurposes); uint32_t StreamCount = File.getNumStreams(); @@ -425,7 +320,8 @@ Error LLVMOutputStyle::dumpStreamBytes() { if (opts::raw::DumpStreamData.empty()) return Error::success(); - discoverStreamPurposes(); + if (StreamPurposes.empty()) + discoverStreamPurposes(File, StreamPurposes); DictScope D(P, "Stream Data"); for (uint32_t SI : opts::raw::DumpStreamData) { @@ -444,7 +340,7 @@ Error LLVMOutputStyle::dumpStreamBytes() { auto Blocks = File.getMsfLayout().StreamMap[SI]; P.printList("Blocks", Blocks); - StreamReader R(*S); + BinaryStreamReader R(*S); ArrayRef<uint8_t> StreamData; if (auto EC = R.readBytes(StreamData, S->getLength())) return EC; @@ -453,6 +349,28 @@ Error LLVMOutputStyle::dumpStreamBytes() { return Error::success(); } +Error LLVMOutputStyle::dumpStringTable() { + if (!opts::raw::DumpStringTable) + return Error::success(); + + auto IS = File.getStringTable(); + if (!IS) + return IS.takeError(); + + DictScope D(P, "String Table"); + for (uint32_t I : IS->name_ids()) { + StringRef S = IS->getStringForID(I); + if (!S.empty()) { + llvm::SmallString<32> Str; + Str.append("'"); + Str.append(S); + Str.append("'"); + P.printString(Str); + } + } + return Error::success(); +} + Error LLVMOutputStyle::dumpInfoStream() { if (!opts::raw::DumpHeaders) return Error::success(); @@ -469,25 +387,28 @@ Error LLVMOutputStyle::dumpInfoStream() { P.printHex("Signature", IS->getSignature()); P.printNumber("Age", IS->getAge()); P.printObject("Guid", IS->getGuid()); + P.printHex("Features", IS->getFeatures()); + { + DictScope DD(P, "Named Streams"); + for (const auto &S : IS->getNamedStreams().entries()) + P.printObject(S.getKey(), S.getValue()); + } return Error::success(); } -static void printTypeIndexOffset(raw_ostream &OS, - const TypeIndexOffset &TIOff) { - OS << "{" << TIOff.Type.getIndex() << ", " << TIOff.Offset << "}"; -} +namespace { +class RecordBytesVisitor : public TypeVisitorCallbacks { +public: + explicit RecordBytesVisitor(ScopedPrinter &P) : P(P) {} -static void dumpTpiHash(ScopedPrinter &P, TpiStream &Tpi) { - if (!opts::raw::DumpTpiHash) - return; - DictScope DD(P, "Hash"); - P.printNumber("Number of Hash Buckets", Tpi.NumHashBuckets()); - P.printNumber("Hash Key Size", Tpi.getHashKeySize()); - P.printList("Values", Tpi.getHashValues()); - P.printList("Type Index Offsets", Tpi.getTypeIndexOffsets(), - printTypeIndexOffset); - P.printList("Hash Adjustments", Tpi.getHashAdjustments(), - printTypeIndexOffset); + Error visitTypeEnd(CVType &Record) override { + P.printBinaryBlock("Bytes", Record.content()); + return Error::success(); + } + +private: + ScopedPrinter &P; +}; } Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) { @@ -495,6 +416,7 @@ Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) { bool DumpRecordBytes = false; bool DumpRecords = false; + bool DumpTpiHash = false; StringRef Label; StringRef VerLabel; if (StreamIdx == StreamTPI) { @@ -504,6 +426,7 @@ Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) { } DumpRecordBytes = opts::raw::DumpTpiRecordBytes; DumpRecords = opts::raw::DumpTpiRecords; + DumpTpiHash = opts::raw::DumpTpiHash; Label = "Type Info Stream (TPI)"; VerLabel = "TPI Version"; } else if (StreamIdx == StreamIPI) { @@ -516,64 +439,102 @@ Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) { Label = "Type Info Stream (IPI)"; VerLabel = "IPI Version"; } - if (!DumpRecordBytes && !DumpRecords && !opts::raw::DumpModuleSyms) + if (!DumpRecordBytes && !DumpRecords && !DumpTpiHash && + !opts::raw::DumpModuleSyms) return Error::success(); + bool IsSilentDatabaseBuild = !DumpRecordBytes && !DumpRecords && !DumpTpiHash; + auto Tpi = (StreamIdx == StreamTPI) ? File.getPDBTpiStream() : File.getPDBIpiStream(); if (!Tpi) return Tpi.takeError(); - CVTypeDumper Dumper(TypeDB); - if (DumpRecords || DumpRecordBytes) { - DictScope D(P, Label); + std::unique_ptr<DictScope> StreamScope; + std::unique_ptr<ListScope> RecordScope; + if (!IsSilentDatabaseBuild) { + StreamScope = llvm::make_unique<DictScope>(P, Label); P.printNumber(VerLabel, Tpi->getTpiVersion()); P.printNumber("Record count", Tpi->NumTypeRecords()); + } - ListScope L(P, "Records"); + TypeDatabase &StreamDB = (StreamIdx == StreamTPI) ? TypeDB : ItemDB; + + TypeDatabaseVisitor DBV(StreamDB); + CompactTypeDumpVisitor CTDV(StreamDB, &P); + TypeDumpVisitor TDV(TypeDB, &P, false); + if (StreamIdx == StreamIPI) + TDV.setItemDB(ItemDB); + RecordBytesVisitor RBV(P); + TypeDeserializer Deserializer; + + // We always need to deserialize and add it to the type database. This is + // true if even if we're not dumping anything, because we could need the + // type database for the purposes of dumping symbols. + TypeVisitorCallbackPipeline Pipeline; + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(DBV); + + // If we're in dump mode, add a dumper with the appropriate detail level. + if (DumpRecords) { + if (opts::raw::CompactRecords) + Pipeline.addCallbackToPipeline(CTDV); + else + Pipeline.addCallbackToPipeline(TDV); + } + if (DumpRecordBytes) + Pipeline.addCallbackToPipeline(RBV); - bool HadError = false; - for (auto &Type : Tpi->types(&HadError)) { - DictScope DD(P, ""); + CVTypeVisitor Visitor(Pipeline); - if (DumpRecords) { - TypeDumpVisitor TDV(TypeDB, &P, false); - if (auto EC = Dumper.dump(Type, TDV)) - return EC; - } + if (DumpRecords || DumpRecordBytes) + RecordScope = llvm::make_unique<ListScope>(P, "Records"); - if (DumpRecordBytes) - P.printBinaryBlock("Bytes", Type.content()); - } - dumpTpiHash(P, *Tpi); - if (HadError) - return make_error<RawError>(raw_error_code::corrupt_file, - "TPI stream contained corrupt record"); - } else if (opts::raw::DumpModuleSyms) { - // Even if the user doesn't want to dump type records, we still need to - // iterate them in order to build the type database. So when they want to - // dump symbols but not types, don't stick a dumper on the end, just build - // the type database. - TypeDatabaseVisitor DBV(TypeDB); - TypeDeserializer Deserializer; - TypeVisitorCallbackPipeline Pipeline; - Pipeline.addCallbackToPipeline(Deserializer); - Pipeline.addCallbackToPipeline(DBV); - - CVTypeVisitor Visitor(Pipeline); - - bool HadError = false; - for (auto Type : Tpi->types(&HadError)) { - if (auto EC = Visitor.visitTypeRecord(Type)) - return EC; + bool HadError = false; + + TypeIndex T(TypeIndex::FirstNonSimpleIndex); + for (auto Type : Tpi->types(&HadError)) { + std::unique_ptr<DictScope> OneRecordScope; + + if ((DumpRecords || DumpRecordBytes) && !opts::raw::CompactRecords) + OneRecordScope = llvm::make_unique<DictScope>(P, ""); + + if (auto EC = Visitor.visitTypeRecord(Type)) + return EC; + } + if (HadError) + return make_error<RawError>(raw_error_code::corrupt_file, + "TPI stream contained corrupt record"); + + if (DumpTpiHash) { + DictScope DD(P, "Hash"); + P.printNumber("Number of Hash Buckets", Tpi->NumHashBuckets()); + P.printNumber("Hash Key Size", Tpi->getHashKeySize()); + P.printList("Values", Tpi->getHashValues()); + + ListScope LHA(P, "Adjusters"); + auto ExpectedST = File.getStringTable(); + if (!ExpectedST) + return ExpectedST.takeError(); + const auto &ST = *ExpectedST; + for (const auto &E : Tpi->getHashAdjusters()) { + DictScope DHA(P); + StringRef Name = ST.getStringForID(E.first); + P.printString("Type", Name); + P.printHex("TI", E.second); } + } - dumpTpiHash(P, *Tpi); - if (HadError) - return make_error<RawError>(raw_error_code::corrupt_file, - "TPI stream contained corrupt record"); + if (!IsSilentDatabaseBuild) { + ListScope L(P, "TypeIndexOffsets"); + for (const auto &IO : Tpi->getTypeIndexOffsets()) { + P.printString(formatv("Index: {0:x}, Offset: {1:N}", IO.Type.getIndex(), + (uint32_t)IO.Offset) + .str()); + } } + P.flush(); return Error::success(); } @@ -679,10 +640,10 @@ Error LLVMOutputStyle::dumpDbiStream() { public: RecordVisitor(ScopedPrinter &P, PDBFile &F) : P(P), F(F) {} Error visitUnknown(ModuleSubstreamKind Kind, - ReadableStreamRef Stream) override { + BinaryStreamRef Stream) override { DictScope DD(P, "Unknown"); ArrayRef<uint8_t> Data; - StreamReader R(Stream); + BinaryStreamReader R(Stream); if (auto EC = R.readBytes(Data, R.bytesRemaining())) { return make_error<RawError>( raw_error_code::corrupt_file, @@ -692,7 +653,7 @@ Error LLVMOutputStyle::dumpDbiStream() { return Error::success(); } Error - visitFileChecksums(ReadableStreamRef Data, + visitFileChecksums(BinaryStreamRef Data, const FileChecksumArray &Checksums) override { DictScope DD(P, "FileChecksums"); for (const auto &C : Checksums) { @@ -708,7 +669,7 @@ Error LLVMOutputStyle::dumpDbiStream() { return Error::success(); } - Error visitLines(ReadableStreamRef Data, + Error visitLines(BinaryStreamRef Data, const LineSubstreamHeader *Header, const LineInfoArray &Lines) override { DictScope DD(P, "Lines"); diff --git a/tools/llvm-pdbdump/LLVMOutputStyle.h b/tools/llvm-pdbdump/LLVMOutputStyle.h index 816d591f08f83..bfff3b8308db9 100644 --- a/tools/llvm-pdbdump/LLVMOutputStyle.h +++ b/tools/llvm-pdbdump/LLVMOutputStyle.h @@ -12,9 +12,12 @@ #include "OutputStyle.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/DebugInfo/CodeView/TypeDatabase.h" #include "llvm/Support/ScopedPrinter.h" +#include <string> + namespace llvm { class BitVector; namespace pdb { @@ -25,8 +28,6 @@ public: Error dump() override; private: - void discoverStreamPurposes(); - Error dumpFileHeaders(); Error dumpStreamSummary(); Error dumpFreePageMap(); @@ -34,6 +35,7 @@ private: Error dumpGlobalsStream(); Error dumpStreamBytes(); Error dumpStreamBlocks(); + Error dumpStringTable(); Error dumpInfoStream(); Error dumpTpiStream(uint32_t StreamIdx); Error dumpDbiStream(); @@ -50,7 +52,8 @@ private: PDBFile &File; ScopedPrinter P; codeview::TypeDatabase TypeDB; - std::vector<std::string> StreamPurposes; + codeview::TypeDatabase ItemDB; + SmallVector<std::string, 32> StreamPurposes; }; } } diff --git a/tools/llvm-pdbdump/LinePrinter.cpp b/tools/llvm-pdbdump/LinePrinter.cpp index 47c7d3e3c0e74..d4a5a8d859e5c 100644 --- a/tools/llvm-pdbdump/LinePrinter.cpp +++ b/tools/llvm-pdbdump/LinePrinter.cpp @@ -12,6 +12,7 @@ #include "llvm-pdbdump.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/PDB/UDTLayout.h" #include "llvm/Support/Regex.h" #include <algorithm> @@ -42,8 +43,8 @@ bool IsItemExcluded(llvm::StringRef Item, using namespace llvm; -LinePrinter::LinePrinter(int Indent, llvm::raw_ostream &Stream) - : OS(Stream), IndentSpaces(Indent), CurrentIndent(0) { +LinePrinter::LinePrinter(int Indent, bool UseColor, llvm::raw_ostream &Stream) + : OS(Stream), IndentSpaces(Indent), CurrentIndent(0), UseColor(UseColor) { SetFilters(ExcludeTypeFilters, opts::pretty::ExcludeTypes.begin(), opts::pretty::ExcludeTypes.end()); SetFilters(ExcludeSymbolFilters, opts::pretty::ExcludeSymbols.begin(), @@ -70,8 +71,20 @@ void LinePrinter::NewLine() { OS.indent(CurrentIndent); } -bool LinePrinter::IsTypeExcluded(llvm::StringRef TypeName) { - return IsItemExcluded(TypeName, IncludeTypeFilters, ExcludeTypeFilters); +bool LinePrinter::IsClassExcluded(const ClassLayout &Class) { + if (IsTypeExcluded(Class.getUDTName(), Class.getClassSize())) + return true; + if (Class.deepPaddingSize() < opts::pretty::PaddingThreshold) + return true; + return false; +} + +bool LinePrinter::IsTypeExcluded(llvm::StringRef TypeName, uint32_t Size) { + if (IsItemExcluded(TypeName, IncludeTypeFilters, ExcludeTypeFilters)) + return true; + if (Size < opts::pretty::SizeThreshold) + return true; + return false; } bool LinePrinter::IsSymbolExcluded(llvm::StringRef SymbolName) { @@ -83,17 +96,25 @@ bool LinePrinter::IsCompilandExcluded(llvm::StringRef CompilandName) { ExcludeCompilandFilters); } -WithColor::WithColor(LinePrinter &P, PDB_ColorItem C) : OS(P.OS) { - applyColor(C); +WithColor::WithColor(LinePrinter &P, PDB_ColorItem C) + : OS(P.OS), UseColor(P.hasColor()) { + if (UseColor) + applyColor(C); } -WithColor::~WithColor() { OS.resetColor(); } +WithColor::~WithColor() { + if (UseColor) + OS.resetColor(); +} void WithColor::applyColor(PDB_ColorItem C) { switch (C) { case PDB_ColorItem::None: OS.resetColor(); return; + case PDB_ColorItem::Comment: + OS.changeColor(raw_ostream::GREEN, false); + return; case PDB_ColorItem::Address: OS.changeColor(raw_ostream::YELLOW, /*bold=*/true); return; @@ -113,6 +134,7 @@ void WithColor::applyColor(PDB_ColorItem C) { case PDB_ColorItem::Path: OS.changeColor(raw_ostream::CYAN, false); return; + case PDB_ColorItem::Padding: case PDB_ColorItem::SectionHeader: OS.changeColor(raw_ostream::RED, true); return; diff --git a/tools/llvm-pdbdump/LinePrinter.h b/tools/llvm-pdbdump/LinePrinter.h index a4401f8af9552..1a922feb1e622 100644 --- a/tools/llvm-pdbdump/LinePrinter.h +++ b/tools/llvm-pdbdump/LinePrinter.h @@ -20,20 +20,24 @@ namespace llvm { namespace pdb { +class ClassLayout; + class LinePrinter { friend class WithColor; public: - LinePrinter(int Indent, raw_ostream &Stream); + LinePrinter(int Indent, bool UseColor, raw_ostream &Stream); void Indent(); void Unindent(); void NewLine(); + bool hasColor() const { return UseColor; } raw_ostream &getStream() { return OS; } int getIndentLevel() const { return CurrentIndent; } - bool IsTypeExcluded(llvm::StringRef TypeName); + bool IsClassExcluded(const ClassLayout &Class); + bool IsTypeExcluded(llvm::StringRef TypeName, uint32_t Size); bool IsSymbolExcluded(llvm::StringRef SymbolName); bool IsCompilandExcluded(llvm::StringRef CompilandName); @@ -48,6 +52,7 @@ private: raw_ostream &OS; int IndentSpaces; int CurrentIndent; + bool UseColor; std::list<Regex> ExcludeCompilandFilters; std::list<Regex> ExcludeTypeFilters; @@ -68,6 +73,8 @@ enum class PDB_ColorItem { None, Address, Type, + Comment, + Padding, Keyword, Offset, Identifier, @@ -87,6 +94,7 @@ public: private: void applyColor(PDB_ColorItem C); raw_ostream &OS; + bool UseColor; }; } } diff --git a/tools/llvm-pdbdump/PdbYaml.cpp b/tools/llvm-pdbdump/PdbYaml.cpp index 34e0611e14643..e2c4ee967ed36 100644 --- a/tools/llvm-pdbdump/PdbYaml.cpp +++ b/tools/llvm-pdbdump/PdbYaml.cpp @@ -16,14 +16,15 @@ #include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolSerializer.h" #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/DebugInfo/CodeView/TypeSerializer.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/TpiHashing.h" #include "llvm/DebugInfo/PDB/PDBExtras.h" #include "llvm/DebugInfo/PDB/PDBTypes.h" -#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" -#include "llvm/DebugInfo/PDB/Raw/TpiHashing.h" using namespace llvm; using namespace llvm::pdb; @@ -37,6 +38,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::PdbDbiModuleInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::PdbSymbolRecord) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::PdbTpiRecord) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::StreamBlockList) +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::pdb::PdbRaw_FeatureSig) namespace llvm { namespace yaml { @@ -133,25 +135,45 @@ template <> struct ScalarEnumerationTraits<llvm::pdb::PdbRaw_TpiVer> { io.enumCase(Value, "VC80", llvm::pdb::PdbRaw_TpiVer::PdbTpiV80); } }; + +template <> struct ScalarEnumerationTraits<llvm::pdb::PdbRaw_FeatureSig> { + static void enumeration(IO &io, PdbRaw_FeatureSig &Features) { + io.enumCase(Features, "MinimalDebugInfo", + PdbRaw_FeatureSig::MinimalDebugInfo); + io.enumCase(Features, "NoTypeMerge", PdbRaw_FeatureSig::NoTypeMerge); + io.enumCase(Features, "VC110", PdbRaw_FeatureSig::VC110); + io.enumCase(Features, "VC140", PdbRaw_FeatureSig::VC140); + } +}; } } void MappingTraits<PdbObject>::mapping(IO &IO, PdbObject &Obj) { + // Create a single serialization context that will be passed through the + // entire process of serializing / deserializing a Tpi Stream. This is + // especially important when we are going from Pdb -> Yaml because we need + // to maintain state in a TypeTableBuilder across mappings, and at the end of + // the entire process, we need to have one TypeTableBuilder that has every + // record. + pdb::yaml::SerializationContext Context(IO, Obj.Allocator); + + IO.mapOptional("MSF", Obj.Headers); IO.mapOptional("StreamSizes", Obj.StreamSizes); IO.mapOptional("StreamMap", Obj.StreamMap); + IO.mapOptional("StringTable", Obj.StringTable); IO.mapOptional("PdbStream", Obj.PdbStream); - IO.mapOptional("DbiStream", Obj.DbiStream); - IO.mapOptionalWithContext("TpiStream", Obj.TpiStream, Obj.Allocator); - IO.mapOptionalWithContext("IpiStream", Obj.IpiStream, Obj.Allocator); + IO.mapOptionalWithContext("DbiStream", Obj.DbiStream, Context); + IO.mapOptionalWithContext("TpiStream", Obj.TpiStream, Context); + IO.mapOptionalWithContext("IpiStream", Obj.IpiStream, Context); } void MappingTraits<MSFHeaders>::mapping(IO &IO, MSFHeaders &Obj) { - IO.mapRequired("SuperBlock", Obj.SuperBlock); - IO.mapRequired("NumDirectoryBlocks", Obj.NumDirectoryBlocks); - IO.mapRequired("DirectoryBlocks", Obj.DirectoryBlocks); - IO.mapRequired("NumStreams", Obj.NumStreams); - IO.mapRequired("FileSize", Obj.FileSize); + IO.mapOptional("SuperBlock", Obj.SuperBlock); + IO.mapOptional("NumDirectoryBlocks", Obj.NumDirectoryBlocks); + IO.mapOptional("DirectoryBlocks", Obj.DirectoryBlocks); + IO.mapOptional("NumStreams", Obj.NumStreams); + IO.mapOptional("FileSize", Obj.FileSize); } void MappingTraits<msf::SuperBlock>::mapping(IO &IO, msf::SuperBlock &SB) { @@ -159,12 +181,13 @@ void MappingTraits<msf::SuperBlock>::mapping(IO &IO, msf::SuperBlock &SB) { ::memcpy(SB.MagicBytes, msf::Magic, sizeof(msf::Magic)); } - IO.mapRequired("BlockSize", SB.BlockSize); - IO.mapRequired("FreeBlockMap", SB.FreeBlockMapBlock); - IO.mapRequired("NumBlocks", SB.NumBlocks); - IO.mapRequired("NumDirectoryBytes", SB.NumDirectoryBytes); - IO.mapRequired("Unknown1", SB.Unknown1); - IO.mapRequired("BlockMapAddr", SB.BlockMapAddr); + using u32 = support::ulittle32_t; + IO.mapOptional("BlockSize", SB.BlockSize, u32(4096U)); + IO.mapOptional("FreeBlockMap", SB.FreeBlockMapBlock, u32(0U)); + IO.mapOptional("NumBlocks", SB.NumBlocks, u32(0U)); + IO.mapOptional("NumDirectoryBytes", SB.NumDirectoryBytes, u32(0U)); + IO.mapOptional("Unknown1", SB.Unknown1, u32(0U)); + IO.mapOptional("BlockMapAddr", SB.BlockMapAddr, u32(0U)); } void MappingTraits<StreamBlockList>::mapping(IO &IO, StreamBlockList &SB) { @@ -172,35 +195,27 @@ void MappingTraits<StreamBlockList>::mapping(IO &IO, StreamBlockList &SB) { } void MappingTraits<PdbInfoStream>::mapping(IO &IO, PdbInfoStream &Obj) { - IO.mapRequired("Age", Obj.Age); - IO.mapRequired("Guid", Obj.Guid); - IO.mapRequired("Signature", Obj.Signature); - IO.mapRequired("Version", Obj.Version); - IO.mapRequired("NamedStreams", Obj.NamedStreams); + IO.mapOptional("Age", Obj.Age, 1U); + IO.mapOptional("Guid", Obj.Guid); + IO.mapOptional("Signature", Obj.Signature, 0U); + IO.mapOptional("Features", Obj.Features); + IO.mapOptional("Version", Obj.Version, PdbImplVC70); } -void MappingTraits<PdbDbiStream>::mapping(IO &IO, PdbDbiStream &Obj) { - IO.mapRequired("VerHeader", Obj.VerHeader); - IO.mapRequired("Age", Obj.Age); - IO.mapRequired("BuildNumber", Obj.BuildNumber); - IO.mapRequired("PdbDllVersion", Obj.PdbDllVersion); - IO.mapRequired("PdbDllRbld", Obj.PdbDllRbld); - IO.mapRequired("Flags", Obj.Flags); - IO.mapRequired("MachineType", Obj.MachineType); - IO.mapOptional("Modules", Obj.ModInfos); +void MappingContextTraits<PdbDbiStream, pdb::yaml::SerializationContext>::mapping(IO &IO, PdbDbiStream &Obj, pdb::yaml::SerializationContext &Context) { + IO.mapOptional("VerHeader", Obj.VerHeader, PdbDbiV70); + IO.mapOptional("Age", Obj.Age, 1U); + IO.mapOptional("BuildNumber", Obj.BuildNumber, uint16_t(0U)); + IO.mapOptional("PdbDllVersion", Obj.PdbDllVersion, 0U); + IO.mapOptional("PdbDllRbld", Obj.PdbDllRbld, uint16_t(0U)); + IO.mapOptional("Flags", Obj.Flags, uint16_t(1U)); + IO.mapOptional("MachineType", Obj.MachineType, PDB_Machine::x86); + IO.mapOptionalWithContext("Modules", Obj.ModInfos, Context); } -void MappingContextTraits<PdbTpiStream, BumpPtrAllocator>::mapping( - IO &IO, pdb::yaml::PdbTpiStream &Obj, BumpPtrAllocator &Allocator) { - // Create a single serialization context that will be passed through the - // entire process of serializing / deserializing a Tpi Stream. This is - // especially important when we are going from Pdb -> Yaml because we need - // to maintain state in a TypeTableBuilder across mappings, and at the end of - // the entire process, we need to have one TypeTableBuilder that has every - // record. - pdb::yaml::SerializationContext Context(IO, Allocator); - - IO.mapRequired("Version", Obj.Version); +void MappingContextTraits<PdbTpiStream, pdb::yaml::SerializationContext>::mapping( + IO &IO, pdb::yaml::PdbTpiStream &Obj, pdb::yaml::SerializationContext &Context) { + IO.mapOptional("Version", Obj.Version, PdbTpiV80); IO.mapRequired("Records", Obj.Records, Context); } @@ -210,8 +225,9 @@ void MappingTraits<NamedStreamMapping>::mapping(IO &IO, IO.mapRequired("StreamNum", Obj.StreamNumber); } -void MappingTraits<PdbSymbolRecord>::mapping(IO &IO, PdbSymbolRecord &Obj) { +void MappingContextTraits<PdbSymbolRecord, pdb::yaml::SerializationContext>::mapping(IO &IO, PdbSymbolRecord &Obj, pdb::yaml::SerializationContext &Context) { codeview::SymbolVisitorCallbackPipeline Pipeline; + codeview::SymbolSerializer Serializer(Context.Allocator); codeview::SymbolDeserializer Deserializer(nullptr); codeview::yaml::YamlSymbolDumper Dumper(IO); @@ -220,23 +236,26 @@ void MappingTraits<PdbSymbolRecord>::mapping(IO &IO, PdbSymbolRecord &Obj) { Pipeline.addCallbackToPipeline(Deserializer); Pipeline.addCallbackToPipeline(Dumper); } else { - return; + // For the other way around, dump it into a concrete structure, and then + // serialize it into the CVRecord. + Pipeline.addCallbackToPipeline(Dumper); + Pipeline.addCallbackToPipeline(Serializer); } codeview::CVSymbolVisitor Visitor(Pipeline); consumeError(Visitor.visitSymbolRecord(Obj.Record)); } -void MappingTraits<PdbModiStream>::mapping(IO &IO, PdbModiStream &Obj) { - IO.mapRequired("Signature", Obj.Signature); - IO.mapRequired("Records", Obj.Symbols); +void MappingContextTraits<PdbModiStream, pdb::yaml::SerializationContext>::mapping(IO &IO, PdbModiStream &Obj, pdb::yaml::SerializationContext &Context) { + IO.mapOptional("Signature", Obj.Signature, 4U); + IO.mapRequired("Records", Obj.Symbols, Context); } -void MappingTraits<PdbDbiModuleInfo>::mapping(IO &IO, PdbDbiModuleInfo &Obj) { +void MappingContextTraits<PdbDbiModuleInfo, pdb::yaml::SerializationContext>::mapping(IO &IO, PdbDbiModuleInfo &Obj, pdb::yaml::SerializationContext &Context) { IO.mapRequired("Module", Obj.Mod); - IO.mapRequired("ObjFile", Obj.Obj); + IO.mapOptional("ObjFile", Obj.Obj, Obj.Mod); IO.mapOptional("SourceFiles", Obj.SourceFiles); - IO.mapOptional("Modi", Obj.Modi); + IO.mapOptionalWithContext("Modi", Obj.Modi, Context); } void MappingContextTraits<PdbTpiRecord, pdb::yaml::SerializationContext>:: diff --git a/tools/llvm-pdbdump/PdbYaml.h b/tools/llvm-pdbdump/PdbYaml.h index 398186f16d723..2c4cd237f8d7f 100644 --- a/tools/llvm-pdbdump/PdbYaml.h +++ b/tools/llvm-pdbdump/PdbYaml.h @@ -16,9 +16,9 @@ #include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/MSF/MSFCommon.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" #include "llvm/DebugInfo/PDB/PDBTypes.h" -#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" -#include "llvm/DebugInfo/PDB/Raw/RawConstants.h" #include "llvm/Support/Endian.h" #include "llvm/Support/YAMLTraits.h" @@ -32,10 +32,10 @@ struct SerializationContext; struct MSFHeaders { msf::SuperBlock SuperBlock; - uint32_t NumDirectoryBlocks; + uint32_t NumDirectoryBlocks = 0; std::vector<uint32_t> DirectoryBlocks; - uint32_t NumStreams; - uint32_t FileSize; + uint32_t NumStreams = 0; + uint32_t FileSize = 0; }; struct StreamBlockList { @@ -48,10 +48,11 @@ struct NamedStreamMapping { }; struct PdbInfoStream { - PdbRaw_ImplVer Version; - uint32_t Signature; - uint32_t Age; + PdbRaw_ImplVer Version = PdbImplVC70; + uint32_t Signature = 0; + uint32_t Age = 1; PDB_UniqueId Guid; + std::vector<PdbRaw_FeatureSig> Features; std::vector<NamedStreamMapping> NamedStreams; }; @@ -72,13 +73,13 @@ struct PdbDbiModuleInfo { }; struct PdbDbiStream { - PdbRaw_DbiVer VerHeader; - uint32_t Age; - uint16_t BuildNumber; - uint32_t PdbDllVersion; - uint16_t PdbDllRbld; - uint16_t Flags; - PDB_Machine MachineType; + PdbRaw_DbiVer VerHeader = PdbDbiV70; + uint32_t Age = 1; + uint16_t BuildNumber = 0; + uint32_t PdbDllVersion = 0; + uint16_t PdbDllRbld = 0; + uint16_t Flags = 1; + PDB_Machine MachineType = PDB_Machine::x86; std::vector<PdbDbiModuleInfo> ModInfos; }; @@ -92,7 +93,7 @@ struct PdbTpiFieldListRecord { }; struct PdbTpiStream { - PdbRaw_TpiVer Version; + PdbRaw_TpiVer Version = PdbTpiV80; std::vector<PdbTpiRecord> Records; }; @@ -107,6 +108,8 @@ struct PdbObject { Optional<PdbTpiStream> TpiStream; Optional<PdbTpiStream> IpiStream; + Optional<std::vector<StringRef>> StringTable; + BumpPtrAllocator &Allocator; }; } @@ -136,30 +139,30 @@ template <> struct MappingTraits<pdb::yaml::PdbInfoStream> { static void mapping(IO &IO, pdb::yaml::PdbInfoStream &Obj); }; -template <> struct MappingTraits<pdb::yaml::PdbDbiStream> { - static void mapping(IO &IO, pdb::yaml::PdbDbiStream &Obj); +template <> struct MappingContextTraits<pdb::yaml::PdbDbiStream, pdb::yaml::SerializationContext> { + static void mapping(IO &IO, pdb::yaml::PdbDbiStream &Obj, pdb::yaml::SerializationContext &Context); }; template <> -struct MappingContextTraits<pdb::yaml::PdbTpiStream, llvm::BumpPtrAllocator> { +struct MappingContextTraits<pdb::yaml::PdbTpiStream, pdb::yaml::SerializationContext> { static void mapping(IO &IO, pdb::yaml::PdbTpiStream &Obj, - llvm::BumpPtrAllocator &Allocator); + pdb::yaml::SerializationContext &Context); }; template <> struct MappingTraits<pdb::yaml::NamedStreamMapping> { static void mapping(IO &IO, pdb::yaml::NamedStreamMapping &Obj); }; -template <> struct MappingTraits<pdb::yaml::PdbSymbolRecord> { - static void mapping(IO &IO, pdb::yaml::PdbSymbolRecord &Obj); +template <> struct MappingContextTraits<pdb::yaml::PdbSymbolRecord, pdb::yaml::SerializationContext> { + static void mapping(IO &IO, pdb::yaml::PdbSymbolRecord &Obj, pdb::yaml::SerializationContext &Context); }; -template <> struct MappingTraits<pdb::yaml::PdbModiStream> { - static void mapping(IO &IO, pdb::yaml::PdbModiStream &Obj); +template <> struct MappingContextTraits<pdb::yaml::PdbModiStream, pdb::yaml::SerializationContext> { + static void mapping(IO &IO, pdb::yaml::PdbModiStream &Obj, pdb::yaml::SerializationContext &Context); }; -template <> struct MappingTraits<pdb::yaml::PdbDbiModuleInfo> { - static void mapping(IO &IO, pdb::yaml::PdbDbiModuleInfo &Obj); +template <> struct MappingContextTraits<pdb::yaml::PdbDbiModuleInfo, pdb::yaml::SerializationContext> { + static void mapping(IO &IO, pdb::yaml::PdbDbiModuleInfo &Obj, pdb::yaml::SerializationContext &Context); }; template <> diff --git a/tools/llvm-pdbdump/PrettyBuiltinDumper.cpp b/tools/llvm-pdbdump/PrettyBuiltinDumper.cpp index f866132aa8866..591d5e70cfd6e 100644 --- a/tools/llvm-pdbdump/PrettyBuiltinDumper.cpp +++ b/tools/llvm-pdbdump/PrettyBuiltinDumper.cpp @@ -20,6 +20,10 @@ BuiltinDumper::BuiltinDumper(LinePrinter &P) : PDBSymDumper(false), Printer(P) {} void BuiltinDumper::start(const PDBSymbolTypeBuiltin &Symbol) { + if (Symbol.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "const "; + if (Symbol.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile "; WithColor(Printer, PDB_ColorItem::Type).get() << getTypeName(Symbol); } diff --git a/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp b/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp index b0c534f7c5b17..9f213a4b4d960 100644 --- a/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp +++ b/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp @@ -10,22 +10,16 @@ #include "PrettyClassDefinitionDumper.h" #include "LinePrinter.h" -#include "PrettyEnumDumper.h" -#include "PrettyFunctionDumper.h" -#include "PrettyTypedefDumper.h" -#include "PrettyVariableDumper.h" +#include "PrettyClassLayoutGraphicalDumper.h" +#include "PrettyClassLayoutTextDumper.h" #include "llvm-pdbdump.h" -#include "llvm/DebugInfo/PDB/IPDBSession.h" -#include "llvm/DebugInfo/PDB/PDBExtras.h" -#include "llvm/DebugInfo/PDB/PDBSymbolData.h" -#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/SmallString.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h" -#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" -#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" -#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" -#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h" +#include "llvm/DebugInfo/PDB/UDTLayout.h" + #include "llvm/Support/Format.h" using namespace llvm; @@ -35,158 +29,97 @@ ClassDefinitionDumper::ClassDefinitionDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {} void ClassDefinitionDumper::start(const PDBSymbolTypeUDT &Class) { - std::string Name = Class.getName(); - WithColor(Printer, PDB_ColorItem::Keyword).get() << Class.getUdtKind() << " "; - WithColor(Printer, PDB_ColorItem::Type).get() << Class.getName(); - - auto Bases = Class.findAllChildren<PDBSymbolTypeBaseClass>(); - if (Bases->getChildCount() > 0) { - Printer.Indent(); - Printer.NewLine(); - Printer << ":"; - uint32_t BaseIndex = 0; - while (auto Base = Bases->getNext()) { - Printer << " "; - WithColor(Printer, PDB_ColorItem::Keyword).get() << Base->getAccess(); - if (Base->isVirtualBaseClass()) - WithColor(Printer, PDB_ColorItem::Keyword).get() << " virtual"; - WithColor(Printer, PDB_ColorItem::Type).get() << " " << Base->getName(); - if (++BaseIndex < Bases->getChildCount()) { - Printer.NewLine(); - Printer << ","; - } - } - Printer.Unindent(); - } - - Printer << " {"; - auto Children = Class.findAllChildren(); - if (Children->getChildCount() == 0) { - Printer << "}"; - return; - } + assert(opts::pretty::ClassFormat != + opts::pretty::ClassDefinitionFormat::None); - // Try to dump symbols organized by member access level. Public members - // first, then protected, then private. This might be slow, so it's worth - // reconsidering the value of this if performance of large PDBs is a problem. - // NOTE: Access level of nested types is not recorded in the PDB, so we have - // a special case for them. - SymbolGroupByAccess Groups; - Groups.insert(std::make_pair(0, SymbolGroup())); - Groups.insert(std::make_pair((int)PDB_MemberAccess::Private, SymbolGroup())); - Groups.insert( - std::make_pair((int)PDB_MemberAccess::Protected, SymbolGroup())); - Groups.insert(std::make_pair((int)PDB_MemberAccess::Public, SymbolGroup())); - - while (auto Child = Children->getNext()) { - PDB_MemberAccess Access = Child->getRawSymbol().getAccess(); - if (isa<PDBSymbolTypeBaseClass>(*Child)) - continue; - - auto &AccessGroup = Groups.find((int)Access)->second; - - if (auto Func = dyn_cast<PDBSymbolFunc>(Child.get())) { - if (Func->isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated) - continue; - if (Func->getLength() == 0 && !Func->isPureVirtual() && - !Func->isIntroVirtualFunction()) - continue; - Child.release(); - AccessGroup.Functions.push_back(std::unique_ptr<PDBSymbolFunc>(Func)); - } else if (auto Data = dyn_cast<PDBSymbolData>(Child.get())) { - Child.release(); - AccessGroup.Data.push_back(std::unique_ptr<PDBSymbolData>(Data)); - } else { - AccessGroup.Unknown.push_back(std::move(Child)); - } - } - - int Count = 0; - Count += dumpAccessGroup((PDB_MemberAccess)0, Groups[0]); - Count += dumpAccessGroup(PDB_MemberAccess::Public, - Groups[(int)PDB_MemberAccess::Public]); - Count += dumpAccessGroup(PDB_MemberAccess::Protected, - Groups[(int)PDB_MemberAccess::Protected]); - Count += dumpAccessGroup(PDB_MemberAccess::Private, - Groups[(int)PDB_MemberAccess::Private]); - if (Count > 0) - Printer.NewLine(); - Printer << "}"; + ClassLayout Layout(Class); + start(Layout); } -int ClassDefinitionDumper::dumpAccessGroup(PDB_MemberAccess Access, - const SymbolGroup &Group) { - if (Group.Functions.empty() && Group.Data.empty() && Group.Unknown.empty()) - return 0; +void ClassDefinitionDumper::start(const ClassLayout &Layout) { + prettyPrintClassIntro(Layout); - int Count = 0; - if (Access == PDB_MemberAccess::Private) { - Printer.NewLine(); - WithColor(Printer, PDB_ColorItem::Keyword).get() << "private"; - Printer << ":"; - } else if (Access == PDB_MemberAccess::Protected) { - Printer.NewLine(); - WithColor(Printer, PDB_ColorItem::Keyword).get() << "protected"; - Printer << ":"; - } else if (Access == PDB_MemberAccess::Public) { - Printer.NewLine(); - WithColor(Printer, PDB_ColorItem::Keyword).get() << "public"; - Printer << ":"; + switch (opts::pretty::ClassFormat) { + case opts::pretty::ClassDefinitionFormat::Graphical: { + PrettyClassLayoutGraphicalDumper Dumper(Printer, 0); + DumpedAnything = Dumper.start(Layout); + break; } - Printer.Indent(); - for (auto iter = Group.Functions.begin(), end = Group.Functions.end(); - iter != end; ++iter) { - ++Count; - (*iter)->dump(*this); - } - for (auto iter = Group.Data.begin(), end = Group.Data.end(); iter != end; - ++iter) { - ++Count; - (*iter)->dump(*this); + case opts::pretty::ClassDefinitionFormat::Standard: + case opts::pretty::ClassDefinitionFormat::Layout: { + PrettyClassLayoutTextDumper Dumper(Printer); + DumpedAnything |= Dumper.start(Layout); + break; } - for (auto iter = Group.Unknown.begin(), end = Group.Unknown.end(); - iter != end; ++iter) { - ++Count; - (*iter)->dump(*this); + default: + llvm_unreachable("Unreachable!"); } - Printer.Unindent(); - return Count; -} - -void ClassDefinitionDumper::dump(const PDBSymbolTypeBaseClass &Symbol) {} -void ClassDefinitionDumper::dump(const PDBSymbolData &Symbol) { - VariableDumper Dumper(Printer); - Dumper.start(Symbol); + prettyPrintClassOutro(Layout); } -void ClassDefinitionDumper::dump(const PDBSymbolFunc &Symbol) { - if (Printer.IsSymbolExcluded(Symbol.getName())) - return; +static void printBase(LinePrinter &Printer, const PDBSymbolTypeBaseClass &Base, + uint32_t &CurIndex, uint32_t TotalBaseCount, + bool IsVirtual) { + Printer << " "; + WithColor(Printer, PDB_ColorItem::Keyword).get() << Base.getAccess(); + if (IsVirtual) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " virtual"; + WithColor(Printer, PDB_ColorItem::Type).get() << " " << Base.getName(); + if (++CurIndex < TotalBaseCount) { + Printer.NewLine(); + Printer << ","; + } +} +void ClassDefinitionDumper::prettyPrintClassIntro(const ClassLayout &Layout) { + DumpedAnything = false; Printer.NewLine(); - FunctionDumper Dumper(Printer); - Dumper.start(Symbol, FunctionDumper::PointerType::None); -} -void ClassDefinitionDumper::dump(const PDBSymbolTypeVTable &Symbol) {} + uint32_t Size = Layout.getClassSize(); + const PDBSymbolTypeUDT &Class = Layout.getClass(); -void ClassDefinitionDumper::dump(const PDBSymbolTypeEnum &Symbol) { - if (Printer.IsTypeExcluded(Symbol.getName())) - return; + WithColor(Printer, PDB_ColorItem::Keyword).get() << Class.getUdtKind() << " "; + WithColor(Printer, PDB_ColorItem::Type).get() << Class.getName(); + WithColor(Printer, PDB_ColorItem::Comment).get() << " [sizeof = " << Size + << "]"; + uint32_t BaseCount = Layout.bases().size(); + uint32_t VBaseCount = Layout.vbases().size(); + uint32_t TotalBaseCount = BaseCount + VBaseCount; + if (TotalBaseCount > 0) { + Printer.Indent(); + Printer.NewLine(); + Printer << ":"; + uint32_t BaseIndex = 0; + for (auto BC : Layout.bases()) { + const auto &Base = BC->getBase(); + printBase(Printer, Base, BaseIndex, TotalBaseCount, false); + } + for (auto &BC : Layout.vbases()) { + printBase(Printer, *BC, BaseIndex, TotalBaseCount, true); + } - Printer.NewLine(); - EnumDumper Dumper(Printer); - Dumper.start(Symbol); -} + Printer.Unindent(); + } -void ClassDefinitionDumper::dump(const PDBSymbolTypeTypedef &Symbol) { - if (Printer.IsTypeExcluded(Symbol.getName())) - return; + Printer << " {"; + Printer.Indent(); +} +void ClassDefinitionDumper::prettyPrintClassOutro(const ClassLayout &Layout) { + Printer.Unindent(); + if (DumpedAnything) + Printer.NewLine(); + Printer << "}"; Printer.NewLine(); - TypedefDumper Dumper(Printer); - Dumper.start(Symbol); + if (Layout.deepPaddingSize() > 0) { + APFloat Pct(100.0 * (double)Layout.deepPaddingSize() / + (double)Layout.getClassSize()); + SmallString<8> PctStr; + Pct.toString(PctStr, 4); + WithColor(Printer, PDB_ColorItem::Padding).get() + << "Total padding " << Layout.deepPaddingSize() << " bytes (" << PctStr + << "% of class size)"; + Printer.NewLine(); + } } - -void ClassDefinitionDumper::dump(const PDBSymbolTypeUDT &Symbol) {} diff --git a/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h b/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h index 0831f47557ed6..0e27733b3ccb9 100644 --- a/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h +++ b/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h @@ -10,6 +10,8 @@ #ifndef LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSDEFINITIONDUMPER_H #define LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSDEFINITIONDUMPER_H +#include "llvm/ADT/BitVector.h" + #include "llvm/DebugInfo/PDB/PDBSymDumper.h" #include "llvm/DebugInfo/PDB/PDBSymbolData.h" #include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" @@ -19,44 +21,26 @@ #include <unordered_map> namespace llvm { +class BitVector; + namespace pdb { +class ClassLayout; class LinePrinter; class ClassDefinitionDumper : public PDBSymDumper { public: ClassDefinitionDumper(LinePrinter &P); - void start(const PDBSymbolTypeUDT &Exe); - - void dump(const PDBSymbolTypeBaseClass &Symbol) override; - void dump(const PDBSymbolData &Symbol) override; - void dump(const PDBSymbolTypeEnum &Symbol) override; - void dump(const PDBSymbolFunc &Symbol) override; - void dump(const PDBSymbolTypeTypedef &Symbol) override; - void dump(const PDBSymbolTypeUDT &Symbol) override; - void dump(const PDBSymbolTypeVTable &Symbol) override; + void start(const PDBSymbolTypeUDT &Class); + void start(const ClassLayout &Class); private: - LinePrinter &Printer; + void prettyPrintClassIntro(const ClassLayout &Class); + void prettyPrintClassOutro(const ClassLayout &Class); - struct SymbolGroup { - SymbolGroup() {} - SymbolGroup(SymbolGroup &&Other) { - Functions = std::move(Other.Functions); - Data = std::move(Other.Data); - Unknown = std::move(Other.Unknown); - } - - std::list<std::unique_ptr<PDBSymbolFunc>> Functions; - std::list<std::unique_ptr<PDBSymbolData>> Data; - std::list<std::unique_ptr<PDBSymbol>> Unknown; - SymbolGroup(const SymbolGroup &other) = delete; - SymbolGroup &operator=(const SymbolGroup &other) = delete; - }; - typedef std::unordered_map<int, SymbolGroup> SymbolGroupByAccess; - - int dumpAccessGroup(PDB_MemberAccess Access, const SymbolGroup &Group); + bool DumpedAnything = false; + LinePrinter &Printer; }; } } diff --git a/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.cpp b/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.cpp new file mode 100644 index 0000000000000..d146ca9d47121 --- /dev/null +++ b/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.cpp @@ -0,0 +1,151 @@ +//===- PrettyClassLayoutGraphicalDumper.h -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyClassLayoutGraphicalDumper.h" + +#include "LinePrinter.h" +#include "PrettyClassDefinitionDumper.h" +#include "PrettyVariableDumper.h" + +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" +#include "llvm/DebugInfo/PDB/UDTLayout.h" +#include "llvm/Support/Format.h" + +using namespace llvm; +using namespace llvm::pdb; + +PrettyClassLayoutGraphicalDumper::PrettyClassLayoutGraphicalDumper( + LinePrinter &P, uint32_t InitialOffset) + : PDBSymDumper(true), Printer(P), ClassOffsetZero(InitialOffset), + CurrentAbsoluteOffset(InitialOffset) {} + +bool PrettyClassLayoutGraphicalDumper::start(const UDTLayoutBase &Layout) { + const BitVector &UseMap = Layout.usedBytes(); + int NextPaddingByte = UseMap.find_first_unset(); + + for (auto &Item : Layout.layout_items()) { + // Calculate the absolute offset of the first byte of the next field. + uint32_t RelativeOffset = Item->getOffsetInParent(); + CurrentAbsoluteOffset = ClassOffsetZero + RelativeOffset; + + // Since there is storage there, it should be set! However, this might + // be an empty base, in which case it could extend outside the bounds of + // the parent class. + if (RelativeOffset < UseMap.size() && (Item->getSize() > 0)) { + assert(UseMap.test(RelativeOffset)); + + // If there is any remaining padding in this class, and the offset of the + // new item is after the padding, then we must have just jumped over some + // padding. Print a padding row and then look for where the next block + // of padding begins. + if ((NextPaddingByte >= 0) && + (RelativeOffset > uint32_t(NextPaddingByte))) { + printPaddingRow(RelativeOffset - NextPaddingByte); + NextPaddingByte = UseMap.find_next_unset(RelativeOffset); + } + } + + CurrentItem = Item.get(); + Item->getSymbol().dump(*this); + } + + if (NextPaddingByte >= 0 && Layout.getClassSize() > 1) { + uint32_t Amount = Layout.getClassSize() - NextPaddingByte; + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Padding).get() << "<padding> (" << Amount + << " bytes)"; + DumpedAnything = true; + } + + return DumpedAnything; +} + +void PrettyClassLayoutGraphicalDumper::printPaddingRow(uint32_t Amount) { + if (Amount == 0) + return; + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Padding).get() << "<padding> (" << Amount + << " bytes)"; + DumpedAnything = true; +} + +void PrettyClassLayoutGraphicalDumper::dump( + const PDBSymbolTypeBaseClass &Symbol) { + assert(CurrentItem != nullptr); + + Printer.NewLine(); + BaseClassLayout &Layout = static_cast<BaseClassLayout &>(*CurrentItem); + + std::string Label = Layout.isVirtualBase() ? "vbase" : "base"; + Printer << Label << " "; + + WithColor(Printer, PDB_ColorItem::Offset).get() + << "+" << format_hex(CurrentAbsoluteOffset, 4) + << " [sizeof=" << Layout.getSize() << "] "; + + WithColor(Printer, PDB_ColorItem::Identifier).get() << Layout.getName(); + + Printer.Indent(); + uint32_t ChildOffsetZero = ClassOffsetZero + Layout.getOffsetInParent(); + PrettyClassLayoutGraphicalDumper BaseDumper(Printer, ChildOffsetZero); + BaseDumper.start(Layout); + Printer.Unindent(); + + DumpedAnything = true; +} + +void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolData &Symbol) { + assert(CurrentItem != nullptr); + + DataMemberLayoutItem &Layout = + static_cast<DataMemberLayoutItem &>(*CurrentItem); + + VariableDumper VarDumper(Printer); + VarDumper.start(Symbol, ClassOffsetZero); + + if (Layout.hasUDTLayout()) { + Printer.Indent(); + PrettyClassLayoutGraphicalDumper TypeDumper(Printer, ClassOffsetZero); + TypeDumper.start(Layout.getUDTLayout()); + Printer.Unindent(); + } + + DumpedAnything = true; +} + +void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolTypeVTable &Symbol) { + assert(CurrentItem != nullptr); + + VTableLayoutItem &Layout = static_cast<VTableLayoutItem &>(*CurrentItem); + + VariableDumper VarDumper(Printer); + VarDumper.start(Symbol, ClassOffsetZero); + + Printer.Indent(); + uint32_t Index = 0; + for (auto &Func : Layout.funcs()) { + Printer.NewLine(); + std::string Name = Func->getName(); + auto ParentClass = + unique_dyn_cast<PDBSymbolTypeUDT>(Func->getClassParent()); + assert(ParentClass); + WithColor(Printer, PDB_ColorItem::Address).get() << " [" << Index << "] "; + WithColor(Printer, PDB_ColorItem::Identifier).get() + << "&" << ParentClass->getName(); + Printer << "::"; + WithColor(Printer, PDB_ColorItem::Identifier).get() << Name; + ++Index; + } + Printer.Unindent(); + + DumpedAnything = true; +} diff --git a/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.h b/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.h new file mode 100644 index 0000000000000..7dfb74c4e14b4 --- /dev/null +++ b/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.h @@ -0,0 +1,47 @@ +//===- PrettyClassLayoutGraphicalDumper.h -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSLAYOUTGRAPHICALDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSLAYOUTGRAPHICALDUMPER_H + +#include "llvm/ADT/BitVector.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { + +namespace pdb { + +class UDTLayoutBase; +class StorageItemBase; +class LinePrinter; + +class PrettyClassLayoutGraphicalDumper : public PDBSymDumper { +public: + PrettyClassLayoutGraphicalDumper(LinePrinter &P, uint32_t InitialOffset); + + bool start(const UDTLayoutBase &Layout); + + void dump(const PDBSymbolTypeBaseClass &Symbol) override; + void dump(const PDBSymbolData &Symbol) override; + void dump(const PDBSymbolTypeVTable &Symbol) override; + +private: + void printPaddingRow(uint32_t Amount); + + LinePrinter &Printer; + + StorageItemBase *CurrentItem = nullptr; + uint32_t ClassOffsetZero = 0; + uint32_t CurrentAbsoluteOffset = 0; + bool DumpedAnything = false; +}; +} +} +#endif diff --git a/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.cpp b/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.cpp new file mode 100644 index 0000000000000..02f31108b0dfe --- /dev/null +++ b/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.cpp @@ -0,0 +1,119 @@ +//===- PrettyClassLayoutTextDumper.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyClassLayoutTextDumper.h" + +#include "LinePrinter.h" +#include "PrettyEnumDumper.h" +#include "PrettyFunctionDumper.h" +#include "PrettyTypedefDumper.h" +#include "PrettyVariableDumper.h" +#include "llvm-pdbdump.h" + +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h" +#include "llvm/DebugInfo/PDB/UDTLayout.h" + +#include "llvm/Support/Format.h" + +using namespace llvm; +using namespace llvm::pdb; + +PrettyClassLayoutTextDumper::PrettyClassLayoutTextDumper(LinePrinter &P) + : PDBSymDumper(true), Printer(P) {} + +bool PrettyClassLayoutTextDumper::start(const ClassLayout &Layout) { + if (opts::pretty::ClassFormat == + opts::pretty::ClassDefinitionFormat::Standard) { + for (auto &Other : Layout.other_items()) + Other->dump(*this); + for (auto &Func : Layout.funcs()) + Func->dump(*this); + } + + const BitVector &UseMap = Layout.usedBytes(); + int NextUnusedByte = Layout.usedBytes().find_first_unset(); + // Next dump items which affect class layout. + for (auto &LayoutItem : Layout.layout_items()) { + if (NextUnusedByte >= 0) { + // If there are padding bytes remaining, see if this field is the first to + // cross a padding boundary, and print a padding field indicator if so. + int Off = LayoutItem->getOffsetInParent(); + if (Off > NextUnusedByte) { + uint32_t Amount = Off - NextUnusedByte; + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Padding).get() << "<padding> (" + << Amount << " bytes)"; + assert(UseMap.find_next(NextUnusedByte) == Off); + NextUnusedByte = UseMap.find_next_unset(Off); + } + } + LayoutItem->getSymbol().dump(*this); + } + + if (NextUnusedByte >= 0 && Layout.getClassSize() > 1) { + uint32_t Amount = Layout.getClassSize() - NextUnusedByte; + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Padding).get() << "<padding> (" << Amount + << " bytes)"; + DumpedAnything = true; + } + + return DumpedAnything; +} + +void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeBaseClass &Symbol) {} + +void PrettyClassLayoutTextDumper::dump(const PDBSymbolData &Symbol) { + VariableDumper Dumper(Printer); + Dumper.start(Symbol); + DumpedAnything = true; +} + +void PrettyClassLayoutTextDumper::dump(const PDBSymbolFunc &Symbol) { + if (Printer.IsSymbolExcluded(Symbol.getName())) + return; + if (Symbol.isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated) + return; + if (Symbol.getLength() == 0 && !Symbol.isPureVirtual() && + !Symbol.isIntroVirtualFunction()) + return; + + DumpedAnything = true; + Printer.NewLine(); + FunctionDumper Dumper(Printer); + Dumper.start(Symbol, FunctionDumper::PointerType::None); +} + +void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeVTable &Symbol) { + VariableDumper Dumper(Printer); + Dumper.start(Symbol); + DumpedAnything = true; +} + +void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeEnum &Symbol) { + DumpedAnything = true; + Printer.NewLine(); + EnumDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeTypedef &Symbol) { + DumpedAnything = true; + Printer.NewLine(); + TypedefDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeUDT &Symbol) {} diff --git a/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.h b/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.h new file mode 100644 index 0000000000000..56c20f0e84336 --- /dev/null +++ b/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.h @@ -0,0 +1,44 @@ +//===- PrettyClassLayoutTextDumper.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSLAYOUTTEXTDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSLAYOUTTEXTDUMPER_H + +#include "llvm/ADT/BitVector.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { + +namespace pdb { + +class ClassLayout; +class LinePrinter; + +class PrettyClassLayoutTextDumper : public PDBSymDumper { +public: + PrettyClassLayoutTextDumper(LinePrinter &P); + + bool start(const ClassLayout &Layout); + + void dump(const PDBSymbolTypeBaseClass &Symbol) override; + void dump(const PDBSymbolData &Symbol) override; + void dump(const PDBSymbolTypeEnum &Symbol) override; + void dump(const PDBSymbolFunc &Symbol) override; + void dump(const PDBSymbolTypeTypedef &Symbol) override; + void dump(const PDBSymbolTypeUDT &Symbol) override; + void dump(const PDBSymbolTypeVTable &Symbol) override; + +private: + bool DumpedAnything = false; + LinePrinter &Printer; +}; +} +} +#endif diff --git a/tools/llvm-pdbdump/PrettyFunctionDumper.cpp b/tools/llvm-pdbdump/PrettyFunctionDumper.cpp index 2f6ca894fadf3..b0be33c157ce6 100644 --- a/tools/llvm-pdbdump/PrettyFunctionDumper.cpp +++ b/tools/llvm-pdbdump/PrettyFunctionDumper.cpp @@ -195,10 +195,7 @@ void FunctionDumper::start(const PDBSymbolFunc &Symbol, PointerType Pointer) { } void FunctionDumper::dump(const PDBSymbolTypeArray &Symbol) { - uint32_t ElementTypeId = Symbol.getTypeId(); - auto ElementType = Symbol.getSession().getSymbolById(ElementTypeId); - if (!ElementType) - return; + auto ElementType = Symbol.getElementType(); ElementType->dump(*this); Printer << "["; @@ -232,12 +229,11 @@ void FunctionDumper::dump(const PDBSymbolTypeTypedef &Symbol) { } void FunctionDumper::dump(const PDBSymbolTypePointer &Symbol) { - uint32_t PointeeId = Symbol.getTypeId(); - auto PointeeType = Symbol.getSession().getSymbolById(PointeeId); + auto PointeeType = Symbol.getPointeeType(); if (!PointeeType) return; - if (auto FuncSig = dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType.get())) { + if (auto FuncSig = unique_dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType)) { FunctionDumper NestedDumper(Printer); PointerType Pointer = Symbol.isReference() ? PointerType::Reference : PointerType::Pointer; diff --git a/tools/llvm-pdbdump/PrettyTypeDumper.cpp b/tools/llvm-pdbdump/PrettyTypeDumper.cpp index 4f70c8047337a..ffeef72150d26 100644 --- a/tools/llvm-pdbdump/PrettyTypeDumper.cpp +++ b/tools/llvm-pdbdump/PrettyTypeDumper.cpp @@ -22,45 +22,166 @@ #include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" +#include "llvm/DebugInfo/PDB/UDTLayout.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/FormatVariadic.h" using namespace llvm; using namespace llvm::pdb; +using LayoutPtr = std::unique_ptr<ClassLayout>; + +typedef bool (*CompareFunc)(const LayoutPtr &S1, const LayoutPtr &S2); + +static bool CompareNames(const LayoutPtr &S1, const LayoutPtr &S2) { + return S1->getUDTName() < S2->getUDTName(); +} + +static bool CompareSizes(const LayoutPtr &S1, const LayoutPtr &S2) { + return S1->getClassSize() < S2->getClassSize(); +} + +static bool ComparePadding(const LayoutPtr &S1, const LayoutPtr &S2) { + return S1->deepPaddingSize() < S2->deepPaddingSize(); +} + +static CompareFunc getComparisonFunc(opts::pretty::ClassSortMode Mode) { + switch (Mode) { + case opts::pretty::ClassSortMode::Name: + return CompareNames; + case opts::pretty::ClassSortMode::Size: + return CompareSizes; + case opts::pretty::ClassSortMode::Padding: + return ComparePadding; + default: + return nullptr; + } +} + +template <typename Enumerator> +static std::vector<std::unique_ptr<ClassLayout>> +filterAndSortClassDefs(LinePrinter &Printer, Enumerator &E, + uint32_t UnfilteredCount) { + std::vector<std::unique_ptr<ClassLayout>> Filtered; + + Filtered.reserve(UnfilteredCount); + CompareFunc Comp = getComparisonFunc(opts::pretty::ClassOrder); + + uint32_t Examined = 0; + uint32_t Discarded = 0; + while (auto Class = E.getNext()) { + ++Examined; + if (Examined % 10000 == 0) { + outs() << formatv("Examined {0}/{1} items. {2} items discarded\n", + Examined, UnfilteredCount, Discarded); + outs().flush(); + } + + if (Class->getUnmodifiedTypeId() != 0) { + ++Discarded; + continue; + } + + if (Printer.IsTypeExcluded(Class->getName(), Class->getLength())) { + ++Discarded; + continue; + } + + auto Layout = llvm::make_unique<ClassLayout>(std::move(Class)); + if (Layout->deepPaddingSize() < opts::pretty::PaddingThreshold) { + ++Discarded; + continue; + } + + Filtered.push_back(std::move(Layout)); + } + + if (Comp) + std::sort(Filtered.begin(), Filtered.end(), Comp); + return Filtered; +} + TypeDumper::TypeDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {} void TypeDumper::start(const PDBSymbolExe &Exe) { - auto Enums = Exe.findAllChildren<PDBSymbolTypeEnum>(); - Printer.NewLine(); - WithColor(Printer, PDB_ColorItem::Identifier).get() << "Enums"; - Printer << ": (" << Enums->getChildCount() << " items)"; - Printer.Indent(); - while (auto Enum = Enums->getNext()) - Enum->dump(*this); - Printer.Unindent(); - - auto Typedefs = Exe.findAllChildren<PDBSymbolTypeTypedef>(); - Printer.NewLine(); - WithColor(Printer, PDB_ColorItem::Identifier).get() << "Typedefs"; - Printer << ": (" << Typedefs->getChildCount() << " items)"; - Printer.Indent(); - while (auto Typedef = Typedefs->getNext()) - Typedef->dump(*this); - Printer.Unindent(); - - auto Classes = Exe.findAllChildren<PDBSymbolTypeUDT>(); - Printer.NewLine(); - WithColor(Printer, PDB_ColorItem::Identifier).get() << "Classes"; - Printer << ": (" << Classes->getChildCount() << " items)"; - Printer.Indent(); - while (auto Class = Classes->getNext()) - Class->dump(*this); - Printer.Unindent(); + if (opts::pretty::Enums) { + auto Enums = Exe.findAllChildren<PDBSymbolTypeEnum>(); + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Enums"; + Printer << ": (" << Enums->getChildCount() << " items)"; + Printer.Indent(); + while (auto Enum = Enums->getNext()) + Enum->dump(*this); + Printer.Unindent(); + } + + if (opts::pretty::Typedefs) { + auto Typedefs = Exe.findAllChildren<PDBSymbolTypeTypedef>(); + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Typedefs"; + Printer << ": (" << Typedefs->getChildCount() << " items)"; + Printer.Indent(); + while (auto Typedef = Typedefs->getNext()) + Typedef->dump(*this); + Printer.Unindent(); + } + + if (opts::pretty::Classes) { + auto Classes = Exe.findAllChildren<PDBSymbolTypeUDT>(); + uint32_t All = Classes->getChildCount(); + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Classes"; + + bool Precompute = false; + Precompute = + (opts::pretty::ClassOrder != opts::pretty::ClassSortMode::None); + + // If we're using no sort mode, then we can start getting immediate output + // from the tool by just filtering as we go, rather than processing + // everything up front so that we can sort it. This makes the tool more + // responsive. So only precompute the filtered/sorted set of classes if + // necessary due to the specified options. + std::vector<LayoutPtr> Filtered; + uint32_t Shown = All; + if (Precompute) { + Filtered = filterAndSortClassDefs(Printer, *Classes, All); + + Shown = Filtered.size(); + } + + Printer << ": (Showing " << Shown << " items"; + if (Shown < All) + Printer << ", " << (All - Shown) << " filtered"; + Printer << ")"; + Printer.Indent(); + + // If we pre-computed, iterate the filtered/sorted list, otherwise iterate + // the DIA enumerator and filter on the fly. + if (Precompute) { + for (auto &Class : Filtered) + dumpClassLayout(*Class); + } else { + while (auto Class = Classes->getNext()) { + if (Printer.IsTypeExcluded(Class->getName(), Class->getLength())) + continue; + + auto Layout = llvm::make_unique<ClassLayout>(std::move(Class)); + if (Layout->deepPaddingSize() < opts::pretty::PaddingThreshold) + continue; + + dumpClassLayout(*Layout); + } + } + + Printer.Unindent(); + } } void TypeDumper::dump(const PDBSymbolTypeEnum &Symbol) { - if (Symbol.getUnmodifiedTypeId() != 0) - return; - if (Printer.IsTypeExcluded(Symbol.getName())) + assert(opts::pretty::Enums); + + if (Printer.IsTypeExcluded(Symbol.getName(), Symbol.getLength())) return; // Dump member enums when dumping their class definition. if (nullptr != Symbol.getClassParent()) @@ -72,7 +193,9 @@ void TypeDumper::dump(const PDBSymbolTypeEnum &Symbol) { } void TypeDumper::dump(const PDBSymbolTypeTypedef &Symbol) { - if (Printer.IsTypeExcluded(Symbol.getName())) + assert(opts::pretty::Typedefs); + + if (Printer.IsTypeExcluded(Symbol.getName(), Symbol.getLength())) return; Printer.NewLine(); @@ -80,19 +203,15 @@ void TypeDumper::dump(const PDBSymbolTypeTypedef &Symbol) { Dumper.start(Symbol); } -void TypeDumper::dump(const PDBSymbolTypeUDT &Symbol) { - if (Symbol.getUnmodifiedTypeId() != 0) - return; - if (Printer.IsTypeExcluded(Symbol.getName())) - return; - - Printer.NewLine(); +void TypeDumper::dumpClassLayout(const ClassLayout &Class) { + assert(opts::pretty::Classes); - if (opts::pretty::NoClassDefs) { + if (opts::pretty::ClassFormat == opts::pretty::ClassDefinitionFormat::None) { + Printer.NewLine(); WithColor(Printer, PDB_ColorItem::Keyword).get() << "class "; - WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << Class.getUDTName(); } else { ClassDefinitionDumper Dumper(Printer); - Dumper.start(Symbol); + Dumper.start(Class); } } diff --git a/tools/llvm-pdbdump/PrettyTypeDumper.h b/tools/llvm-pdbdump/PrettyTypeDumper.h index f9d8304c3208e..68a2f0246ebae 100644 --- a/tools/llvm-pdbdump/PrettyTypeDumper.h +++ b/tools/llvm-pdbdump/PrettyTypeDumper.h @@ -15,6 +15,7 @@ namespace llvm { namespace pdb { class LinePrinter; +class ClassLayout; class TypeDumper : public PDBSymDumper { public: @@ -24,7 +25,8 @@ public: void dump(const PDBSymbolTypeEnum &Symbol) override; void dump(const PDBSymbolTypeTypedef &Symbol) override; - void dump(const PDBSymbolTypeUDT &Symbol) override; + + void dumpClassLayout(const ClassLayout &Class); private: LinePrinter &Printer; diff --git a/tools/llvm-pdbdump/PrettyTypedefDumper.cpp b/tools/llvm-pdbdump/PrettyTypedefDumper.cpp index c458755cb7806..2d8e915d76043 100644 --- a/tools/llvm-pdbdump/PrettyTypedefDumper.cpp +++ b/tools/llvm-pdbdump/PrettyTypedefDumper.cpp @@ -53,11 +53,8 @@ void TypedefDumper::dump(const PDBSymbolTypePointer &Symbol) { WithColor(Printer, PDB_ColorItem::Keyword).get() << "const "; if (Symbol.isVolatileType()) WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile "; - uint32_t PointeeId = Symbol.getTypeId(); - auto PointeeType = Symbol.getSession().getSymbolById(PointeeId); - if (!PointeeType) - return; - if (auto FuncSig = dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType.get())) { + auto PointeeType = Symbol.getPointeeType(); + if (auto FuncSig = unique_dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType)) { FunctionDumper::PointerType Pointer = FunctionDumper::PointerType::Pointer; if (Symbol.isReference()) Pointer = FunctionDumper::PointerType::Reference; diff --git a/tools/llvm-pdbdump/PrettyVariableDumper.cpp b/tools/llvm-pdbdump/PrettyVariableDumper.cpp index e1469186ad8b5..76a0d23bf87a4 100644 --- a/tools/llvm-pdbdump/PrettyVariableDumper.cpp +++ b/tools/llvm-pdbdump/PrettyVariableDumper.cpp @@ -14,6 +14,7 @@ #include "PrettyFunctionDumper.h" #include "llvm-pdbdump.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" #include "llvm/DebugInfo/PDB/PDBSymbolData.h" #include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h" @@ -23,16 +24,18 @@ #include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" #include "llvm/Support/Format.h" using namespace llvm; +using namespace llvm::codeview; using namespace llvm::pdb; VariableDumper::VariableDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {} -void VariableDumper::start(const PDBSymbolData &Var) { +void VariableDumper::start(const PDBSymbolData &Var, uint32_t Offset) { if (Var.isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated) return; if (Printer.IsSymbolExcluded(Var.getName())) @@ -40,13 +43,15 @@ void VariableDumper::start(const PDBSymbolData &Var) { auto VarType = Var.getType(); + uint64_t Length = VarType->getRawSymbol().getLength(); + switch (auto LocType = Var.getLocationType()) { case PDB_LocType::Static: Printer.NewLine(); Printer << "data ["; WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(Var.getVirtualAddress(), 10); - Printer << "] "; + Printer << ", sizeof=" << Length << "] "; WithColor(Printer, PDB_ColorItem::Keyword).get() << "static "; dumpSymbolTypeAndName(*VarType, Var.getName()); break; @@ -54,8 +59,7 @@ void VariableDumper::start(const PDBSymbolData &Var) { if (isa<PDBSymbolTypeEnum>(*VarType)) break; Printer.NewLine(); - Printer << "data "; - WithColor(Printer, PDB_ColorItem::Keyword).get() << "const "; + Printer << "data [sizeof=" << Length << "] "; dumpSymbolTypeAndName(*VarType, Var.getName()); Printer << " = "; WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Var.getValue(); @@ -64,27 +68,57 @@ void VariableDumper::start(const PDBSymbolData &Var) { Printer.NewLine(); Printer << "data "; WithColor(Printer, PDB_ColorItem::Offset).get() - << "+" << format_hex(Var.getOffset(), 4) << " "; + << "+" << format_hex(Offset + Var.getOffset(), 4) + << " [sizeof=" << Length << "] "; dumpSymbolTypeAndName(*VarType, Var.getName()); break; case PDB_LocType::BitField: Printer.NewLine(); Printer << "data "; WithColor(Printer, PDB_ColorItem::Offset).get() - << "+" << format_hex(Var.getOffset(), 4) << " "; + << "+" << format_hex(Offset + Var.getOffset(), 4) + << " [sizeof=" << Length << "] "; dumpSymbolTypeAndName(*VarType, Var.getName()); Printer << " : "; WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Var.getLength(); break; default: Printer.NewLine(); - Printer << "data "; + Printer << "data [sizeof=" << Length << "] "; Printer << "unknown(" << LocType << ") "; WithColor(Printer, PDB_ColorItem::Identifier).get() << Var.getName(); break; } } +void VariableDumper::start(const PDBSymbolTypeVTable &Var, uint32_t Offset) { + Printer.NewLine(); + Printer << "vfptr "; + auto VTableType = cast<PDBSymbolTypePointer>(Var.getType()); + uint32_t PointerSize = VTableType->getLength(); + + WithColor(Printer, PDB_ColorItem::Offset).get() + << "+" << format_hex(Offset + Var.getOffset(), 4) + << " [sizeof=" << PointerSize << "] "; +} + +void VariableDumper::dump(const PDBSymbolTypeArray &Symbol) { + auto ElementType = Symbol.getElementType(); + assert(ElementType); + if (!ElementType) + return; + ElementType->dump(*this); +} + +void VariableDumper::dumpRight(const PDBSymbolTypeArray &Symbol) { + auto ElementType = Symbol.getElementType(); + assert(ElementType); + if (!ElementType) + return; + Printer << '[' << Symbol.getCount() << ']'; + ElementType->dumpRight(*this); +} + void VariableDumper::dump(const PDBSymbolTypeBuiltin &Symbol) { BuiltinDumper Dumper(Printer); Dumper.start(Symbol); @@ -94,27 +128,71 @@ void VariableDumper::dump(const PDBSymbolTypeEnum &Symbol) { WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); } -void VariableDumper::dump(const PDBSymbolTypeFunctionSig &Symbol) {} +void VariableDumper::dump(const PDBSymbolTypeFunctionSig &Symbol) { + auto ReturnType = Symbol.getReturnType(); + ReturnType->dump(*this); + Printer << " "; + + uint32_t ClassParentId = Symbol.getClassParentId(); + auto ClassParent = + Symbol.getSession().getConcreteSymbolById<PDBSymbolTypeUDT>( + ClassParentId); + + if (ClassParent) { + WithColor(Printer, PDB_ColorItem::Identifier).get() + << ClassParent->getName(); + Printer << "::"; + } +} + +void VariableDumper::dumpRight(const PDBSymbolTypeFunctionSig &Symbol) { + Printer << "("; + if (auto Arguments = Symbol.getArguments()) { + uint32_t Index = 0; + while (auto Arg = Arguments->getNext()) { + Arg->dump(*this); + if (++Index < Arguments->getChildCount()) + Printer << ", "; + } + } + Printer << ")"; + + if (Symbol.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " const"; + if (Symbol.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile"; +} void VariableDumper::dump(const PDBSymbolTypePointer &Symbol) { auto PointeeType = Symbol.getPointeeType(); if (!PointeeType) return; + PointeeType->dump(*this); + if (auto FuncSig = unique_dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType)) { + // A hack to get the calling convention in the right spot. + Printer << " ("; + PDB_CallingConv CC = FuncSig->getCallingConvention(); + WithColor(Printer, PDB_ColorItem::Keyword).get() << CC << " "; + } else if (isa<PDBSymbolTypeArray>(PointeeType)) { + Printer << " ("; + } + Printer << (Symbol.isReference() ? "&" : "*"); + if (Symbol.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " const "; + if (Symbol.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile "; +} - if (auto Func = dyn_cast<PDBSymbolFunc>(PointeeType.get())) { - FunctionDumper NestedDumper(Printer); - FunctionDumper::PointerType Pointer = - Symbol.isReference() ? FunctionDumper::PointerType::Reference - : FunctionDumper::PointerType::Pointer; - NestedDumper.start(*Func, Pointer); - } else { - if (Symbol.isConstType()) - WithColor(Printer, PDB_ColorItem::Keyword).get() << "const "; - if (Symbol.isVolatileType()) - WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile "; - PointeeType->dump(*this); - Printer << (Symbol.isReference() ? "&" : "*"); +void VariableDumper::dumpRight(const PDBSymbolTypePointer &Symbol) { + auto PointeeType = Symbol.getPointeeType(); + assert(PointeeType); + if (!PointeeType) + return; + if (isa<PDBSymbolTypeFunctionSig>(PointeeType) || + isa<PDBSymbolTypeArray>(PointeeType)) { + Printer << ")"; } + PointeeType->dumpRight(*this); } void VariableDumper::dump(const PDBSymbolTypeTypedef &Symbol) { @@ -128,44 +206,7 @@ void VariableDumper::dump(const PDBSymbolTypeUDT &Symbol) { void VariableDumper::dumpSymbolTypeAndName(const PDBSymbol &Type, StringRef Name) { - if (auto *ArrayType = dyn_cast<PDBSymbolTypeArray>(&Type)) { - std::string IndexSpec; - raw_string_ostream IndexStream(IndexSpec); - std::unique_ptr<PDBSymbol> ElementType = ArrayType->getElementType(); - while (auto NestedArray = dyn_cast<PDBSymbolTypeArray>(ElementType.get())) { - IndexStream << "["; - IndexStream << NestedArray->getCount(); - IndexStream << "]"; - ElementType = NestedArray->getElementType(); - } - IndexStream << "[" << ArrayType->getCount() << "]"; - ElementType->dump(*this); - WithColor(Printer, PDB_ColorItem::Identifier).get() << " " << Name; - Printer << IndexStream.str(); - } else { - if (!tryDumpFunctionPointer(Type, Name)) { - Type.dump(*this); - WithColor(Printer, PDB_ColorItem::Identifier).get() << " " << Name; - } - } -} - -bool VariableDumper::tryDumpFunctionPointer(const PDBSymbol &Type, - StringRef Name) { - // Function pointers come across as pointers to function signatures. But the - // signature carries no name, so we have to handle this case separately. - if (auto *PointerType = dyn_cast<PDBSymbolTypePointer>(&Type)) { - auto PointeeType = PointerType->getPointeeType(); - if (auto *FunctionSig = - dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType.get())) { - FunctionDumper Dumper(Printer); - FunctionDumper::PointerType PT = FunctionDumper::PointerType::Pointer; - if (PointerType->isReference()) - PT = FunctionDumper::PointerType::Reference; - std::string NameStr(Name.begin(), Name.end()); - Dumper.start(*FunctionSig, NameStr.c_str(), PT); - return true; - } - } - return false; + Type.dump(*this); + WithColor(Printer, PDB_ColorItem::Identifier).get() << " " << Name; + Type.dumpRight(*this); } diff --git a/tools/llvm-pdbdump/PrettyVariableDumper.h b/tools/llvm-pdbdump/PrettyVariableDumper.h index a122bb86058cf..4ba3bc97d85b6 100644 --- a/tools/llvm-pdbdump/PrettyVariableDumper.h +++ b/tools/llvm-pdbdump/PrettyVariableDumper.h @@ -24,8 +24,10 @@ class VariableDumper : public PDBSymDumper { public: VariableDumper(LinePrinter &P); - void start(const PDBSymbolData &Var); + void start(const PDBSymbolData &Var, uint32_t Offset = 0); + void start(const PDBSymbolTypeVTable &Var, uint32_t Offset = 0); + void dump(const PDBSymbolTypeArray &Symbol) override; void dump(const PDBSymbolTypeBuiltin &Symbol) override; void dump(const PDBSymbolTypeEnum &Symbol) override; void dump(const PDBSymbolTypeFunctionSig &Symbol) override; @@ -33,9 +35,12 @@ public: void dump(const PDBSymbolTypeTypedef &Symbol) override; void dump(const PDBSymbolTypeUDT &Symbol) override; + void dumpRight(const PDBSymbolTypeArray &Symbol) override; + void dumpRight(const PDBSymbolTypeFunctionSig &Symbol) override; + void dumpRight(const PDBSymbolTypePointer &Symbol) override; + private: void dumpSymbolTypeAndName(const PDBSymbol &Type, StringRef Name); - bool tryDumpFunctionPointer(const PDBSymbol &Type, StringRef Name); LinePrinter &Printer; }; diff --git a/tools/llvm-pdbdump/StreamUtil.cpp b/tools/llvm-pdbdump/StreamUtil.cpp new file mode 100644 index 0000000000000..db1e01aa01543 --- /dev/null +++ b/tools/llvm-pdbdump/StreamUtil.cpp @@ -0,0 +1,136 @@ +//===- StreamUtil.cpp - PDB stream utilities --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "StreamUtil.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/ModInfo.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" + +namespace llvm { +namespace pdb { +void discoverStreamPurposes(PDBFile &File, + SmallVectorImpl<std::string> &Purposes) { + + // It's OK if we fail to load some of these streams, we still attempt to print + // what we can. + auto Dbi = File.getPDBDbiStream(); + auto Tpi = File.getPDBTpiStream(); + auto Ipi = File.getPDBIpiStream(); + auto Info = File.getPDBInfoStream(); + + uint32_t StreamCount = File.getNumStreams(); + DenseMap<uint16_t, const ModuleInfoEx *> ModStreams; + DenseMap<uint16_t, std::string> NamedStreams; + + if (Dbi) { + for (auto &ModI : Dbi->modules()) { + uint16_t SN = ModI.Info.getModuleStreamIndex(); + if (SN != kInvalidStreamIndex) + ModStreams[SN] = &ModI; + } + } + if (Info) { + for (auto &NSE : Info->named_streams()) { + if (NSE.second != kInvalidStreamIndex) + NamedStreams[NSE.second] = NSE.first(); + } + } + + Purposes.resize(StreamCount); + for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { + std::string Value; + if (StreamIdx == OldMSFDirectory) + Value = "Old MSF Directory"; + else if (StreamIdx == StreamPDB) + Value = "PDB Stream"; + else if (StreamIdx == StreamDBI) + Value = "DBI Stream"; + else if (StreamIdx == StreamTPI) + Value = "TPI Stream"; + else if (StreamIdx == StreamIPI) + Value = "IPI Stream"; + else if (Dbi && StreamIdx == Dbi->getGlobalSymbolStreamIndex()) + Value = "Global Symbol Hash"; + else if (Dbi && StreamIdx == Dbi->getPublicSymbolStreamIndex()) + Value = "Public Symbol Hash"; + else if (Dbi && StreamIdx == Dbi->getSymRecordStreamIndex()) + Value = "Public Symbol Records"; + else if (Tpi && StreamIdx == Tpi->getTypeHashStreamIndex()) + Value = "TPI Hash"; + else if (Tpi && StreamIdx == Tpi->getTypeHashStreamAuxIndex()) + Value = "TPI Aux Hash"; + else if (Ipi && StreamIdx == Ipi->getTypeHashStreamIndex()) + Value = "IPI Hash"; + else if (Ipi && StreamIdx == Ipi->getTypeHashStreamAuxIndex()) + Value = "IPI Aux Hash"; + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Exception)) + Value = "Exception Data"; + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Fixup)) + Value = "Fixup Data"; + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::FPO)) + Value = "FPO Data"; + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::NewFPO)) + Value = "New FPO Data"; + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapFromSrc)) + Value = "Omap From Source Data"; + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapToSrc)) + Value = "Omap To Source Data"; + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Pdata)) + Value = "Pdata"; + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdr)) + Value = "Section Header Data"; + else if (Dbi && + StreamIdx == + Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdrOrig)) + Value = "Section Header Original Data"; + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::TokenRidMap)) + Value = "Token Rid Data"; + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Xdata)) + Value = "Xdata"; + else { + auto ModIter = ModStreams.find(StreamIdx); + auto NSIter = NamedStreams.find(StreamIdx); + if (ModIter != ModStreams.end()) { + Value = "Module \""; + Value += ModIter->second->Info.getModuleName().str(); + Value += "\""; + } else if (NSIter != NamedStreams.end()) { + Value = "Named Stream \""; + Value += NSIter->second; + Value += "\""; + } else { + Value = "???"; + } + } + Purposes[StreamIdx] = Value; + } + + // Consume errors from missing streams. + if (!Dbi) + consumeError(Dbi.takeError()); + if (!Tpi) + consumeError(Tpi.takeError()); + if (!Ipi) + consumeError(Ipi.takeError()); + if (!Info) + consumeError(Info.takeError()); +} +} +} diff --git a/tools/llvm-pdbdump/StreamUtil.h b/tools/llvm-pdbdump/StreamUtil.h new file mode 100644 index 0000000000000..b5c0beba44fed --- /dev/null +++ b/tools/llvm-pdbdump/StreamUtil.h @@ -0,0 +1,25 @@ +//===- Streamutil.h - PDB stream utilities ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_STREAMUTIL_H +#define LLVM_TOOLS_LLVMPDBDUMP_STREAMUTIL_H + +#include "llvm/ADT/SmallVector.h" + +#include <string> + +namespace llvm { +namespace pdb { +class PDBFile; +void discoverStreamPurposes(PDBFile &File, + SmallVectorImpl<std::string> &Purposes); +} +} + +#endif diff --git a/tools/llvm-pdbdump/YAMLOutputStyle.cpp b/tools/llvm-pdbdump/YAMLOutputStyle.cpp index 3f2733d701a85..5b53d2137166a 100644 --- a/tools/llvm-pdbdump/YAMLOutputStyle.cpp +++ b/tools/llvm-pdbdump/YAMLOutputStyle.cpp @@ -13,18 +13,20 @@ #include "llvm-pdbdump.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" -#include "llvm/DebugInfo/PDB/Raw/DbiStream.h" -#include "llvm/DebugInfo/PDB/Raw/InfoStream.h" -#include "llvm/DebugInfo/PDB/Raw/ModStream.h" -#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" -#include "llvm/DebugInfo/PDB/Raw/RawConstants.h" -#include "llvm/DebugInfo/PDB/Raw/TpiStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/ModStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" using namespace llvm; using namespace llvm::pdb; YAMLOutputStyle::YAMLOutputStyle(PDBFile &File) - : File(File), Out(outs()), Obj(File.getAllocator()) {} + : File(File), Out(outs()), Obj(File.getAllocator()) { + Out.setWriteDefaultValues(!opts::pdb2yaml::Minimal); +} Error YAMLOutputStyle::dump() { if (opts::pdb2yaml::StreamDirectory) @@ -45,6 +47,9 @@ Error YAMLOutputStyle::dump() { if (auto EC = dumpStreamDirectory()) return EC; + if (auto EC = dumpStringTable()) + return EC; + if (auto EC = dumpPDBStream()) return EC; @@ -83,6 +88,24 @@ Error YAMLOutputStyle::dumpFileHeaders() { return Error::success(); } +Error YAMLOutputStyle::dumpStringTable() { + if (!opts::pdb2yaml::StringTable) + return Error::success(); + + Obj.StringTable.emplace(); + auto ExpectedST = File.getStringTable(); + if (!ExpectedST) + return ExpectedST.takeError(); + + const auto &ST = ExpectedST.get(); + for (auto ID : ST.name_ids()) { + StringRef S = ST.getStringForID(ID); + if (!S.empty()) + Obj.StringTable->push_back(S); + } + return Error::success(); +} + Error YAMLOutputStyle::dumpStreamMetadata() { if (!opts::pdb2yaml::StreamMetadata) return Error::success(); @@ -122,12 +145,7 @@ Error YAMLOutputStyle::dumpPDBStream() { Obj.PdbStream->Guid = InfoS.getGuid(); Obj.PdbStream->Signature = InfoS.getSignature(); Obj.PdbStream->Version = InfoS.getVersion(); - for (auto &NS : InfoS.named_streams()) { - yaml::NamedStreamMapping Mapping; - Mapping.StreamName = NS.getKey(); - Mapping.StreamNumber = NS.getValue(); - Obj.PdbStream->NamedStreams.push_back(Mapping); - } + Obj.PdbStream->Features = InfoS.getFeatureSignatures(); return Error::success(); } diff --git a/tools/llvm-pdbdump/YAMLOutputStyle.h b/tools/llvm-pdbdump/YAMLOutputStyle.h index 3cd603a95b6a3..db9868db4a7e1 100644 --- a/tools/llvm-pdbdump/YAMLOutputStyle.h +++ b/tools/llvm-pdbdump/YAMLOutputStyle.h @@ -26,6 +26,7 @@ public: Error dump() override; private: + Error dumpStringTable(); Error dumpFileHeaders(); Error dumpStreamMetadata(); Error dumpStreamDirectory(); diff --git a/tools/llvm-pdbdump/YamlSymbolDumper.cpp b/tools/llvm-pdbdump/YamlSymbolDumper.cpp index 210260a03b5fc..431bf404fb040 100644 --- a/tools/llvm-pdbdump/YamlSymbolDumper.cpp +++ b/tools/llvm-pdbdump/YamlSymbolDumper.cpp @@ -113,6 +113,7 @@ template <> struct ScalarEnumerationTraits<RegisterId> { for (const auto &E : RegNames) { io.enumCase(Reg, E.Name.str().c_str(), static_cast<RegisterId>(E.Value)); } + io.enumFallback<Hex16>(Reg); } }; diff --git a/tools/llvm-pdbdump/YamlTypeDumper.cpp b/tools/llvm-pdbdump/YamlTypeDumper.cpp index 5c527c71c7e44..b4eb197e866a4 100644 --- a/tools/llvm-pdbdump/YamlTypeDumper.cpp +++ b/tools/llvm-pdbdump/YamlTypeDumper.cpp @@ -17,7 +17,7 @@ #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeSerializer.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" -#include "llvm/DebugInfo/PDB/Raw/TpiHashing.h" +#include "llvm/DebugInfo/PDB/Native/TpiHashing.h" using namespace llvm; using namespace llvm::codeview; @@ -194,6 +194,13 @@ template <> struct ScalarEnumerationTraits<WindowsRTClassKind> { } }; +template <> struct ScalarEnumerationTraits<LabelType> { + static void enumeration(IO &IO, LabelType &Value) { + IO.enumCase(Value, "Near", LabelType::Near); + IO.enumCase(Value, "Far", LabelType::Far); + } +}; + template <> struct ScalarBitSetTraits<PointerOptions> { static void bitset(IO &IO, PointerOptions &Options) { IO.bitSetCase(Options, "None", PointerOptions::None); @@ -291,7 +298,11 @@ void MappingTraits<StringIdRecord>::mapping(IO &IO, StringIdRecord &String) { } void MappingTraits<ArgListRecord>::mapping(IO &IO, ArgListRecord &Args) { - IO.mapRequired("ArgIndices", Args.StringIndices); + IO.mapRequired("ArgIndices", Args.ArgIndices); +} + +void MappingTraits<StringListRecord>::mapping(IO &IO, StringListRecord &Strings) { + IO.mapRequired("StringIndices", Strings.StringIndices); } void MappingTraits<ClassRecord>::mapping(IO &IO, ClassRecord &Class) { @@ -427,6 +438,10 @@ void MappingTraits<BuildInfoRecord>::mapping(IO &IO, BuildInfoRecord &Args) { IO.mapRequired("ArgIndices", Args.ArgIndices); } +void MappingTraits<LabelRecord>::mapping(IO &IO, LabelRecord &R) { + IO.mapRequired("Mode", R.Mode); +} + void MappingTraits<NestedTypeRecord>::mapping(IO &IO, NestedTypeRecord &Nested) { IO.mapRequired("Type", Nested.Type); @@ -573,8 +588,8 @@ struct MappingContextTraits<pdb::yaml::PdbTpiFieldListRecord, assert(IO.outputting()); codeview::TypeVisitorCallbackPipeline Pipeline; - msf::ByteStream Data(Obj.Record.Data); - msf::StreamReader FieldReader(Data); + BinaryByteStream Data(Obj.Record.Data, llvm::support::little); + BinaryStreamReader FieldReader(Data); codeview::FieldListDeserializer Deserializer(FieldReader); // For PDB to Yaml, deserialize into a high level record type, then dump diff --git a/tools/llvm-pdbdump/fuzzer/llvm-pdbdump-fuzzer.cpp b/tools/llvm-pdbdump/fuzzer/llvm-pdbdump-fuzzer.cpp index e818dda32fc02..38eaf16c65b05 100644 --- a/tools/llvm-pdbdump/fuzzer/llvm-pdbdump-fuzzer.cpp +++ b/tools/llvm-pdbdump/fuzzer/llvm-pdbdump-fuzzer.cpp @@ -13,7 +13,7 @@ /// //===----------------------------------------------------------------------===// #include "llvm/ADT/STLExtras.h" -#include "llvm/DebugInfo/CodeView/ByteStream.h" +#include "llvm/DebugInfo/CodeView/BinaryByteStream.h" #include "llvm/DebugInfo/CodeView/SymbolDumper.h" #include "llvm/DebugInfo/CodeView/TypeDumper.h" #include "llvm/DebugInfo/PDB/Raw/DbiStream.h" @@ -28,14 +28,15 @@ using namespace llvm; namespace { -// We need a class which behaves like an immutable ByteStream, but whose data +// We need a class which behaves like an immutable BinaryByteStream, but whose +// data // is backed by an llvm::MemoryBuffer. It also needs to own the underlying // MemoryBuffer, so this simple adapter is a good way to achieve that. -class InputByteStream : public codeview::ByteStream<false> { +class InputByteStream : public codeview::BinaryByteStream<false> { public: explicit InputByteStream(std::unique_ptr<MemoryBuffer> Buffer) - : ByteStream(ArrayRef<uint8_t>(Buffer->getBuffer().bytes_begin(), - Buffer->getBuffer().bytes_end())), + : BinaryByteStream(ArrayRef<uint8_t>(Buffer->getBuffer().bytes_begin(), + Buffer->getBuffer().bytes_end())), MemBuffer(std::move(Buffer)) {} std::unique_ptr<MemoryBuffer> MemBuffer; diff --git a/tools/llvm-pdbdump/llvm-pdbdump.cpp b/tools/llvm-pdbdump/llvm-pdbdump.cpp index d3495e524abcf..06c2afc0bc78a 100644 --- a/tools/llvm-pdbdump/llvm-pdbdump.cpp +++ b/tools/llvm-pdbdump/llvm-pdbdump.cpp @@ -7,13 +7,14 @@ // //===----------------------------------------------------------------------===// // -// Dumps debug information present in PDB files. This utility makes use of -// the Microsoft Windows SDK, so will not compile or run on non-Windows -// platforms. +// Dumps debug information present in PDB files. // //===----------------------------------------------------------------------===// #include "llvm-pdbdump.h" + +#include "Analyze.h" +#include "Diff.h" #include "LLVMOutputStyle.h" #include "LinePrinter.h" #include "OutputStyle.h" @@ -29,29 +30,31 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Config/config.h" -#include "llvm/DebugInfo/MSF/ByteStream.h" #include "llvm/DebugInfo/MSF/MSFBuilder.h" #include "llvm/DebugInfo/PDB/GenericError.h" #include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" #include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" #include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/ModInfoBuilder.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/StringTableBuilder.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h" #include "llvm/DebugInfo/PDB/PDB.h" #include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" #include "llvm/DebugInfo/PDB/PDBSymbolData.h" #include "llvm/DebugInfo/PDB/PDBSymbolExe.h" #include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" #include "llvm/DebugInfo/PDB/PDBSymbolThunk.h" -#include "llvm/DebugInfo/PDB/Raw/DbiStream.h" -#include "llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h" -#include "llvm/DebugInfo/PDB/Raw/InfoStream.h" -#include "llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h" -#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" -#include "llvm/DebugInfo/PDB/Raw/PDBFileBuilder.h" -#include "llvm/DebugInfo/PDB/Raw/RawConstants.h" -#include "llvm/DebugInfo/PDB/Raw/RawError.h" -#include "llvm/DebugInfo/PDB/Raw/RawSession.h" -#include "llvm/DebugInfo/PDB/Raw/TpiStream.h" -#include "llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h" +#include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/COM.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ConvertUTF.h" @@ -78,6 +81,9 @@ cl::SubCommand RawSubcommand("raw", "Dump raw structure of the PDB file"); cl::SubCommand PrettySubcommand("pretty", "Dump semantic information about types and symbols"); + +cl::SubCommand DiffSubcommand("diff", "Diff the contents of 2 PDB files"); + cl::SubCommand YamlToPdbSubcommand("yaml2pdb", "Generate a PDB file from a YAML description"); @@ -85,8 +91,12 @@ cl::SubCommand PdbToYamlSubcommand("pdb2yaml", "Generate a detailed YAML description of a PDB File"); +cl::SubCommand + AnalyzeSubcommand("analyze", + "Analyze various aspects of a PDB's structure"); + cl::OptionCategory TypeCategory("Symbol Type Options"); -cl::OptionCategory FilterCategory("Filtering Options"); +cl::OptionCategory FilterCategory("Filtering and Sorting Options"); cl::OptionCategory OtherOptions("Other Options"); namespace pretty { @@ -102,8 +112,41 @@ cl::opt<bool> Globals("globals", cl::desc("Dump global symbols"), cl::cat(TypeCategory), cl::sub(PrettySubcommand)); cl::opt<bool> Externals("externals", cl::desc("Dump external symbols"), cl::cat(TypeCategory), cl::sub(PrettySubcommand)); -cl::opt<bool> Types("types", cl::desc("Display types"), cl::cat(TypeCategory), - cl::sub(PrettySubcommand)); +cl::opt<bool> + Types("types", + cl::desc("Display all types (implies -classes, -enums, -typedefs)"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt<bool> Classes("classes", cl::desc("Display class types"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt<bool> Enums("enums", cl::desc("Display enum types"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt<bool> Typedefs("typedefs", cl::desc("Display typedef types"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt<ClassSortMode> ClassOrder( + "class-order", cl::desc("Class sort order"), cl::init(ClassSortMode::None), + cl::values(clEnumValN(ClassSortMode::None, "none", + "Undefined / no particular sort order"), + clEnumValN(ClassSortMode::Name, "name", "Sort classes by name"), + clEnumValN(ClassSortMode::Size, "size", "Sort classes by size"), + clEnumValN(ClassSortMode::Padding, "padding", + "Sort classes by amount of padding")), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); + +cl::opt<ClassDefinitionFormat> ClassFormat( + "class-definitions", cl::desc("Class definition format"), + cl::init(ClassDefinitionFormat::Standard), + cl::values( + clEnumValN(ClassDefinitionFormat::Standard, "all-members", + "Display all class members including data, constants, " + "typedefs, functions, etc"), + clEnumValN(ClassDefinitionFormat::Layout, "layout-members", + "Only display members that contribute to class size."), + clEnumValN(ClassDefinitionFormat::Graphical, "graphical", + "Display graphical representation of each class's layout."), + clEnumValN(ClassDefinitionFormat::None, "none", + "Don't display class definitions")), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); + cl::opt<bool> Lines("lines", cl::desc("Line tables"), cl::cat(TypeCategory), cl::sub(PrettySubcommand)); cl::opt<bool> @@ -114,6 +157,12 @@ cl::opt<uint64_t> LoadAddress( "load-address", cl::desc("Assume the module is loaded at the specified address"), cl::cat(OtherOptions), cl::sub(PrettySubcommand)); +cl::opt<bool> Native("native", cl::desc("Use native PDB reader instead of DIA"), + cl::cat(OtherOptions), cl::sub(PrettySubcommand)); +cl::opt<cl::boolOrDefault> + ColorOutput("color-output", + cl::desc("Override use of color (default = isatty)"), + cl::cat(OtherOptions), cl::sub(PrettySubcommand)); cl::list<std::string> ExcludeTypes( "exclude-types", cl::desc("Exclude types by regular expression"), cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand)); @@ -136,6 +185,14 @@ cl::list<std::string> IncludeCompilands( "include-compilands", cl::desc("Include only compilands those which match a regular expression"), cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +cl::opt<uint32_t> SizeThreshold( + "min-type-size", cl::desc("Displays only those types which are greater " + "than or equal to the specified size."), + cl::init(0), cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +cl::opt<uint32_t> PaddingThreshold( + "min-class-padding", cl::desc("Displays only those classes which have at " + "least the specified amount of padding."), + cl::init(0), cl::cat(FilterCategory), cl::sub(PrettySubcommand)); cl::opt<bool> ExcludeCompilerGenerated( "no-compiler-generated", @@ -145,14 +202,23 @@ cl::opt<bool> ExcludeSystemLibraries("no-system-libs", cl::desc("Don't show symbols from system libraries"), cl::cat(FilterCategory), cl::sub(PrettySubcommand)); -cl::opt<bool> NoClassDefs("no-class-definitions", - cl::desc("Don't display full class definitions"), - cl::cat(FilterCategory), cl::sub(PrettySubcommand)); + cl::opt<bool> NoEnumDefs("no-enum-definitions", cl::desc("Don't display full enum definitions"), cl::cat(FilterCategory), cl::sub(PrettySubcommand)); } +namespace diff { +cl::opt<bool> Pedantic("pedantic", + cl::desc("Finds all differences (even structural ones " + "that produce otherwise identical PDBs)"), + cl::sub(DiffSubcommand)); + +cl::list<std::string> InputFilenames(cl::Positional, + cl::desc("<first> <second>"), + cl::OneOrMore, cl::sub(DiffSubcommand)); +} + namespace raw { cl::OptionCategory MsfOptions("MSF Container Options"); @@ -187,6 +253,11 @@ cl::list<uint32_t> // TYPE OPTIONS cl::opt<bool> + CompactRecords("compact-records", + cl::desc("Dump type and symbol records with less detail"), + cl::cat(TypeOptions), cl::sub(RawSubcommand)); + +cl::opt<bool> DumpTpiRecords("tpi-records", cl::desc("dump CodeView type records from TPI stream"), cl::cat(TypeOptions), cl::sub(RawSubcommand)); @@ -227,6 +298,9 @@ cl::opt<bool> cl::cat(SymbolOptions), cl::sub(RawSubcommand)); // MISCELLANEOUS OPTIONS +cl::opt<bool> DumpStringTable("string-table", cl::desc("dump PDB String Table"), + cl::cat(MiscOptions), cl::sub(RawSubcommand)); + cl::opt<bool> DumpSectionContribs("section-contribs", cl::desc("dump section contributions"), cl::cat(MiscOptions), cl::sub(RawSubcommand)); @@ -262,6 +336,9 @@ cl::opt<bool> cl::desc("Do not dump MSF file headers (you will not be able " "to generate a fresh PDB from the resulting YAML)"), cl::sub(PdbToYamlSubcommand), cl::init(false)); +cl::opt<bool> Minimal("minimal", + cl::desc("Don't write fields with default values"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); cl::opt<bool> StreamMetadata( "stream-metadata", @@ -274,6 +351,10 @@ cl::opt<bool> StreamDirectory( cl::opt<bool> PdbStream("pdb-stream", cl::desc("Dump the PDB Stream (Stream 1)"), cl::sub(PdbToYamlSubcommand), cl::init(false)); + +cl::opt<bool> StringTable("string-table", cl::desc("Dump the PDB String Table"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); + cl::opt<bool> DbiStream("dbi-stream", cl::desc("Dump the DBI Stream (Stream 2)"), cl::sub(PdbToYamlSubcommand), cl::init(false)); @@ -305,6 +386,14 @@ cl::list<std::string> InputFilename(cl::Positional, cl::desc("<input PDB file>"), cl::Required, cl::sub(PdbToYamlSubcommand)); } + +namespace analyze { +cl::opt<bool> StringTable("hash-collisions", cl::desc("Find hash collisions"), + cl::sub(AnalyzeSubcommand), cl::init(false)); +cl::list<std::string> InputFilename(cl::Positional, + cl::desc("<input PDB file>"), cl::Required, + cl::sub(AnalyzeSubcommand)); +} } static ExitOnError ExitOnErr; @@ -324,13 +413,13 @@ static void yamlToPdb(StringRef Path) { llvm::yaml::Input In(Buffer->getBuffer()); pdb::yaml::PdbObject YamlObj(Allocator); In >> YamlObj; - if (!YamlObj.Headers.hasValue()) - ExitOnErr(make_error<GenericError>(generic_error_code::unspecified, - "Yaml does not contain MSF headers")); PDBFileBuilder Builder(Allocator); - ExitOnErr(Builder.initialize(YamlObj.Headers->SuperBlock.BlockSize)); + uint32_t BlockSize = 4096; + if (YamlObj.Headers.hasValue()) + BlockSize = YamlObj.Headers->SuperBlock.BlockSize; + ExitOnErr(Builder.initialize(BlockSize)); // Add each of the reserved streams. We ignore stream metadata in the // yaml, because we will reconstruct our own view of the streams. For // example, the YAML may say that there were 20 streams in the original @@ -340,56 +429,74 @@ static void yamlToPdb(StringRef Path) { for (uint32_t I = 0; I < kSpecialStreamCount; ++I) ExitOnErr(Builder.getMsfBuilder().addStream(0)); - if (YamlObj.PdbStream.hasValue()) { - auto &InfoBuilder = Builder.getInfoBuilder(); - InfoBuilder.setAge(YamlObj.PdbStream->Age); - InfoBuilder.setGuid(YamlObj.PdbStream->Guid); - InfoBuilder.setSignature(YamlObj.PdbStream->Signature); - InfoBuilder.setVersion(YamlObj.PdbStream->Version); - for (auto &NM : YamlObj.PdbStream->NamedStreams) - InfoBuilder.getNamedStreamsBuilder().addMapping(NM.StreamName, - NM.StreamNumber); + if (YamlObj.StringTable.hasValue()) { + auto &Strings = Builder.getStringTableBuilder(); + for (auto S : *YamlObj.StringTable) + Strings.insert(S); } - if (YamlObj.DbiStream.hasValue()) { - auto &DbiBuilder = Builder.getDbiBuilder(); - DbiBuilder.setAge(YamlObj.DbiStream->Age); - DbiBuilder.setBuildNumber(YamlObj.DbiStream->BuildNumber); - DbiBuilder.setFlags(YamlObj.DbiStream->Flags); - DbiBuilder.setMachineType(YamlObj.DbiStream->MachineType); - DbiBuilder.setPdbDllRbld(YamlObj.DbiStream->PdbDllRbld); - DbiBuilder.setPdbDllVersion(YamlObj.DbiStream->PdbDllVersion); - DbiBuilder.setVersionHeader(YamlObj.DbiStream->VerHeader); - for (const auto &MI : YamlObj.DbiStream->ModInfos) { - ExitOnErr(DbiBuilder.addModuleInfo(MI.Obj, MI.Mod)); - for (auto S : MI.SourceFiles) - ExitOnErr(DbiBuilder.addModuleSourceFile(MI.Mod, S)); + pdb::yaml::PdbInfoStream DefaultInfoStream; + pdb::yaml::PdbDbiStream DefaultDbiStream; + pdb::yaml::PdbTpiStream DefaultTpiStream; + + const auto &Info = YamlObj.PdbStream.getValueOr(DefaultInfoStream); + + auto &InfoBuilder = Builder.getInfoBuilder(); + InfoBuilder.setAge(Info.Age); + InfoBuilder.setGuid(Info.Guid); + InfoBuilder.setSignature(Info.Signature); + InfoBuilder.setVersion(Info.Version); + for (auto F : Info.Features) + InfoBuilder.addFeature(F); + + const auto &Dbi = YamlObj.DbiStream.getValueOr(DefaultDbiStream); + auto &DbiBuilder = Builder.getDbiBuilder(); + DbiBuilder.setAge(Dbi.Age); + DbiBuilder.setBuildNumber(Dbi.BuildNumber); + DbiBuilder.setFlags(Dbi.Flags); + DbiBuilder.setMachineType(Dbi.MachineType); + DbiBuilder.setPdbDllRbld(Dbi.PdbDllRbld); + DbiBuilder.setPdbDllVersion(Dbi.PdbDllVersion); + DbiBuilder.setVersionHeader(Dbi.VerHeader); + for (const auto &MI : Dbi.ModInfos) { + auto &ModiBuilder = ExitOnErr(DbiBuilder.addModuleInfo(MI.Mod)); + + for (auto S : MI.SourceFiles) + ExitOnErr(DbiBuilder.addModuleSourceFile(MI.Mod, S)); + if (MI.Modi.hasValue()) { + const auto &ModiStream = *MI.Modi; + ModiBuilder.setObjFileName(MI.Obj); + for (auto Symbol : ModiStream.Symbols) + ModiBuilder.addSymbol(Symbol.Record); } } - if (YamlObj.TpiStream.hasValue()) { - auto &TpiBuilder = Builder.getTpiBuilder(); - TpiBuilder.setVersionHeader(YamlObj.TpiStream->Version); - for (const auto &R : YamlObj.TpiStream->Records) - TpiBuilder.addTypeRecord(R.Record); - } + auto &TpiBuilder = Builder.getTpiBuilder(); + const auto &Tpi = YamlObj.TpiStream.getValueOr(DefaultTpiStream); + TpiBuilder.setVersionHeader(Tpi.Version); + for (const auto &R : Tpi.Records) + TpiBuilder.addTypeRecord(R.Record.data(), R.Record.Hash); - if (YamlObj.IpiStream.hasValue()) { - auto &IpiBuilder = Builder.getIpiBuilder(); - IpiBuilder.setVersionHeader(YamlObj.IpiStream->Version); - for (const auto &R : YamlObj.IpiStream->Records) - IpiBuilder.addTypeRecord(R.Record); - } + const auto &Ipi = YamlObj.IpiStream.getValueOr(DefaultTpiStream); + auto &IpiBuilder = Builder.getIpiBuilder(); + IpiBuilder.setVersionHeader(Ipi.Version); + for (const auto &R : Ipi.Records) + TpiBuilder.addTypeRecord(R.Record.data(), R.Record.Hash); ExitOnErr(Builder.commit(opts::yaml2pdb::YamlPdbOutputFile)); } +static PDBFile &loadPDB(StringRef Path, std::unique_ptr<IPDBSession> &Session) { + ExitOnErr(loadDataForPDB(PDB_ReaderType::Native, Path, Session)); + + NativeSession *NS = static_cast<NativeSession *>(Session.get()); + return NS->getPDBFile(); +} + static void pdb2Yaml(StringRef Path) { std::unique_ptr<IPDBSession> Session; - ExitOnErr(loadDataForPDB(PDB_ReaderType::Raw, Path, Session)); + auto &File = loadPDB(Path, Session); - RawSession *RS = static_cast<RawSession *>(Session.get()); - PDBFile &File = RS->getPDBFile(); auto O = llvm::make_unique<YAMLOutputStyle>(File); O = llvm::make_unique<YAMLOutputStyle>(File); @@ -398,24 +505,48 @@ static void pdb2Yaml(StringRef Path) { static void dumpRaw(StringRef Path) { std::unique_ptr<IPDBSession> Session; - ExitOnErr(loadDataForPDB(PDB_ReaderType::Raw, Path, Session)); + auto &File = loadPDB(Path, Session); - RawSession *RS = static_cast<RawSession *>(Session.get()); - PDBFile &File = RS->getPDBFile(); auto O = llvm::make_unique<LLVMOutputStyle>(File); ExitOnErr(O->dump()); } +static void dumpAnalysis(StringRef Path) { + std::unique_ptr<IPDBSession> Session; + auto &File = loadPDB(Path, Session); + auto O = llvm::make_unique<AnalysisStyle>(File); + + ExitOnErr(O->dump()); +} + +static void diff(StringRef Path1, StringRef Path2) { + std::unique_ptr<IPDBSession> Session1; + std::unique_ptr<IPDBSession> Session2; + + auto &File1 = loadPDB(Path1, Session1); + auto &File2 = loadPDB(Path2, Session2); + + auto O = llvm::make_unique<DiffStyle>(File1, File2); + + ExitOnErr(O->dump()); +} + static void dumpPretty(StringRef Path) { std::unique_ptr<IPDBSession> Session; - ExitOnErr(loadDataForPDB(PDB_ReaderType::DIA, Path, Session)); + const auto ReaderType = + opts::pretty::Native ? PDB_ReaderType::Native : PDB_ReaderType::DIA; + ExitOnErr(loadDataForPDB(ReaderType, Path, Session)); if (opts::pretty::LoadAddress) Session->setLoadAddress(opts::pretty::LoadAddress); - LinePrinter Printer(2, outs()); + auto &Stream = outs(); + const bool UseColor = opts::pretty::ColorOutput == cl::BOU_UNSET + ? Stream.has_colors() + : opts::pretty::ColorOutput == cl::BOU_TRUE; + LinePrinter Printer(2, UseColor, Stream); auto GlobalScope(Session->getGlobalScope()); std::string FileName(GlobalScope->getSymbolsFileName()); @@ -465,7 +596,7 @@ static void dumpPretty(StringRef Path) { Printer.Unindent(); } - if (opts::pretty::Types) { + if (opts::pretty::Classes || opts::pretty::Enums || opts::pretty::Typedefs) { Printer.NewLine(); WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---TYPES---"; Printer.Indent(); @@ -556,24 +687,34 @@ int main(int argc_, const char *argv_[]) { } } - if (opts::RawSubcommand && opts::raw::RawAll) { - opts::raw::DumpHeaders = true; - opts::raw::DumpModules = true; - opts::raw::DumpModuleFiles = true; - opts::raw::DumpModuleSyms = true; - opts::raw::DumpGlobals = true; - opts::raw::DumpPublics = true; - opts::raw::DumpSectionHeaders = true; - opts::raw::DumpStreamSummary = true; - opts::raw::DumpPageStats = true; - opts::raw::DumpStreamBlocks = true; - opts::raw::DumpTpiRecords = true; - opts::raw::DumpTpiHash = true; - opts::raw::DumpIpiRecords = true; - opts::raw::DumpSectionMap = true; - opts::raw::DumpSectionContribs = true; - opts::raw::DumpLineInfo = true; - opts::raw::DumpFpo = true; + if (opts::RawSubcommand) { + if (opts::raw::RawAll) { + opts::raw::DumpHeaders = true; + opts::raw::DumpModules = true; + opts::raw::DumpModuleFiles = true; + opts::raw::DumpModuleSyms = true; + opts::raw::DumpGlobals = true; + opts::raw::DumpPublics = true; + opts::raw::DumpSectionHeaders = true; + opts::raw::DumpStreamSummary = true; + opts::raw::DumpPageStats = true; + opts::raw::DumpStreamBlocks = true; + opts::raw::DumpTpiRecords = true; + opts::raw::DumpTpiHash = true; + opts::raw::DumpIpiRecords = true; + opts::raw::DumpSectionMap = true; + opts::raw::DumpSectionContribs = true; + opts::raw::DumpLineInfo = true; + opts::raw::DumpFpo = true; + opts::raw::DumpStringTable = true; + } + + if (opts::raw::CompactRecords && + (opts::raw::DumpTpiRecordBytes || opts::raw::DumpIpiRecordBytes)) { + errs() << "-compact-records is incompatible with -tpi-record-bytes and " + "-ipi-record-bytes.\n"; + exit(1); + } } llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded); @@ -582,6 +723,8 @@ int main(int argc_, const char *argv_[]) { pdb2Yaml(opts::pdb2yaml::InputFilename.front()); } else if (opts::YamlToPdbSubcommand) { yamlToPdb(opts::yaml2pdb::InputFilename.front()); + } else if (opts::AnalyzeSubcommand) { + dumpAnalysis(opts::analyze::InputFilename.front()); } else if (opts::PrettySubcommand) { if (opts::pretty::Lines) opts::pretty::Compilands = true; @@ -595,6 +738,12 @@ int main(int argc_, const char *argv_[]) { opts::pretty::Lines = true; } + if (opts::pretty::Types) { + opts::pretty::Classes = true; + opts::pretty::Typedefs = true; + opts::pretty::Enums = true; + } + // When adding filters for excluded compilands and types, we need to // remember that these are regexes. So special characters such as * and \ // need to be escaped in the regex. In the case of a literal \, this means @@ -616,6 +765,12 @@ int main(int argc_, const char *argv_[]) { } else if (opts::RawSubcommand) { std::for_each(opts::raw::InputFilenames.begin(), opts::raw::InputFilenames.end(), dumpRaw); + } else if (opts::DiffSubcommand) { + if (opts::diff::InputFilenames.size() != 2) { + errs() << "diff subcommand expects exactly 2 arguments.\n"; + exit(1); + } + diff(opts::diff::InputFilenames[0], opts::diff::InputFilenames[1]); } outs().flush(); diff --git a/tools/llvm-pdbdump/llvm-pdbdump.h b/tools/llvm-pdbdump/llvm-pdbdump.h index 42d847a23fcc9..a5429a253df40 100644 --- a/tools/llvm-pdbdump/llvm-pdbdump.h +++ b/tools/llvm-pdbdump/llvm-pdbdump.h @@ -17,14 +17,19 @@ namespace opts { namespace pretty { + +enum class ClassDefinitionFormat { None, Layout, Graphical, Standard }; +enum class ClassSortMode { None, Name, Size, Padding }; + extern llvm::cl::opt<bool> Compilands; extern llvm::cl::opt<bool> Symbols; extern llvm::cl::opt<bool> Globals; -extern llvm::cl::opt<bool> Types; +extern llvm::cl::opt<bool> Classes; +extern llvm::cl::opt<bool> Enums; +extern llvm::cl::opt<bool> Typedefs; extern llvm::cl::opt<bool> All; extern llvm::cl::opt<bool> ExcludeCompilerGenerated; -extern llvm::cl::opt<bool> NoClassDefs; extern llvm::cl::opt<bool> NoEnumDefs; extern llvm::cl::list<std::string> ExcludeTypes; extern llvm::cl::list<std::string> ExcludeSymbols; @@ -32,6 +37,10 @@ extern llvm::cl::list<std::string> ExcludeCompilands; extern llvm::cl::list<std::string> IncludeTypes; extern llvm::cl::list<std::string> IncludeSymbols; extern llvm::cl::list<std::string> IncludeCompilands; +extern llvm::cl::opt<ClassSortMode> ClassOrder; +extern llvm::cl::opt<uint32_t> SizeThreshold; +extern llvm::cl::opt<uint32_t> PaddingThreshold; +extern llvm::cl::opt<ClassDefinitionFormat> ClassFormat; } namespace raw { @@ -43,6 +52,7 @@ struct BlockRange { extern llvm::Optional<BlockRange> DumpBlockRange; extern llvm::cl::list<uint32_t> DumpStreamData; +extern llvm::cl::opt<bool> CompactRecords; extern llvm::cl::opt<bool> DumpGlobals; extern llvm::cl::opt<bool> DumpHeaders; extern llvm::cl::opt<bool> DumpStreamBlocks; @@ -63,12 +73,19 @@ extern llvm::cl::opt<bool> DumpSectionMap; extern llvm::cl::opt<bool> DumpSymRecordBytes; extern llvm::cl::opt<bool> DumpSectionHeaders; extern llvm::cl::opt<bool> DumpFpo; +extern llvm::cl::opt<bool> DumpStringTable; +} + +namespace diff { +extern llvm::cl::opt<bool> Pedantic; } namespace pdb2yaml { extern llvm::cl::opt<bool> NoFileHeaders; +extern llvm::cl::opt<bool> Minimal; extern llvm::cl::opt<bool> StreamMetadata; extern llvm::cl::opt<bool> StreamDirectory; +extern llvm::cl::opt<bool> StringTable; extern llvm::cl::opt<bool> PdbStream; extern llvm::cl::opt<bool> DbiStream; extern llvm::cl::opt<bool> DbiModuleInfo; diff --git a/tools/llvm-profdata/llvm-profdata.cpp b/tools/llvm-profdata/llvm-profdata.cpp index 6715566a166c2..a257910ecf776 100644 --- a/tools/llvm-profdata/llvm-profdata.cpp +++ b/tools/llvm-profdata/llvm-profdata.cpp @@ -446,8 +446,58 @@ static int merge_main(int argc, const char *argv[]) { return 0; } +typedef struct ValueSitesStats { + ValueSitesStats() + : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0), + TotalNumValues(0) {} + uint64_t TotalNumValueSites; + uint64_t TotalNumValueSitesWithValueProfile; + uint64_t TotalNumValues; + std::vector<unsigned> ValueSitesHistogram; +} ValueSitesStats; + +static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK, + ValueSitesStats &Stats, raw_fd_ostream &OS, + InstrProfSymtab *Symtab) { + uint32_t NS = Func.getNumValueSites(VK); + Stats.TotalNumValueSites += NS; + for (size_t I = 0; I < NS; ++I) { + uint32_t NV = Func.getNumValueDataForSite(VK, I); + std::unique_ptr<InstrProfValueData[]> VD = Func.getValueForSite(VK, I); + Stats.TotalNumValues += NV; + if (NV) { + Stats.TotalNumValueSitesWithValueProfile++; + if (NV > Stats.ValueSitesHistogram.size()) + Stats.ValueSitesHistogram.resize(NV, 0); + Stats.ValueSitesHistogram[NV - 1]++; + } + for (uint32_t V = 0; V < NV; V++) { + OS << "\t[ " << I << ", "; + if (Symtab == nullptr) + OS << VD[V].Value; + else + OS << Symtab->getFuncName(VD[V].Value); + OS << ", " << VD[V].Count << " ]\n"; + } + } +} + +static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK, + ValueSitesStats &Stats) { + OS << " Total number of sites: " << Stats.TotalNumValueSites << "\n"; + OS << " Total number of sites with values: " + << Stats.TotalNumValueSitesWithValueProfile << "\n"; + OS << " Total number of profiled values: " << Stats.TotalNumValues << "\n"; + + OS << " Value sites histogram:\n\tNumTargets, SiteCount\n"; + for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) { + if (Stats.ValueSitesHistogram[I] > 0) + OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n"; + } +} + static int showInstrProfile(const std::string &Filename, bool ShowCounts, - bool ShowIndirectCallTargets, + bool ShowIndirectCallTargets, bool ShowMemOPSizes, bool ShowDetailedSummary, std::vector<uint32_t> DetailedSummaryCutoffs, bool ShowAllFunctions, @@ -465,10 +515,8 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, auto Reader = std::move(ReaderOrErr.get()); bool IsIRInstr = Reader->isIRLevelProfile(); size_t ShownFunctions = 0; - uint64_t TotalNumValueSites = 0; - uint64_t TotalNumValueSitesWithValueProfile = 0; - uint64_t TotalNumValues = 0; - std::vector<unsigned> ICHistogram; + int NumVPKind = IPVK_Last - IPVK_First + 1; + std::vector<ValueSitesStats> VPStats(NumVPKind); for (const auto &Func : *Reader) { bool Show = ShowAllFunctions || (!ShowFunction.empty() && @@ -502,6 +550,11 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, OS << " Indirect Call Site Count: " << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n"; + uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize); + if (ShowMemOPSizes && NumMemOPCalls > 0) + OS << " Number of Memory Intrinsics Calls: " << NumMemOPCalls + << "\n"; + if (ShowCounts) { OS << " Block counts: ["; size_t Start = (IsIRInstr ? 0 : 1); @@ -512,27 +565,16 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, } if (ShowIndirectCallTargets) { - InstrProfSymtab &Symtab = Reader->getSymtab(); - uint32_t NS = Func.getNumValueSites(IPVK_IndirectCallTarget); - OS << " Indirect Target Results: \n"; - TotalNumValueSites += NS; - for (size_t I = 0; I < NS; ++I) { - uint32_t NV = Func.getNumValueDataForSite(IPVK_IndirectCallTarget, I); - std::unique_ptr<InstrProfValueData[]> VD = - Func.getValueForSite(IPVK_IndirectCallTarget, I); - TotalNumValues += NV; - if (NV) { - TotalNumValueSitesWithValueProfile++; - if (NV > ICHistogram.size()) - ICHistogram.resize(NV, 0); - ICHistogram[NV - 1]++; - } - for (uint32_t V = 0; V < NV; V++) { - OS << "\t[ " << I << ", "; - OS << Symtab.getFuncName(VD[V].Value) << ", " << VD[V].Count - << " ]\n"; - } - } + OS << " Indirect Target Results:\n"; + traverseAllValueSites(Func, IPVK_IndirectCallTarget, + VPStats[IPVK_IndirectCallTarget], OS, + &(Reader->getSymtab())); + } + + if (ShowMemOPSizes && NumMemOPCalls > 0) { + OS << " Memory Instrinsic Size Results:\n"; + traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS, + nullptr); } } } @@ -547,17 +589,16 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, OS << "Total functions: " << PS->getNumFunctions() << "\n"; OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n"; OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n"; + if (ShownFunctions && ShowIndirectCallTargets) { - OS << "Total Number of Indirect Call Sites : " << TotalNumValueSites - << "\n"; - OS << "Total Number of Sites With Values : " - << TotalNumValueSitesWithValueProfile << "\n"; - OS << "Total Number of Profiled Values : " << TotalNumValues << "\n"; - - OS << "IC Value histogram : \n\tNumTargets, SiteCount\n"; - for (unsigned I = 0; I < ICHistogram.size(); I++) { - OS << "\t" << I + 1 << ", " << ICHistogram[I] << "\n"; - } + OS << "Statistics for indirect call sites profile:\n"; + showValueSitesStats(OS, IPVK_IndirectCallTarget, + VPStats[IPVK_IndirectCallTarget]); + } + + if (ShownFunctions && ShowMemOPSizes) { + OS << "Statistics for memory intrinsic calls sizes profile:\n"; + showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]); } if (ShowDetailedSummary) { @@ -608,6 +649,10 @@ static int show_main(int argc, const char *argv[]) { cl::opt<bool> ShowIndirectCallTargets( "ic-targets", cl::init(false), cl::desc("Show indirect call site target values for shown functions")); + cl::opt<bool> ShowMemOPSizes( + "memop-sizes", cl::init(false), + cl::desc("Show the profiled sizes of the memory intrinsic calls " + "for shown functions")); cl::opt<bool> ShowDetailedSummary("detailed-summary", cl::init(false), cl::desc("Show detailed profile summary")); cl::list<uint32_t> DetailedSummaryCutoffs( @@ -646,8 +691,9 @@ static int show_main(int argc, const char *argv[]) { DetailedSummaryCutoffs.end()); if (ProfileKind == instr) return showInstrProfile(Filename, ShowCounts, ShowIndirectCallTargets, - ShowDetailedSummary, DetailedSummaryCutoffs, - ShowAllFunctions, ShowFunction, TextFormat, OS); + ShowMemOPSizes, ShowDetailedSummary, + DetailedSummaryCutoffs, ShowAllFunctions, + ShowFunction, TextFormat, OS); else return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, ShowFunction, OS); diff --git a/tools/llvm-readobj/ARMAttributeParser.cpp b/tools/llvm-readobj/ARMAttributeParser.cpp deleted file mode 100644 index 877dd71c90706..0000000000000 --- a/tools/llvm-readobj/ARMAttributeParser.cpp +++ /dev/null @@ -1,682 +0,0 @@ -//===--- ARMAttributeParser.cpp - ARM Attribute Information Printer -------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "ARMAttributeParser.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/Support/LEB128.h" -#include "llvm/Support/ScopedPrinter.h" - -using namespace llvm; -using namespace llvm::ARMBuildAttrs; - - -static const EnumEntry<unsigned> TagNames[] = { - { "Tag_File", ARMBuildAttrs::File }, - { "Tag_Section", ARMBuildAttrs::Section }, - { "Tag_Symbol", ARMBuildAttrs::Symbol }, -}; - -namespace llvm { -#define ATTRIBUTE_HANDLER(Attr_) \ - { ARMBuildAttrs::Attr_, &ARMAttributeParser::Attr_ } - -const ARMAttributeParser::DisplayHandler -ARMAttributeParser::DisplayRoutines[] = { - { ARMBuildAttrs::CPU_raw_name, &ARMAttributeParser::StringAttribute, }, - { ARMBuildAttrs::CPU_name, &ARMAttributeParser::StringAttribute }, - ATTRIBUTE_HANDLER(CPU_arch), - ATTRIBUTE_HANDLER(CPU_arch_profile), - ATTRIBUTE_HANDLER(ARM_ISA_use), - ATTRIBUTE_HANDLER(THUMB_ISA_use), - ATTRIBUTE_HANDLER(FP_arch), - ATTRIBUTE_HANDLER(WMMX_arch), - ATTRIBUTE_HANDLER(Advanced_SIMD_arch), - ATTRIBUTE_HANDLER(PCS_config), - ATTRIBUTE_HANDLER(ABI_PCS_R9_use), - ATTRIBUTE_HANDLER(ABI_PCS_RW_data), - ATTRIBUTE_HANDLER(ABI_PCS_RO_data), - ATTRIBUTE_HANDLER(ABI_PCS_GOT_use), - ATTRIBUTE_HANDLER(ABI_PCS_wchar_t), - ATTRIBUTE_HANDLER(ABI_FP_rounding), - ATTRIBUTE_HANDLER(ABI_FP_denormal), - ATTRIBUTE_HANDLER(ABI_FP_exceptions), - ATTRIBUTE_HANDLER(ABI_FP_user_exceptions), - ATTRIBUTE_HANDLER(ABI_FP_number_model), - ATTRIBUTE_HANDLER(ABI_align_needed), - ATTRIBUTE_HANDLER(ABI_align_preserved), - ATTRIBUTE_HANDLER(ABI_enum_size), - ATTRIBUTE_HANDLER(ABI_HardFP_use), - ATTRIBUTE_HANDLER(ABI_VFP_args), - ATTRIBUTE_HANDLER(ABI_WMMX_args), - ATTRIBUTE_HANDLER(ABI_optimization_goals), - ATTRIBUTE_HANDLER(ABI_FP_optimization_goals), - ATTRIBUTE_HANDLER(compatibility), - ATTRIBUTE_HANDLER(CPU_unaligned_access), - ATTRIBUTE_HANDLER(FP_HP_extension), - ATTRIBUTE_HANDLER(ABI_FP_16bit_format), - ATTRIBUTE_HANDLER(MPextension_use), - ATTRIBUTE_HANDLER(DIV_use), - ATTRIBUTE_HANDLER(DSP_extension), - ATTRIBUTE_HANDLER(T2EE_use), - ATTRIBUTE_HANDLER(Virtualization_use), - ATTRIBUTE_HANDLER(nodefaults) -}; - -#undef ATTRIBUTE_HANDLER - -uint64_t ARMAttributeParser::ParseInteger(const uint8_t *Data, - uint32_t &Offset) { - unsigned Length; - uint64_t Value = decodeULEB128(Data + Offset, &Length); - Offset = Offset + Length; - return Value; -} - -StringRef ARMAttributeParser::ParseString(const uint8_t *Data, - uint32_t &Offset) { - const char *String = reinterpret_cast<const char*>(Data + Offset); - size_t Length = std::strlen(String); - Offset = Offset + Length + 1; - return StringRef(String, Length); -} - -void ARMAttributeParser::IntegerAttribute(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - SW.printNumber(ARMBuildAttrs::AttrTypeAsString(Tag), - ParseInteger(Data, Offset)); -} - -void ARMAttributeParser::StringAttribute(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - StringRef TagName = ARMBuildAttrs::AttrTypeAsString(Tag, /*TagPrefix*/false); - - DictScope AS(SW, "Attribute"); - SW.printNumber("Tag", Tag); - if (!TagName.empty()) - SW.printString("TagName", TagName); - SW.printString("Value", ParseString(Data, Offset)); -} - -void ARMAttributeParser::PrintAttribute(unsigned Tag, unsigned Value, - StringRef ValueDesc) { - StringRef TagName = ARMBuildAttrs::AttrTypeAsString(Tag, /*TagPrefix*/false); - - DictScope AS(SW, "Attribute"); - SW.printNumber("Tag", Tag); - SW.printNumber("Value", Value); - if (!TagName.empty()) - SW.printString("TagName", TagName); - if (!ValueDesc.empty()) - SW.printString("Description", ValueDesc); -} - -void ARMAttributeParser::CPU_arch(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "Pre-v4", "ARM v4", "ARM v4T", "ARM v5T", "ARM v5TE", "ARM v5TEJ", "ARM v6", - "ARM v6KZ", "ARM v6T2", "ARM v6K", "ARM v7", "ARM v6-M", "ARM v6S-M", - "ARM v7E-M", "ARM v8" - }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::CPU_arch_profile(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - uint64_t Encoded = ParseInteger(Data, Offset); - - StringRef Profile; - switch (Encoded) { - default: Profile = "Unknown"; break; - case 'A': Profile = "Application"; break; - case 'R': Profile = "Real-time"; break; - case 'M': Profile = "Microcontroller"; break; - case 'S': Profile = "Classic"; break; - case 0: Profile = "None"; break; - } - - PrintAttribute(Tag, Encoded, Profile); -} - -void ARMAttributeParser::ARM_ISA_use(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { "Not Permitted", "Permitted" }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::THUMB_ISA_use(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { "Not Permitted", "Thumb-1", "Thumb-2" }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::FP_arch(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "Not Permitted", "VFPv1", "VFPv2", "VFPv3", "VFPv3-D16", "VFPv4", - "VFPv4-D16", "ARMv8-a FP", "ARMv8-a FP-D16" - }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::WMMX_arch(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { "Not Permitted", "WMMXv1", "WMMXv2" }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::Advanced_SIMD_arch(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "Not Permitted", "NEONv1", "NEONv2+FMA", "ARMv8-a NEON", "ARMv8.1-a NEON" - }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::PCS_config(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "None", "Bare Platform", "Linux Application", "Linux DSO", "Palm OS 2004", - "Reserved (Palm OS)", "Symbian OS 2004", "Reserved (Symbian OS)" - }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::ABI_PCS_R9_use(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { "v6", "Static Base", "TLS", "Unused" }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::ABI_PCS_RW_data(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "Absolute", "PC-relative", "SB-relative", "Not Permitted" - }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::ABI_PCS_RO_data(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "Absolute", "PC-relative", "Not Permitted" - }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::ABI_PCS_GOT_use(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "Not Permitted", "Direct", "GOT-Indirect" - }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::ABI_PCS_wchar_t(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "Not Permitted", "Unknown", "2-byte", "Unknown", "4-byte" - }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::ABI_FP_rounding(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { "IEEE-754", "Runtime" }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::ABI_FP_denormal(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "Unsupported", "IEEE-754", "Sign Only" - }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::ABI_FP_exceptions(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { "Not Permitted", "IEEE-754" }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::ABI_FP_user_exceptions(AttrType Tag, - const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { "Not Permitted", "IEEE-754" }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::ABI_FP_number_model(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "Not Permitted", "Finite Only", "RTABI", "IEEE-754" - }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::ABI_align_needed(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "Not Permitted", "8-byte alignment", "4-byte alignment", "Reserved" - }; - - uint64_t Value = ParseInteger(Data, Offset); - - std::string Description; - if (Value < array_lengthof(Strings)) - Description = std::string(Strings[Value]); - else if (Value <= 12) - Description = std::string("8-byte alignment, ") + utostr(1ULL << Value) - + std::string("-byte extended alignment"); - else - Description = "Invalid"; - - PrintAttribute(Tag, Value, Description); -} - -void ARMAttributeParser::ABI_align_preserved(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "Not Required", "8-byte data alignment", "8-byte data and code alignment", - "Reserved" - }; - - uint64_t Value = ParseInteger(Data, Offset); - - std::string Description; - if (Value < array_lengthof(Strings)) - Description = std::string(Strings[Value]); - else if (Value <= 12) - Description = std::string("8-byte stack alignment, ") + - utostr(1ULL << Value) + std::string("-byte data alignment"); - else - Description = "Invalid"; - - PrintAttribute(Tag, Value, Description); -} - -void ARMAttributeParser::ABI_enum_size(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "Not Permitted", "Packed", "Int32", "External Int32" - }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::ABI_HardFP_use(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "Tag_FP_arch", "Single-Precision", "Reserved", "Tag_FP_arch (deprecated)" - }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::ABI_VFP_args(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "AAPCS", "AAPCS VFP", "Custom", "Not Permitted" - }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::ABI_WMMX_args(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { "AAPCS", "iWMMX", "Custom" }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::ABI_optimization_goals(AttrType Tag, - const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "None", "Speed", "Aggressive Speed", "Size", "Aggressive Size", "Debugging", - "Best Debugging" - }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::ABI_FP_optimization_goals(AttrType Tag, - const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "None", "Speed", "Aggressive Speed", "Size", "Aggressive Size", "Accuracy", - "Best Accuracy" - }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::compatibility(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - uint64_t Integer = ParseInteger(Data, Offset); - StringRef String = ParseString(Data, Offset); - - DictScope AS(SW, "Attribute"); - SW.printNumber("Tag", Tag); - SW.startLine() << "Value: " << Integer << ", " << String << '\n'; - SW.printString("TagName", AttrTypeAsString(Tag, /*TagPrefix*/false)); - switch (Integer) { - case 0: - SW.printString("Description", StringRef("No Specific Requirements")); - break; - case 1: - SW.printString("Description", StringRef("AEABI Conformant")); - break; - default: - SW.printString("Description", StringRef("AEABI Non-Conformant")); - break; - } -} - -void ARMAttributeParser::CPU_unaligned_access(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { "Not Permitted", "v6-style" }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::FP_HP_extension(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { "If Available", "Permitted" }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::ABI_FP_16bit_format(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { "Not Permitted", "IEEE-754", "VFPv3" }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::MPextension_use(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { "Not Permitted", "Permitted" }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::DIV_use(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "If Available", "Not Permitted", "Permitted" - }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::DSP_extension(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { "Not Permitted", "Permitted" }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::T2EE_use(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { "Not Permitted", "Permitted" }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::Virtualization_use(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "Not Permitted", "TrustZone", "Virtualization Extensions", - "TrustZone + Virtualization Extensions" - }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); -} - -void ARMAttributeParser::nodefaults(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - uint64_t Value = ParseInteger(Data, Offset); - PrintAttribute(Tag, Value, "Unspecified Tags UNDEFINED"); -} - -void ARMAttributeParser::ParseIndexList(const uint8_t *Data, uint32_t &Offset, - SmallVectorImpl<uint8_t> &IndexList) { - for (;;) { - unsigned Length; - uint64_t Value = decodeULEB128(Data + Offset, &Length); - Offset = Offset + Length; - if (Value == 0) - break; - IndexList.push_back(Value); - } -} - -void ARMAttributeParser::ParseAttributeList(const uint8_t *Data, - uint32_t &Offset, uint32_t Length) { - while (Offset < Length) { - unsigned Length; - uint64_t Tag = decodeULEB128(Data + Offset, &Length); - Offset += Length; - - bool Handled = false; - for (unsigned AHI = 0, AHE = array_lengthof(DisplayRoutines); - AHI != AHE && !Handled; ++AHI) { - if (DisplayRoutines[AHI].Attribute == Tag) { - (this->*DisplayRoutines[AHI].Routine)(ARMBuildAttrs::AttrType(Tag), - Data, Offset); - Handled = true; - break; - } - } - if (!Handled) { - if (Tag < 32) { - errs() << "unhandled AEABI Tag " << Tag - << " (" << ARMBuildAttrs::AttrTypeAsString(Tag) << ")\n"; - continue; - } - - if (Tag % 2 == 0) - IntegerAttribute(ARMBuildAttrs::AttrType(Tag), Data, Offset); - else - StringAttribute(ARMBuildAttrs::AttrType(Tag), Data, Offset); - } - } -} - -void ARMAttributeParser::ParseSubsection(const uint8_t *Data, uint32_t Length) { - uint32_t Offset = sizeof(uint32_t); /* SectionLength */ - - SW.printNumber("SectionLength", Length); - - const char *VendorName = reinterpret_cast<const char*>(Data + Offset); - size_t VendorNameLength = std::strlen(VendorName); - SW.printString("Vendor", StringRef(VendorName, VendorNameLength)); - Offset = Offset + VendorNameLength + 1; - - if (StringRef(VendorName, VendorNameLength).lower() != "aeabi") - return; - - while (Offset < Length) { - /// Tag_File | Tag_Section | Tag_Symbol uleb128:byte-size - uint8_t Tag = Data[Offset]; - SW.printEnum("Tag", Tag, makeArrayRef(TagNames)); - Offset = Offset + sizeof(Tag); - - uint32_t Size = - *reinterpret_cast<const support::ulittle32_t*>(Data + Offset); - SW.printNumber("Size", Size); - Offset = Offset + sizeof(Size); - - if (Size > Length) { - errs() << "subsection length greater than section length\n"; - return; - } - - StringRef ScopeName, IndexName; - SmallVector<uint8_t, 8> Indicies; - switch (Tag) { - case ARMBuildAttrs::File: - ScopeName = "FileAttributes"; - break; - case ARMBuildAttrs::Section: - ScopeName = "SectionAttributes"; - IndexName = "Sections"; - ParseIndexList(Data, Offset, Indicies); - break; - case ARMBuildAttrs::Symbol: - ScopeName = "SymbolAttributes"; - IndexName = "Symbols"; - ParseIndexList(Data, Offset, Indicies); - break; - default: - errs() << "unrecognised tag: 0x" << utohexstr(Tag) << '\n'; - return; - } - - DictScope ASS(SW, ScopeName); - - if (!Indicies.empty()) - SW.printList(IndexName, Indicies); - - ParseAttributeList(Data, Offset, Length); - } -} - -void ARMAttributeParser::Parse(ArrayRef<uint8_t> Section) { - size_t Offset = 1; - unsigned SectionNumber = 0; - - while (Offset < Section.size()) { - uint32_t SectionLength = - *reinterpret_cast<const support::ulittle32_t*>(Section.data() + Offset); - - SW.startLine() << "Section " << ++SectionNumber << " {\n"; - SW.indent(); - - ParseSubsection(Section.data() + Offset, SectionLength); - Offset = Offset + SectionLength; - - SW.unindent(); - SW.startLine() << "}\n"; - } -} -} - diff --git a/tools/llvm-readobj/ARMAttributeParser.h b/tools/llvm-readobj/ARMAttributeParser.h deleted file mode 100644 index 6936b70ca1230..0000000000000 --- a/tools/llvm-readobj/ARMAttributeParser.h +++ /dev/null @@ -1,126 +0,0 @@ -//===--- ARMAttributeParser.h - ARM Attribute Information Printer ---------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_LLVM_READOBJ_ARMATTRIBUTEPARSER_H -#define LLVM_TOOLS_LLVM_READOBJ_ARMATTRIBUTEPARSER_H - -#include "llvm/Support/ARMBuildAttributes.h" -#include "llvm/Support/ScopedPrinter.h" - -namespace llvm { -class StringRef; - -class ARMAttributeParser { - ScopedPrinter &SW; - - struct DisplayHandler { - ARMBuildAttrs::AttrType Attribute; - void (ARMAttributeParser::*Routine)(ARMBuildAttrs::AttrType, - const uint8_t *, uint32_t &); - }; - static const DisplayHandler DisplayRoutines[]; - - uint64_t ParseInteger(const uint8_t *Data, uint32_t &Offset); - StringRef ParseString(const uint8_t *Data, uint32_t &Offset); - - void IntegerAttribute(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void StringAttribute(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - - void PrintAttribute(unsigned Tag, unsigned Value, StringRef ValueDesc); - - void CPU_arch(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void CPU_arch_profile(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ARM_ISA_use(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void THUMB_ISA_use(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void FP_arch(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void WMMX_arch(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void Advanced_SIMD_arch(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void PCS_config(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_PCS_R9_use(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_PCS_RW_data(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_PCS_RO_data(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_PCS_GOT_use(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_PCS_wchar_t(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_FP_rounding(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_FP_denormal(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_FP_exceptions(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_FP_user_exceptions(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_FP_number_model(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_align_needed(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_align_preserved(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_enum_size(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_HardFP_use(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_VFP_args(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_WMMX_args(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_optimization_goals(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_FP_optimization_goals(ARMBuildAttrs::AttrType Tag, - const uint8_t *Data, uint32_t &Offset); - void compatibility(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void CPU_unaligned_access(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void FP_HP_extension(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void ABI_FP_16bit_format(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void MPextension_use(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void DIV_use(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void DSP_extension(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void T2EE_use(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void Virtualization_use(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - void nodefaults(ARMBuildAttrs::AttrType Tag, const uint8_t *Data, - uint32_t &Offset); - - void ParseAttributeList(const uint8_t *Data, uint32_t &Offset, - uint32_t Length); - void ParseIndexList(const uint8_t *Data, uint32_t &Offset, - SmallVectorImpl<uint8_t> &IndexList); - void ParseSubsection(const uint8_t *Data, uint32_t Length); -public: - ARMAttributeParser(ScopedPrinter &SW) : SW(SW) {} - - void Parse(ArrayRef<uint8_t> Section); -}; - -} - -#endif - diff --git a/tools/llvm-readobj/CMakeLists.txt b/tools/llvm-readobj/CMakeLists.txt index 0f8336ffd3265..0ad149538f635 100644 --- a/tools/llvm-readobj/CMakeLists.txt +++ b/tools/llvm-readobj/CMakeLists.txt @@ -4,10 +4,10 @@ set(LLVM_LINK_COMPONENTS Support DebugInfoCodeView DebugInfoMSF + DebugInfoPDB ) add_llvm_tool(llvm-readobj - ARMAttributeParser.cpp ARMWinEHPrinter.cpp COFFDumper.cpp COFFImportDumper.cpp @@ -16,5 +16,6 @@ add_llvm_tool(llvm-readobj llvm-readobj.cpp MachODumper.cpp ObjDumper.cpp + WasmDumper.cpp Win64EHDumper.cpp ) diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp index c83655fe4d223..9836c137ed2ca 100644 --- a/tools/llvm-readobj/COFFDumper.cpp +++ b/tools/llvm-readobj/COFFDumper.cpp @@ -35,14 +35,15 @@ #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" #include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" -#include "llvm/DebugInfo/MSF/ByteStream.h" #include "llvm/Object/COFF.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/COFF.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Support/Format.h" +#include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/Win64EH.h" @@ -55,7 +56,6 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::codeview; -using namespace llvm::msf; using namespace llvm::support; using namespace llvm::Win64EH; @@ -79,7 +79,8 @@ public: void printCOFFBaseReloc() override; void printCOFFDebugDirectory() override; void printCodeViewDebugInfo() override; - void mergeCodeViewTypes(llvm::codeview::TypeTableBuilder &CVTypes) override; + void mergeCodeViewTypes(llvm::codeview::TypeTableBuilder &CVIDs, + llvm::codeview::TypeTableBuilder &CVTypes) override; void printStackMap() const override; private: void printSymbol(const SymbolRef &Sym); @@ -154,7 +155,7 @@ public: Sec = Obj->getCOFFSection(SR); } - uint32_t getRecordOffset(msf::StreamReader Reader) override { + uint32_t getRecordOffset(BinaryStreamReader Reader) override { ArrayRef<uint8_t> Data; if (auto EC = Reader.readLongestContiguousChunk(Data)) { llvm::consumeError(std::move(EC)); @@ -840,8 +841,8 @@ void COFFDumper::printCodeViewSymbolSection(StringRef SectionName, } case ModuleSubstreamKind::FrameData: { // First four bytes is a relocation against the function. - msf::ByteStream S(Contents); - msf::StreamReader SR(S); + BinaryByteStream S(Contents, llvm::support::little); + BinaryStreamReader SR(S); const uint32_t *CodePtr; error(SR.readObject(CodePtr)); StringRef LinkageName; @@ -965,9 +966,9 @@ void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection, CVSymbolDumper CVSD(W, TypeDB, std::move(CODD), opts::CodeViewSubsectionBytes); - ByteStream Stream(BinaryData); + BinaryByteStream Stream(BinaryData, llvm::support::little); CVSymbolArray Symbols; - StreamReader Reader(Stream); + BinaryStreamReader Reader(Stream); if (auto EC = Reader.readArray(Symbols, Reader.getLength())) { consumeError(std::move(EC)); W.flush(); @@ -982,8 +983,8 @@ void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection, } void COFFDumper::printCodeViewFileChecksums(StringRef Subsection) { - msf::ByteStream S(Subsection); - msf::StreamReader SR(S); + BinaryByteStream S(Subsection, llvm::support::little); + BinaryStreamReader SR(S); while (!SR.empty()) { DictScope S(W, "FileChecksum"); const FileChecksum *FC; @@ -1011,8 +1012,8 @@ void COFFDumper::printCodeViewFileChecksums(StringRef Subsection) { } void COFFDumper::printCodeViewInlineeLines(StringRef Subsection) { - msf::ByteStream S(Subsection); - msf::StreamReader SR(S); + BinaryByteStream S(Subsection, llvm::support::little); + BinaryStreamReader SR(S); uint32_t Signature; error(SR.readInteger(Signature)); bool HasExtraFiles = Signature == unsigned(InlineeLinesSignature::ExtraFiles); @@ -1064,7 +1065,8 @@ void COFFDumper::printFileNameForOffset(StringRef Label, uint32_t FileOffset) { W.printHex(Label, getFileNameForFileOffset(FileOffset), FileOffset); } -void COFFDumper::mergeCodeViewTypes(TypeTableBuilder &CVTypes) { +void COFFDumper::mergeCodeViewTypes(TypeTableBuilder &CVIDs, + TypeTableBuilder &CVTypes) { for (const SectionRef &S : Obj->sections()) { StringRef SectionName; error(S.getName(SectionName)); @@ -1077,17 +1079,17 @@ void COFFDumper::mergeCodeViewTypes(TypeTableBuilder &CVTypes) { error(object_error::parse_failed); ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(Data.data()), Data.size()); - ByteStream Stream(Bytes); + BinaryByteStream Stream(Bytes, llvm::support::little); CVTypeArray Types; - StreamReader Reader(Stream); + BinaryStreamReader Reader(Stream); if (auto EC = Reader.readArray(Types, Reader.getLength())) { consumeError(std::move(EC)); W.flush(); error(object_error::parse_failed); } - if (!mergeTypeStreams(CVTypes, Types)) - return error(object_error::parse_failed); + if (auto EC = mergeTypeStreams(CVIDs, CVTypes, nullptr, Types)) + return error(std::move(EC)); } } } @@ -1435,12 +1437,18 @@ void COFFDumper::printCOFFImports() { StringRef Name; error(I.getName(Name)); W.printString("Name", Name); - uint32_t Addr; - error(I.getImportLookupTableRVA(Addr)); - W.printHex("ImportLookupTableRVA", Addr); - error(I.getImportAddressTableRVA(Addr)); - W.printHex("ImportAddressTableRVA", Addr); - printImportedSymbols(I.imported_symbols()); + uint32_t ILTAddr; + error(I.getImportLookupTableRVA(ILTAddr)); + W.printHex("ImportLookupTableRVA", ILTAddr); + uint32_t IATAddr; + error(I.getImportAddressTableRVA(IATAddr)); + W.printHex("ImportAddressTableRVA", IATAddr); + // The import lookup table can be missing with certain older linkers, so + // fall back to the import address table in that case. + if (ILTAddr) + printImportedSymbols(I.lookup_table_symbols()); + else + printImportedSymbols(I.imported_symbols()); } // Delay imports @@ -1549,20 +1557,43 @@ void COFFDumper::printStackMap() const { } void llvm::dumpCodeViewMergedTypes(ScopedPrinter &Writer, + llvm::codeview::TypeTableBuilder &IDTable, llvm::codeview::TypeTableBuilder &CVTypes) { // Flatten it first, then run our dumper on it. - ListScope S(Writer, "MergedTypeStream"); - SmallString<0> Buf; + SmallString<0> TypeBuf; CVTypes.ForEachRecord([&](TypeIndex TI, ArrayRef<uint8_t> Record) { - Buf.append(Record.begin(), Record.end()); + TypeBuf.append(Record.begin(), Record.end()); }); TypeDatabase TypeDB; - CVTypeDumper CVTD(TypeDB); - TypeDumpVisitor TDV(TypeDB, &Writer, opts::CodeViewSubsectionBytes); - if (auto EC = - CVTD.dump({Buf.str().bytes_begin(), Buf.str().bytes_end()}, TDV)) { - Writer.flush(); - error(llvm::errorToErrorCode(std::move(EC))); + { + ListScope S(Writer, "MergedTypeStream"); + CVTypeDumper CVTD(TypeDB); + TypeDumpVisitor TDV(TypeDB, &Writer, opts::CodeViewSubsectionBytes); + if (auto EC = CVTD.dump( + {TypeBuf.str().bytes_begin(), TypeBuf.str().bytes_end()}, TDV)) { + Writer.flush(); + error(std::move(EC)); + } + } + + // Flatten the id stream and print it next. The ID stream refers to names from + // the type stream. + SmallString<0> IDBuf; + IDTable.ForEachRecord([&](TypeIndex TI, ArrayRef<uint8_t> Record) { + IDBuf.append(Record.begin(), Record.end()); + }); + + { + ListScope S(Writer, "MergedIDStream"); + TypeDatabase IDDB; + CVTypeDumper CVTD(IDDB); + TypeDumpVisitor TDV(TypeDB, &Writer, opts::CodeViewSubsectionBytes); + TDV.setItemDB(IDDB); + if (auto EC = CVTD.dump( + {IDBuf.str().bytes_begin(), IDBuf.str().bytes_end()}, TDV)) { + Writer.flush(); + error(std::move(EC)); + } } } diff --git a/tools/llvm-readobj/ELFDumper.cpp b/tools/llvm-readobj/ELFDumper.cpp index 997af568d3944..7893eea5d2209 100644 --- a/tools/llvm-readobj/ELFDumper.cpp +++ b/tools/llvm-readobj/ELFDumper.cpp @@ -12,7 +12,6 @@ /// //===----------------------------------------------------------------------===// -#include "ARMAttributeParser.h" #include "ARMEHABIPrinter.h" #include "Error.h" #include "ObjDumper.h" @@ -22,6 +21,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Object/ELFObjectFile.h" +#include "llvm/Support/ARMAttributeParser.h" #include "llvm/Support/ARMBuildAttributes.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Format.h" @@ -129,7 +129,7 @@ public: void printMipsReginfo() override; void printMipsOptions() override; - void printAMDGPURuntimeMD() override; + void printAMDGPUCodeObjectMetadata() override; void printStackMap() const override; @@ -1003,6 +1003,7 @@ static const char *getElfSectionType(unsigned Arch, unsigned Type) { LLVM_READOBJ_ENUM_CASE(ELF, SHT_MIPS_REGINFO); LLVM_READOBJ_ENUM_CASE(ELF, SHT_MIPS_OPTIONS); LLVM_READOBJ_ENUM_CASE(ELF, SHT_MIPS_ABIFLAGS); + LLVM_READOBJ_ENUM_CASE(ELF, SHT_MIPS_DWARF); } } @@ -1892,7 +1893,7 @@ template <> void ELFDumper<ELFType<support::little, false>>::printAttributes() { if (Contents.size() == 1) continue; - ARMAttributeParser(W).Parse(Contents); + ARMAttributeParser(&W).Parse(Contents, true); } } } @@ -2356,7 +2357,7 @@ template <class ELFT> void ELFDumper<ELFT>::printMipsOptions() { } } -template <class ELFT> void ELFDumper<ELFT>::printAMDGPURuntimeMD() { +template <class ELFT> void ELFDumper<ELFT>::printAMDGPUCodeObjectMetadata() { const Elf_Shdr *Shdr = findSectionByName(*Obj, ".note"); if (!Shdr) { W.startLine() << "There is no .note section in the file.\n"; @@ -2364,7 +2365,7 @@ template <class ELFT> void ELFDumper<ELFT>::printAMDGPURuntimeMD() { } ArrayRef<uint8_t> Sec = unwrapOrError(Obj->getSectionContents(Shdr)); - const uint32_t RuntimeMDNoteType = 7; + const uint32_t CodeObjectMetadataNoteType = 10; for (auto I = reinterpret_cast<const Elf_Word *>(&Sec[0]), E = I + Sec.size()/4; I != E;) { uint32_t NameSZ = I[0]; @@ -2378,7 +2379,7 @@ template <class ELFT> void ELFDumper<ELFT>::printAMDGPURuntimeMD() { I += alignTo<4>(NameSZ)/4; } - if (Name == "AMD" && Type == RuntimeMDNoteType) { + if (Name == "AMD" && Type == CodeObjectMetadataNoteType) { StringRef Desc(reinterpret_cast<const char *>(I), DescSZ); W.printString(Desc); } @@ -2627,6 +2628,8 @@ std::string getSectionTypeString(unsigned Arch, unsigned Type) { return "MIPS_OPTIONS"; case SHT_MIPS_ABIFLAGS: return "MIPS_ABIFLAGS"; + case SHT_MIPS_DWARF: + return "SHT_MIPS_DWARF"; } } switch (Type) { @@ -3337,9 +3340,38 @@ static std::string getGNUNoteTypeName(const uint32_t NT) { return string; } +static std::string getFreeBSDNoteTypeName(const uint32_t NT) { + static const struct { + uint32_t ID; + const char *Name; + } Notes[] = { + {ELF::NT_FREEBSD_THRMISC, "NT_THRMISC (thrmisc structure)"}, + {ELF::NT_FREEBSD_PROCSTAT_PROC, "NT_PROCSTAT_PROC (proc data)"}, + {ELF::NT_FREEBSD_PROCSTAT_FILES, "NT_PROCSTAT_FILES (files data)"}, + {ELF::NT_FREEBSD_PROCSTAT_VMMAP, "NT_PROCSTAT_VMMAP (vmmap data)"}, + {ELF::NT_FREEBSD_PROCSTAT_GROUPS, "NT_PROCSTAT_GROUPS (groups data)"}, + {ELF::NT_FREEBSD_PROCSTAT_UMASK, "NT_PROCSTAT_UMASK (umask data)"}, + {ELF::NT_FREEBSD_PROCSTAT_RLIMIT, "NT_PROCSTAT_RLIMIT (rlimit data)"}, + {ELF::NT_FREEBSD_PROCSTAT_OSREL, "NT_PROCSTAT_OSREL (osreldate data)"}, + {ELF::NT_FREEBSD_PROCSTAT_PSSTRINGS, + "NT_PROCSTAT_PSSTRINGS (ps_strings data)"}, + {ELF::NT_FREEBSD_PROCSTAT_AUXV, "NT_PROCSTAT_AUXV (auxv data)"}, + }; + + for (const auto &Note : Notes) + if (Note.ID == NT) + return std::string(Note.Name); + + std::string string; + raw_string_ostream OS(string); + OS << format("Unknown note type (0x%08x)", NT); + return string; +} + template <typename ELFT> static void printGNUNote(raw_ostream &OS, uint32_t NoteType, - ArrayRef<typename ELFFile<ELFT>::Elf_Word> Words) { + ArrayRef<typename ELFFile<ELFT>::Elf_Word> Words, + size_t Size) { switch (NoteType) { default: return; @@ -3362,16 +3394,14 @@ static void printGNUNote(raw_ostream &OS, uint32_t NoteType, } case ELF::NT_GNU_BUILD_ID: { OS << " Build ID: "; - ArrayRef<uint8_t> ID(reinterpret_cast<const uint8_t *>(Words.data()), - Words.size() * 4); + ArrayRef<uint8_t> ID(reinterpret_cast<const uint8_t *>(Words.data()), Size); for (const auto &B : ID) OS << format_hex_no_prefix(B, 2); break; } case ELF::NT_GNU_GOLD_VERSION: OS << " Version: " - << StringRef(reinterpret_cast<const char *>(Words.data()), - Words.size() * 4); + << StringRef(reinterpret_cast<const char *>(Words.data()), Size); break; } @@ -3415,11 +3445,15 @@ void GNUStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { if (Name == "GNU") { OS << getGNUNoteTypeName(Type) << '\n'; - printGNUNote<ELFT>(OS, Type, Descriptor); + printGNUNote<ELFT>(OS, Type, Descriptor, DescriptorSize); + } else if (Name == "FreeBSD") { + OS << getFreeBSDNoteTypeName(Type) << '\n'; + } else { + OS << "Unknown note type: (" << format_hex(Type, 10) << ')'; } OS << '\n'; - P = P + 3 * sizeof(Elf_Word) * alignTo<4>(NameSize) + + P = P + 3 * sizeof(Elf_Word) + alignTo<4>(NameSize) + alignTo<4>(DescriptorSize); } }; diff --git a/tools/llvm-readobj/LLVMBuild.txt b/tools/llvm-readobj/LLVMBuild.txt index 76dd436e21d6c..c0ed38e18d0c0 100644 --- a/tools/llvm-readobj/LLVMBuild.txt +++ b/tools/llvm-readobj/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = llvm-readobj parent = Tools -required_libraries = all-targets BitReader Object DebugInfoCodeView DebugInfoMSF +required_libraries = all-targets BitReader Object DebugInfoCodeView DebugInfoPDB DebugInfoMSF diff --git a/tools/llvm-readobj/MachODumper.cpp b/tools/llvm-readobj/MachODumper.cpp index 01b074170ba53..39e9092799375 100644 --- a/tools/llvm-readobj/MachODumper.cpp +++ b/tools/llvm-readobj/MachODumper.cpp @@ -713,12 +713,30 @@ void MachODumper::printMachOVersionMin() { case MachO::LC_VERSION_MIN_WATCHOS: Cmd = "LC_VERSION_MIN_WATCHOS"; break; + case MachO::LC_BUILD_VERSION: + Cmd = "LC_BUILD_VERSION"; + break; default: continue; } - MachO::version_min_command VMC = Obj->getVersionMinLoadCommand(Load); DictScope Group(W, "MinVersion"); + // Handle LC_BUILD_VERSION. + if (Load.C.cmd == MachO::LC_BUILD_VERSION) { + MachO::build_version_command BVC = Obj->getBuildVersionLoadCommand(Load); + W.printString("Cmd", Cmd); + W.printNumber("Size", BVC.cmdsize); + W.printString("Platform", + MachOObjectFile::getBuildPlatform(BVC.platform)); + W.printString("Version", MachOObjectFile::getVersionString(BVC.minos)); + if (BVC.sdk) + W.printString("SDK", MachOObjectFile::getVersionString(BVC.sdk)); + else + W.printString("SDK", StringRef("n/a")); + continue; + } + + MachO::version_min_command VMC = Obj->getVersionMinLoadCommand(Load); W.printString("Cmd", Cmd); W.printNumber("Size", VMC.cmdsize); SmallString<32> Version; diff --git a/tools/llvm-readobj/ObjDumper.h b/tools/llvm-readobj/ObjDumper.h index c91558ecbfa75..ff780dae57843 100644 --- a/tools/llvm-readobj/ObjDumper.h +++ b/tools/llvm-readobj/ObjDumper.h @@ -59,7 +59,7 @@ public: virtual void printMipsOptions() { } // Only implemented for AMDGPU ELF at this time. - virtual void printAMDGPURuntimeMD() {} + virtual void printAMDGPUCodeObjectMetadata() {} // Only implemented for PE/COFF. virtual void printCOFFImports() { } @@ -68,7 +68,8 @@ public: virtual void printCOFFBaseReloc() { } virtual void printCOFFDebugDirectory() { } virtual void printCodeViewDebugInfo() { } - virtual void mergeCodeViewTypes(llvm::codeview::TypeTableBuilder &CVTypes) {} + virtual void mergeCodeViewTypes(llvm::codeview::TypeTableBuilder &CVIDs, + llvm::codeview::TypeTableBuilder &CVTypes) {} // Only implemented for MachO. virtual void printMachODataInCode() { } @@ -96,10 +97,15 @@ std::error_code createMachODumper(const object::ObjectFile *Obj, ScopedPrinter &Writer, std::unique_ptr<ObjDumper> &Result); +std::error_code createWasmDumper(const object::ObjectFile *Obj, + ScopedPrinter &Writer, + std::unique_ptr<ObjDumper> &Result); + void dumpCOFFImportFile(const object::COFFImportFile *File); void dumpCodeViewMergedTypes(ScopedPrinter &Writer, - llvm::codeview::TypeTableBuilder &CVTypes); + llvm::codeview::TypeTableBuilder &IDTable, + llvm::codeview::TypeTableBuilder &TypeTable); } // namespace llvm diff --git a/tools/llvm-readobj/WasmDumper.cpp b/tools/llvm-readobj/WasmDumper.cpp new file mode 100644 index 0000000000000..e27da3b96e5d5 --- /dev/null +++ b/tools/llvm-readobj/WasmDumper.cpp @@ -0,0 +1,177 @@ +//===-- WasmDumper.cpp - Wasm-specific object file dumper -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Wasm-specific dumper for llvm-readobj. +// +//===----------------------------------------------------------------------===// + +#include "Error.h" +#include "ObjDumper.h" +#include "llvm-readobj.h" +#include "llvm/Object/Wasm.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; +using namespace object; + +namespace { + +static const EnumEntry<unsigned> WasmSymbolTypes[] = { +#define ENUM_ENTRY(X) { #X, static_cast<unsigned>(WasmSymbol::SymbolType::X) } + ENUM_ENTRY(FUNCTION_IMPORT), + ENUM_ENTRY(FUNCTION_EXPORT), + ENUM_ENTRY(GLOBAL_IMPORT), + ENUM_ENTRY(GLOBAL_EXPORT), + ENUM_ENTRY(DEBUG_FUNCTION_NAME), +#undef ENUM_ENTRY +}; + +static const EnumEntry<uint32_t> WasmSectionTypes[] = { +#define ENUM_ENTRY(X) { #X, wasm::WASM_SEC_##X } + ENUM_ENTRY(CUSTOM), + ENUM_ENTRY(TYPE), + ENUM_ENTRY(IMPORT), + ENUM_ENTRY(FUNCTION), + ENUM_ENTRY(TABLE), + ENUM_ENTRY(MEMORY), + ENUM_ENTRY(GLOBAL), + ENUM_ENTRY(EXPORT), + ENUM_ENTRY(START), + ENUM_ENTRY(ELEM), + ENUM_ENTRY(CODE), + ENUM_ENTRY(DATA), +#undef ENUM_ENTRY +}; + +class WasmDumper : public ObjDumper { +public: + WasmDumper(const WasmObjectFile *Obj, ScopedPrinter &Writer) + : ObjDumper(Writer), Obj(Obj) {} + + void printFileHeaders() override; + void printSections() override; + void printRelocations() override; + void printSymbols() override; + void printDynamicSymbols() override { llvm_unreachable("unimplemented"); } + void printUnwindInfo() override { llvm_unreachable("unimplemented"); } + void printStackMap() const override { llvm_unreachable("unimplemented"); } + +protected: + void printSymbol(const SymbolRef &Sym); + void printRelocation(const SectionRef &Section, const RelocationRef &Reloc); + +private: + const WasmObjectFile *Obj; +}; + +void WasmDumper::printFileHeaders() { + W.printHex("Version", Obj->getHeader().Version); +} + +void WasmDumper::printRelocation(const SectionRef &Section, + const RelocationRef &Reloc) { + SmallString<64> RelocTypeName; + uint64_t RelocType = Reloc.getType(); + Reloc.getTypeName(RelocTypeName); + const wasm::WasmRelocation &WasmReloc = Obj->getWasmRelocation(Reloc); + + if (opts::ExpandRelocs) { + DictScope Group(W, "Relocation"); + W.printNumber("Type", RelocTypeName, RelocType); + W.printHex("Offset", Reloc.getOffset()); + W.printHex("Index", WasmReloc.Index); + W.printHex("Addend", WasmReloc.Addend); + } else { + raw_ostream& OS = W.startLine(); + OS << W.hex(Reloc.getOffset()) + << " " << RelocTypeName << "[" << WasmReloc.Index << "]" + << " " << W.hex(WasmReloc.Addend) << "\n"; + } +} + +void WasmDumper::printRelocations() { + ListScope D(W, "Relocations"); + + int SectionNumber = 0; + for (const SectionRef &Section : Obj->sections()) { + bool PrintedGroup = false; + StringRef Name; + error(Section.getName(Name)); + ++SectionNumber; + + for (const RelocationRef &Reloc : Section.relocations()) { + if (!PrintedGroup) { + W.startLine() << "Section (" << SectionNumber << ") " << Name << " {\n"; + W.indent(); + PrintedGroup = true; + } + + printRelocation(Section, Reloc); + } + + if (PrintedGroup) { + W.unindent(); + W.startLine() << "}\n"; + } + } +} + +void WasmDumper::printSymbols() { + ListScope Group(W, "Symbols"); + + for (const SymbolRef &Symbol : Obj->symbols()) + printSymbol(Symbol); +} + +void WasmDumper::printSections() { + ListScope Group(W, "Sections"); + for (const SectionRef &Section : Obj->sections()) { + const WasmSection &WasmSec = Obj->getWasmSection(Section); + DictScope SectionD(W, "Section"); + W.printEnum("Type", WasmSec.Type, makeArrayRef(WasmSectionTypes)); + W.printNumber("Size", (uint64_t)WasmSec.Content.size()); + W.printNumber("Offset", WasmSec.Offset); + if (WasmSec.Type == wasm::WASM_SEC_CUSTOM) { + W.printString("Name", WasmSec.Name); + } + + if (opts::SectionRelocations) { + ListScope D(W, "Relocations"); + for (const RelocationRef &Reloc : Section.relocations()) + printRelocation(Section, Reloc); + } + + if (opts::SectionData) { + W.printBinaryBlock("SectionData", WasmSec.Content); + } + } +} + +void WasmDumper::printSymbol(const SymbolRef &Sym) { + DictScope D(W, "Symbol"); + WasmSymbol Symbol = Obj->getWasmSymbol(Sym.getRawDataRefImpl()); + W.printString("Name", Symbol.Name); + W.printEnum("Type", static_cast<unsigned>(Symbol.Type), makeArrayRef(WasmSymbolTypes)); +} + +} + +namespace llvm { + +std::error_code createWasmDumper(const object::ObjectFile *Obj, + ScopedPrinter &Writer, + std::unique_ptr<ObjDumper> &Result) { + const WasmObjectFile *WasmObj = dyn_cast<WasmObjectFile>(Obj); + assert(WasmObj && "createWasmDumper called with non-wasm object"); + + Result.reset(new WasmDumper(WasmObj, Writer)); + return readobj_error::success; +} + +} // namespace llvm diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index 970e1545de0ad..bc2a62e799ab0 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/tools/llvm-readobj/llvm-readobj.cpp @@ -186,9 +186,10 @@ namespace opts { cl::opt<bool> MipsOptions("mips-options", cl::desc("Display the MIPS .MIPS.options section")); - // -amdgpu-runtime-metadata - cl::opt<bool> AMDGPURuntimeMD("amdgpu-runtime-metadata", - cl::desc("Display AMDGPU runtime metadata")); + // -amdgpu-code-object-metadata + cl::opt<bool> AMDGPUCodeObjectMetadata( + "amdgpu-code-object-metadata", + cl::desc("Display AMDGPU code object metadata")); // -coff-imports cl::opt<bool> @@ -337,10 +338,12 @@ static bool isMipsArch(unsigned Arch) { } namespace { struct ReadObjTypeTableBuilder { - ReadObjTypeTableBuilder() : Allocator(), Builder(Allocator) {} + ReadObjTypeTableBuilder() + : Allocator(), IDTable(Allocator), TypeTable(Allocator) {} llvm::BumpPtrAllocator Allocator; - llvm::codeview::TypeTableBuilder Builder; + llvm::codeview::TypeTableBuilder IDTable; + llvm::codeview::TypeTableBuilder TypeTable; }; } static ReadObjTypeTableBuilder CVTypes; @@ -358,6 +361,8 @@ static std::error_code createDumper(const ObjectFile *Obj, return createELFDumper(Obj, Writer, Result); if (Obj->isMachO()) return createMachODumper(Obj, Writer, Result); + if (Obj->isWasm()) + return createWasmDumper(Obj, Writer, Result); return readobj_error::unsupported_obj_file_format; } @@ -420,8 +425,8 @@ static void dumpObject(const ObjectFile *Obj) { Dumper->printMipsOptions(); } if (Obj->getArch() == llvm::Triple::amdgcn) - if (opts::AMDGPURuntimeMD) - Dumper->printAMDGPURuntimeMD(); + if (opts::AMDGPUCodeObjectMetadata) + Dumper->printAMDGPUCodeObjectMetadata(); if (opts::SectionGroups) Dumper->printGroupSections(); if (opts::HashHistogram) @@ -443,7 +448,7 @@ static void dumpObject(const ObjectFile *Obj) { if (opts::CodeView) Dumper->printCodeViewDebugInfo(); if (opts::CodeViewMergedTypes) - Dumper->mergeCodeViewTypes(CVTypes.Builder); + Dumper->mergeCodeViewTypes(CVTypes.IDTable, CVTypes.TypeTable); } if (Obj->isMachO()) { if (opts::MachODataInCode) @@ -548,7 +553,7 @@ int main(int argc, const char *argv[]) { if (opts::CodeViewMergedTypes) { ScopedPrinter W(outs()); - dumpCodeViewMergedTypes(W, CVTypes.Builder); + dumpCodeViewMergedTypes(W, CVTypes.IDTable, CVTypes.TypeTable); } return 0; diff --git a/tools/llvm-shlib/CMakeLists.txt b/tools/llvm-shlib/CMakeLists.txt index edadb82c3b435..c68a2b0e60eae 100644 --- a/tools/llvm-shlib/CMakeLists.txt +++ b/tools/llvm-shlib/CMakeLists.txt @@ -37,7 +37,7 @@ endif() add_llvm_library(LLVM SHARED DISABLE_LLVM_LINK_LLVM_DYLIB SONAME ${SOURCES}) list(REMOVE_DUPLICATES LIB_NAMES) -if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" OR MINGW) # FIXME: It should be "GNU ld for elf" +if(("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") OR (MINGW) OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "DragonFly")) # FIXME: It should be "GNU ld for elf" # GNU ld doesn't resolve symbols in the version script. set(LIB_NAMES -Wl,--whole-archive ${LIB_NAMES} -Wl,--no-whole-archive) elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") diff --git a/tools/llvm-stress/llvm-stress.cpp b/tools/llvm-stress/llvm-stress.cpp index 731a24d0ac2d2..74b7735f8cd15 100644 --- a/tools/llvm-stress/llvm-stress.cpp +++ b/tools/llvm-stress/llvm-stress.cpp @@ -28,6 +28,7 @@ #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/ToolOutputFile.h" #include <algorithm> +#include <random> #include <vector> namespace llvm { @@ -113,6 +114,12 @@ public: return Rand64() % y; } + /// Make this like a C++11 random device + typedef uint32_t result_type; + uint32_t operator()() { return Rand32(); } + static constexpr result_type min() { return 0; } + static constexpr result_type max() { return 0x7ffff; } + private: unsigned Seed; }; @@ -417,7 +424,9 @@ struct AllocaModifier: public Modifier { void Act() override { Type *Tp = pickType(); - PT->push_back(new AllocaInst(Tp, "A", BB->getFirstNonPHI())); + const DataLayout &DL = BB->getModule()->getDataLayout(); + PT->push_back(new AllocaInst(Tp, DL.getAllocaAddrSpace(), + "A", BB->getFirstNonPHI())); } }; @@ -662,7 +671,7 @@ static void IntroduceControlFlow(Function *F, Random &R) { BoolInst.push_back(&Instr); } - std::random_shuffle(BoolInst.begin(), BoolInst.end(), R); + std::shuffle(BoolInst.begin(), BoolInst.end(), R); for (auto *Instr : BoolInst) { BasicBlock *Curr = Instr->getParent(); diff --git a/tools/llvm-strings/llvm-strings.cpp b/tools/llvm-strings/llvm-strings.cpp index e750995331e33..5053e9b97b78d 100644 --- a/tools/llvm-strings/llvm-strings.cpp +++ b/tools/llvm-strings/llvm-strings.cpp @@ -15,6 +15,7 @@ #include "llvm/Object/Binary.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Program.h" @@ -40,27 +41,50 @@ static cl::opt<int> cl::init(4)); static cl::alias MinLengthShort("n", cl::desc(""), cl::aliasopt(MinLength)); +enum radix { none, octal, hexadecimal, decimal }; +static cl::opt<radix> + Radix("radix", cl::desc("print the offset within the file"), + cl::values(clEnumValN(octal, "o", "octal"), + clEnumValN(hexadecimal, "x", "hexadecimal"), + clEnumValN(decimal, "d", "decimal")), + cl::init(none)); +static cl::alias RadixShort("t", cl::desc(""), cl::aliasopt(Radix)); + static void strings(raw_ostream &OS, StringRef FileName, StringRef Contents) { - auto print = [&OS, FileName](StringRef L) { + auto print = [&OS, FileName](unsigned Offset, StringRef L) { if (L.size() < static_cast<size_t>(MinLength)) return; if (PrintFileName) - OS << FileName << ": "; - OS << L << '\n'; + OS << FileName << ":"; + switch (Radix) { + case none: + break; + case octal: + OS << format("%8o", Offset); + break; + case hexadecimal: + OS << format("%8x", Offset); + break; + case decimal: + OS << format("%8u", Offset); + break; + } + OS << " " << L << '\n'; }; + const char *B = Contents.begin(); const char *P = nullptr, *E = nullptr, *S = nullptr; for (P = Contents.begin(), E = Contents.end(); P < E; ++P) { if (std::isgraph(*P) || std::isblank(*P)) { if (S == nullptr) S = P; } else if (S) { - print(StringRef(S, P - S)); + print(S - B, StringRef(S, P - S)); S = nullptr; } } if (S) - print(StringRef(S, E - S)); + print(S - B, StringRef(S, E - S)); } int main(int argc, char **argv) { diff --git a/tools/llvm-symbolizer/llvm-symbolizer.cpp b/tools/llvm-symbolizer/llvm-symbolizer.cpp index fc37dea4c484d..c9e0cc2b3b05c 100644 --- a/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/tools/llvm-symbolizer/llvm-symbolizer.cpp @@ -85,6 +85,9 @@ static cl::opt<int> ClPrintSourceContextLines( "print-source-context-lines", cl::init(0), cl::desc("Print N number of source file context")); +static cl::opt<bool> ClVerbose("verbose", cl::init(false), + cl::desc("Print verbose line info")); + template<typename T> static bool error(Expected<T> &ResOrErr) { if (ResOrErr) @@ -160,7 +163,7 @@ int main(int argc, char **argv) { LLVMSymbolizer Symbolizer(Opts); DIPrinter Printer(outs(), ClPrintFunctions != FunctionNameKind::None, - ClPrettyPrint, ClPrintSourceContextLines); + ClPrettyPrint, ClPrintSourceContextLines, ClVerbose); const int kMaxInputStringLength = 1024; char InputString[kMaxInputStringLength]; diff --git a/tools/llvm-xray/CMakeLists.txt b/tools/llvm-xray/CMakeLists.txt index abcd7d932110a..3baf4e64e81cb 100644 --- a/tools/llvm-xray/CMakeLists.txt +++ b/tools/llvm-xray/CMakeLists.txt @@ -9,9 +9,11 @@ set(LLVM_LINK_COMPONENTS set(LLVM_XRAY_TOOLS func-id-helper.cc xray-account.cc + xray-color-helper.cc xray-converter.cc xray-extract.cc xray-extract.cc + xray-graph.cc xray-registry.cc) add_llvm_tool(llvm-xray llvm-xray.cc ${LLVM_XRAY_TOOLS}) diff --git a/tools/llvm-xray/llvm-xray.cc b/tools/llvm-xray/llvm-xray.cc index ac5faaa408b50..98303e7be15c0 100644 --- a/tools/llvm-xray/llvm-xray.cc +++ b/tools/llvm-xray/llvm-xray.cc @@ -30,12 +30,20 @@ int main(int argc, char *argv[]) { " This program consolidates multiple XRay trace " "processing tools for convenient access.\n"); for (auto *SC : cl::getRegisteredSubcommands()) { - if (*SC) + if (*SC) { + // If no subcommand was provided, we need to explicitly check if this is + // the top-level subcommand. + if (SC == &*cl::TopLevelSubCommand) { + cl::PrintHelpMessage(false, true); + return 0; + } if (auto C = dispatch(SC)) { ExitOnError("llvm-xray: ")(C()); return 0; } + } } + // If all else fails, we still print the usage message. cl::PrintHelpMessage(false, true); } diff --git a/tools/llvm-xray/xray-account.cc b/tools/llvm-xray/xray-account.cc index 671a5a073eecc..13654c3911f77 100644 --- a/tools/llvm-xray/xray-account.cc +++ b/tools/llvm-xray/xray-account.cc @@ -18,10 +18,10 @@ #include <utility> #include "xray-account.h" -#include "xray-extract.h" #include "xray-registry.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FormatVariadic.h" +#include "llvm/XRay/InstrumentationMap.h" #include "llvm/XRay/Trace.h" using namespace llvm; @@ -120,16 +120,6 @@ static cl::opt<std::string> static cl::alias AccountInstrMap2("m", cl::aliasopt(AccountInstrMap), cl::desc("Alias for -instr_map"), cl::sub(Account)); -static cl::opt<InstrumentationMapExtractor::InputFormats> InstrMapFormat( - "instr-map-format", cl::desc("format of instrumentation map"), - cl::values(clEnumValN(InstrumentationMapExtractor::InputFormats::ELF, "elf", - "instrumentation map in an ELF header"), - clEnumValN(InstrumentationMapExtractor::InputFormats::YAML, - "yaml", "instrumentation map in YAML")), - cl::sub(Account), cl::init(InstrumentationMapExtractor::InputFormats::ELF)); -static cl::alias InstrMapFormat2("t", cl::aliasopt(InstrMapFormat), - cl::desc("Alias for -instr-map-format"), - cl::sub(Account)); namespace { @@ -418,67 +408,63 @@ void LatencyAccountant::exportStatsAsCSV(raw_ostream &OS, using namespace llvm::xray; static CommandRegistration Unused(&Account, []() -> Error { - int Fd; - auto EC = sys::fs::openFileForRead(AccountInput, Fd); - if (EC) - return make_error<StringError>( - Twine("Cannot open file '") + AccountInput + "'", EC); - - Error Err = Error::success(); - xray::InstrumentationMapExtractor Extractor(AccountInstrMap, InstrMapFormat, - Err); - if (auto E = handleErrors( - std::move(Err), [&](std::unique_ptr<StringError> SE) -> Error { - if (SE->convertToErrorCode() == std::errc::no_such_file_or_directory) - return Error::success(); - return Error(std::move(SE)); - })) - return E; + InstrumentationMap Map; + if (!AccountInstrMap.empty()) { + auto InstrumentationMapOrError = loadInstrumentationMap(AccountInstrMap); + if (!InstrumentationMapOrError) + return joinErrors(make_error<StringError>( + Twine("Cannot open instrumentation map '") + + AccountInstrMap + "'", + std::make_error_code(std::errc::invalid_argument)), + InstrumentationMapOrError.takeError()); + Map = std::move(*InstrumentationMapOrError); + } + std::error_code EC; raw_fd_ostream OS(AccountOutput, EC, sys::fs::OpenFlags::F_Text); if (EC) return make_error<StringError>( Twine("Cannot open file '") + AccountOutput + "' for writing.", EC); - const auto &FunctionAddresses = Extractor.getFunctionAddresses(); + const auto &FunctionAddresses = Map.getFunctionAddresses(); symbolize::LLVMSymbolizer::Options Opts( symbolize::FunctionNameKind::LinkageName, true, true, false, ""); symbolize::LLVMSymbolizer Symbolizer(Opts); llvm::xray::FuncIdConversionHelper FuncIdHelper(AccountInstrMap, Symbolizer, FunctionAddresses); xray::LatencyAccountant FCA(FuncIdHelper, AccountDeduceSiblingCalls); - if (auto TraceOrErr = loadTraceFile(AccountInput)) { - auto &T = *TraceOrErr; - for (const auto &Record : T) { - if (FCA.accountRecord(Record)) - continue; - for (const auto &ThreadStack : FCA.getPerThreadFunctionStack()) { - errs() << "Thread ID: " << ThreadStack.first << "\n"; - auto Level = ThreadStack.second.size(); - for (const auto &Entry : llvm::reverse(ThreadStack.second)) - errs() << "#" << Level-- << "\t" - << FuncIdHelper.SymbolOrNumber(Entry.first) << '\n'; - } - if (!AccountKeepGoing) - return make_error<StringError>( - Twine("Failed accounting function calls in file '") + AccountInput + - "'.", - std::make_error_code(std::errc::executable_format_error)); - } - switch (AccountOutputFormat) { - case AccountOutputFormats::TEXT: - FCA.exportStatsAsText(OS, T.getFileHeader()); - break; - case AccountOutputFormats::CSV: - FCA.exportStatsAsCSV(OS, T.getFileHeader()); - break; - } - } else { + auto TraceOrErr = loadTraceFile(AccountInput); + if (!TraceOrErr) return joinErrors( make_error<StringError>( Twine("Failed loading input file '") + AccountInput + "'", std::make_error_code(std::errc::executable_format_error)), TraceOrErr.takeError()); + + auto &T = *TraceOrErr; + for (const auto &Record : T) { + if (FCA.accountRecord(Record)) + continue; + for (const auto &ThreadStack : FCA.getPerThreadFunctionStack()) { + errs() << "Thread ID: " << ThreadStack.first << "\n"; + auto Level = ThreadStack.second.size(); + for (const auto &Entry : llvm::reverse(ThreadStack.second)) + errs() << "#" << Level-- << "\t" + << FuncIdHelper.SymbolOrNumber(Entry.first) << '\n'; + } + if (!AccountKeepGoing) + return make_error<StringError>( + Twine("Failed accounting function calls in file '") + AccountInput + + "'.", + std::make_error_code(std::errc::executable_format_error)); + } + switch (AccountOutputFormat) { + case AccountOutputFormats::TEXT: + FCA.exportStatsAsText(OS, T.getFileHeader()); + break; + case AccountOutputFormats::CSV: + FCA.exportStatsAsCSV(OS, T.getFileHeader()); + break; } return Error::success(); diff --git a/tools/llvm-xray/xray-color-helper.cc b/tools/llvm-xray/xray-color-helper.cc new file mode 100644 index 0000000000000..925bb7483d8f0 --- /dev/null +++ b/tools/llvm-xray/xray-color-helper.cc @@ -0,0 +1,198 @@ +//===-- xray-graph.cc - XRay Function Call Graph Renderer -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// A class to get a color from a specified gradient. +// +//===----------------------------------------------------------------------===// +#include <algorithm> + +#include "xray-color-helper.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace xray; + +// Sequential ColorMaps, which are used to represent information +// from some minimum to some maximum. + +static const std::tuple<uint8_t, uint8_t, uint8_t> SequentialMaps[][9] = { + {// The greys color scheme from http://colorbrewer2.org/ + std::make_tuple(255, 255, 255), std::make_tuple(240, 240, 240), + std::make_tuple(217, 217, 217), std::make_tuple(189, 189, 189), + std::make_tuple(150, 150, 150), std::make_tuple(115, 115, 115), + std::make_tuple(82, 82, 82), std::make_tuple(37, 37, 37), + std::make_tuple(0, 0, 0)}, + {// The OrRd color scheme from http://colorbrewer2.org/ + std::make_tuple(255, 247, 236), std::make_tuple(254, 232, 200), + std::make_tuple(253, 212, 158), std::make_tuple(253, 187, 132), + std::make_tuple(252, 141, 89), std::make_tuple(239, 101, 72), + std::make_tuple(215, 48, 31), std::make_tuple(179, 0, 0), + std::make_tuple(127, 0, 0)}, + {// The PuBu color scheme from http://colorbrewer2.org/ + std::make_tuple(255, 247, 251), std::make_tuple(236, 231, 242), + std::make_tuple(208, 209, 230), std::make_tuple(166, 189, 219), + std::make_tuple(116, 169, 207), std::make_tuple(54, 144, 192), + std::make_tuple(5, 112, 176), std::make_tuple(4, 90, 141), + std::make_tuple(2, 56, 88)}}; + +ColorHelper::ColorHelper(ColorHelper::SequentialScheme S) + : MinIn(0.0), MaxIn(1.0), ColorMap(SequentialMaps[static_cast<int>(S)]) {} + +// Diverging ColorMaps, which are used to represent information +// representing differenes, or a range that goes from negative to positive. +// These take an input in the range [-1,1]. + +static const std::tuple<uint8_t, uint8_t, uint8_t> DivergingCoeffs[][11] = { + {// The PiYG color scheme from http://colorbrewer2.org/ + std::make_tuple(142, 1, 82), std::make_tuple(197, 27, 125), + std::make_tuple(222, 119, 174), std::make_tuple(241, 182, 218), + std::make_tuple(253, 224, 239), std::make_tuple(247, 247, 247), + std::make_tuple(230, 245, 208), std::make_tuple(184, 225, 134), + std::make_tuple(127, 188, 65), std::make_tuple(77, 146, 33), + std::make_tuple(39, 100, 25)}}; + +ColorHelper::ColorHelper(ColorHelper::DivergingScheme S) + : MinIn(-1.0), MaxIn(1.0), ColorMap(DivergingCoeffs[static_cast<int>(S)]) {} + +// Takes a tuple of uint8_ts representing a color in RGB and converts them to +// HSV represented by a tuple of doubles +static std::tuple<double, double, double> +convertToHSV(const std::tuple<uint8_t, uint8_t, uint8_t> &Color) { + double Scaled[3] = {std::get<0>(Color) / 255.0, std::get<1>(Color) / 255.0, + std::get<2>(Color) / 255.0}; + int Min = 0; + int Max = 0; + for (int i = 1; i < 3; ++i) { + if (Scaled[i] < Scaled[Min]) + Min = i; + if (Scaled[i] > Scaled[Max]) + Max = i; + } + + double C = Scaled[Max] - Scaled[Min]; + + double HPrime = (Scaled[(Max + 1) % 3] - Scaled[(Max + 2) % 3]) / C; + HPrime = HPrime + 2.0 * Max; + + double H = (HPrime < 0) ? (HPrime + 6.0) * 60 + : HPrime * 60; // Scale to between 0 and 360 + + double V = Scaled[Max]; + + double S = (V == 0.0) ? 0.0 : C / V; + + return std::make_tuple(H, S, V); +} + +// Takes a double precision number, clips it between 0 and 1 and then converts +// that to an integer between 0x00 and 0xFF with proxpper rounding. +static uint8_t unitIntervalTo8BitChar(double B) { + double n = std::max(std::min(B, 1.0), 0.0); + return static_cast<uint8_t>(255 * n + 0.5); +} + +// Takes a typle of doubles representing a color in HSV and converts them to +// RGB represented as a tuple of uint8_ts +static std::tuple<uint8_t, uint8_t, uint8_t> +convertToRGB(const std::tuple<double, double, double> &Color) { + const double &H = std::get<0>(Color); + const double &S = std::get<1>(Color); + const double &V = std::get<2>(Color); + + double C = V * S; + + double HPrime = H / 60; + double X = C * (1 - std::abs(std::fmod(HPrime, 2.0) - 1)); + + double RGB1[3]; + int HPrimeInt = static_cast<int>(HPrime); + if (HPrimeInt % 2 == 0) { + RGB1[(HPrimeInt / 2) % 3] = C; + RGB1[(HPrimeInt / 2 + 1) % 3] = X; + RGB1[(HPrimeInt / 2 + 2) % 3] = 0.0; + } else { + RGB1[(HPrimeInt / 2) % 3] = X; + RGB1[(HPrimeInt / 2 + 1) % 3] = C; + RGB1[(HPrimeInt / 2 + 2) % 3] = 0.0; + } + + double Min = V - C; + double RGB2[3] = {RGB1[0] + Min, RGB1[1] + Min, RGB1[2] + Min}; + + return std::make_tuple(unitIntervalTo8BitChar(RGB2[0]), + unitIntervalTo8BitChar(RGB2[1]), + unitIntervalTo8BitChar(RGB2[2])); +} + +// The Hue component of the HSV interpolation Routine +static double interpolateHue(double H0, double H1, double T) { + double D = H1 - H0; + if (H0 > H1) { + std::swap(H0, H1); + + D = -D; + T = 1 - T; + } + + if (D <= 180) { + return H0 + T * (H1 - H0); + } else { + H0 = H0 + 360; + return std::fmod(H0 + T * (H1 - H0) + 720, 360); + } +} + +// Interpolates between two HSV Colors both represented as a tuple of doubles +// Returns an HSV Color represented as a tuple of doubles +static std::tuple<double, double, double> +interpolateHSV(const std::tuple<double, double, double> &C0, + const std::tuple<double, double, double> &C1, double T) { + double H = interpolateHue(std::get<0>(C0), std::get<0>(C1), T); + double S = std::get<1>(C0) + T * (std::get<1>(C1) - std::get<1>(C0)); + double V = std::get<2>(C0) + T * (std::get<2>(C1) - std::get<2>(C0)); + return std::make_tuple(H, S, V); +} + +// Get the Color as a tuple of uint8_ts +std::tuple<uint8_t, uint8_t, uint8_t> +ColorHelper::getColorTuple(double Point) const { + assert(!ColorMap.empty() && "ColorMap must not be empty!"); + size_t MaxIndex = ColorMap.size() - 1; + double IntervalWidth = MaxIn - MinIn; + double OffsetP = Point - MinIn; + double SectionWidth = IntervalWidth / static_cast<double>(MaxIndex); + size_t SectionNo = std::floor(OffsetP / SectionWidth); + double T = (OffsetP - SectionNo * SectionWidth) / SectionWidth; + + auto &RGBColor0 = ColorMap[SectionNo]; + auto &RGBColor1 = ColorMap[std::min(SectionNo + 1, MaxIndex)]; + + auto HSVColor0 = convertToHSV(RGBColor0); + auto HSVColor1 = convertToHSV(RGBColor1); + + auto InterpolatedHSVColor = interpolateHSV(HSVColor0, HSVColor1, T); + return convertToRGB(InterpolatedHSVColor); +} + +// A helper method to convert a color represented as tuple of uint8s to a hex +// string. +std::string +ColorHelper::getColorString(std::tuple<uint8_t, uint8_t, uint8_t> t) { + return llvm::formatv("#{0:X-2}{1:X-2}{2:X-2}", std::get<0>(t), std::get<1>(t), + std::get<2>(t)); +} + +// Gets a color in a gradient given a number in the interval [0,1], it does this +// by evaluating a polynomial which maps [0, 1] -> [0, 1] for each of the R G +// and B values in the color. It then converts this [0,1] colors to a 24 bit +// color as a hex string. +std::string ColorHelper::getColorString(double Point) const { + return getColorString(getColorTuple(Point)); +} diff --git a/tools/llvm-xray/xray-color-helper.h b/tools/llvm-xray/xray-color-helper.h new file mode 100644 index 0000000000000..d3c77de03cb29 --- /dev/null +++ b/tools/llvm-xray/xray-color-helper.h @@ -0,0 +1,81 @@ +//===-- xray-graph.h - XRay Function Call Graph Renderer --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// A class to get a color from a specified gradient. +// +//===----------------------------------------------------------------------===// + +#ifndef XRAY_COLOR_HELPER_H +#define XRAY_COLOR_HELPER_H + +#include <tuple> + +#include "llvm/ADT/ArrayRef.h" + +namespace llvm { +namespace xray { + +/// The color helper class it a healper class which allows you to easily get a +/// color in a gradient. This is used to color-code edges in XRay-Graph tools. +/// +/// There are two types of color schemes in this class: +/// - Sequential schemes, which are used to represent information from some +/// minimum to some maximum. These take an input in the range [0,1] +/// - Diverging schemes, which are used to represent information representing +/// differenes, or a range that goes from negative to positive. These take +/// an input in the range [-1,1]. +/// Usage; +/// ColorHelper S(ColorHelper::SequentialScheme::OrRd); //Chose a color scheme. +/// for (double p = 0.0; p <= 1; p += 0.1){ +/// cout() << S.getColor(p) << " \n"; // Sample the gradient at 0.1 intervals +/// } +/// +/// ColorHelper D(ColorHelper::DivergingScheme::Spectral); // Choose a color +/// // scheme. +/// for (double p= -1; p <= 1 ; p += 0.1){ +/// cout() << D.getColor(p) << " \n"; // sample the gradient at 0.1 intervals +/// } +class ColorHelper { + double MinIn; + double MaxIn; + + ArrayRef<std::tuple<uint8_t, uint8_t, uint8_t>> ColorMap; + +public: + /// Enum of the availible Sequential Color Schemes + enum class SequentialScheme { + // Schemes based on the ColorBrewer Color schemes of the same name from + // http://www.colorbrewer.org/ by Cynthis A Brewer Penn State University. + Greys, + OrRd, + PuBu + }; + + ColorHelper(SequentialScheme S); + + /// Enum of the availible Diverging Color Schemes + enum class DivergingScheme { + // Schemes based on the ColorBrewer Color schemes of the same name from + // http://www.colorbrewer.org/ by Cynthis A Brewer Penn State University. + PiYG + }; + + ColorHelper(DivergingScheme S); + + // Sample the gradient at the input point. + std::tuple<uint8_t, uint8_t, uint8_t> getColorTuple(double Point) const; + + std::string getColorString(double Point) const; + + // Convert a tuple to a string + static std::string getColorString(std::tuple<uint8_t, uint8_t, uint8_t> t); +}; +} +} +#endif diff --git a/tools/llvm-xray/xray-converter.cc b/tools/llvm-xray/xray-converter.cc index 1bc9b15ae12cd..2583ec951495b 100644 --- a/tools/llvm-xray/xray-converter.cc +++ b/tools/llvm-xray/xray-converter.cc @@ -12,13 +12,14 @@ //===----------------------------------------------------------------------===// #include "xray-converter.h" -#include "xray-extract.h" #include "xray-registry.h" #include "llvm/DebugInfo/Symbolize/Symbolize.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/XRay/InstrumentationMap.h" #include "llvm/XRay/Trace.h" #include "llvm/XRay/YAMLXRayRecord.h" @@ -72,18 +73,7 @@ static cl::opt<bool> ConvertSortInput( static cl::alias ConvertSortInput2("s", cl::aliasopt(ConvertSortInput), cl::desc("Alias for -sort"), cl::sub(Convert)); -static cl::opt<InstrumentationMapExtractor::InputFormats> InstrMapFormat( - "instr-map-format", cl::desc("format of instrumentation map"), - cl::values(clEnumValN(InstrumentationMapExtractor::InputFormats::ELF, "elf", - "instrumentation map in an ELF header"), - clEnumValN(InstrumentationMapExtractor::InputFormats::YAML, - "yaml", "instrumentation map in YAML")), - cl::sub(Convert), cl::init(InstrumentationMapExtractor::InputFormats::ELF)); -static cl::alias InstrMapFormat2("t", cl::aliasopt(InstrMapFormat), - cl::desc("Alias for -instr-map-format"), - cl::sub(Convert)); - -using llvm::yaml::IO; + using llvm::yaml::Output; void TraceConverter::exportAsYAML(const Trace &Records, raw_ostream &OS) { @@ -95,7 +85,7 @@ void TraceConverter::exportAsYAML(const Trace &Records, raw_ostream &OS) { for (const auto &R : Records) { Trace.Records.push_back({R.RecordType, R.CPU, R.Type, R.FuncId, Symbolize ? FuncIdHelper.SymbolOrNumber(R.FuncId) - : std::to_string(R.FuncId), + : llvm::to_string(R.FuncId), R.TSC, R.TId}); } Output Out(OS, nullptr, 0); @@ -128,7 +118,9 @@ void TraceConverter::exportAsRAWv1(const Trace &Records, raw_ostream &OS) { // format. for (const auto &R : Records) { Writer.write(R.RecordType); - Writer.write(R.CPU); + // The on disk naive raw format uses 8 bit CPUs, but the record has 16. + // There's no choice but truncation. + Writer.write(static_cast<uint8_t>(R.CPU)); switch (R.Type) { case RecordTypes::ENTER: Writer.write(uint8_t{0}); @@ -151,25 +143,26 @@ namespace xray { static CommandRegistration Unused(&Convert, []() -> Error { // FIXME: Support conversion to BINARY when upgrading XRay trace versions. - int Fd; - auto EC = sys::fs::openFileForRead(ConvertInput, Fd); - if (EC) - return make_error<StringError>( - Twine("Cannot open file '") + ConvertInput + "'", EC); - - Error Err = Error::success(); - xray::InstrumentationMapExtractor Extractor(ConvertInstrMap, InstrMapFormat, - Err); - handleAllErrors(std::move(Err), - [&](const ErrorInfoBase &E) { E.log(errs()); }); + InstrumentationMap Map; + if (!ConvertInstrMap.empty()) { + auto InstrumentationMapOrError = loadInstrumentationMap(ConvertInstrMap); + if (!InstrumentationMapOrError) + return joinErrors(make_error<StringError>( + Twine("Cannot open instrumentation map '") + + ConvertInstrMap + "'", + std::make_error_code(std::errc::invalid_argument)), + InstrumentationMapOrError.takeError()); + Map = std::move(*InstrumentationMapOrError); + } - const auto &FunctionAddresses = Extractor.getFunctionAddresses(); + const auto &FunctionAddresses = Map.getFunctionAddresses(); symbolize::LLVMSymbolizer::Options Opts( symbolize::FunctionNameKind::LinkageName, true, true, false, ""); symbolize::LLVMSymbolizer Symbolizer(Opts); llvm::xray::FuncIdConversionHelper FuncIdHelper(ConvertInstrMap, Symbolizer, FunctionAddresses); llvm::xray::TraceConverter TC(FuncIdHelper, ConvertSymbolize); + std::error_code EC; raw_fd_ostream OS(ConvertOutput, EC, ConvertOutputFormat == ConvertFormats::BINARY ? sys::fs::OpenFlags::F_None @@ -178,22 +171,22 @@ static CommandRegistration Unused(&Convert, []() -> Error { return make_error<StringError>( Twine("Cannot open file '") + ConvertOutput + "' for writing.", EC); - if (auto TraceOrErr = loadTraceFile(ConvertInput, ConvertSortInput)) { - auto &T = *TraceOrErr; - switch (ConvertOutputFormat) { - case ConvertFormats::YAML: - TC.exportAsYAML(T, OS); - break; - case ConvertFormats::BINARY: - TC.exportAsRAWv1(T, OS); - break; - } - } else { + auto TraceOrErr = loadTraceFile(ConvertInput, ConvertSortInput); + if (!TraceOrErr) return joinErrors( make_error<StringError>( Twine("Failed loading input file '") + ConvertInput + "'.", std::make_error_code(std::errc::executable_format_error)), TraceOrErr.takeError()); + + auto &T = *TraceOrErr; + switch (ConvertOutputFormat) { + case ConvertFormats::YAML: + TC.exportAsYAML(T, OS); + break; + case ConvertFormats::BINARY: + TC.exportAsRAWv1(T, OS); + break; } return Error::success(); }); diff --git a/tools/llvm-xray/xray-extract.cc b/tools/llvm-xray/xray-extract.cc index ecd535114a62a..26e461869a083 100644 --- a/tools/llvm-xray/xray-extract.cc +++ b/tools/llvm-xray/xray-extract.cc @@ -16,10 +16,7 @@ #include <type_traits> #include <utility> -#include "xray-extract.h" - #include "xray-registry.h" -#include "xray-sleds.h" #include "llvm/Object/ELF.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" @@ -28,8 +25,8 @@ #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" -#include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/XRay/InstrumentationMap.h" using namespace llvm; using namespace llvm::xray; @@ -49,243 +46,40 @@ static cl::alias ExtractOutput2("o", cl::aliasopt(ExtractOutput), cl::desc("Alias for -output"), cl::sub(Extract)); -struct YAMLXRaySledEntry { - int32_t FuncId; - Hex64 Address; - Hex64 Function; - SledEntry::FunctionKinds Kind; - bool AlwaysInstrument; -}; - -namespace llvm { -namespace yaml { - -template <> struct ScalarEnumerationTraits<SledEntry::FunctionKinds> { - static void enumeration(IO &IO, SledEntry::FunctionKinds &Kind) { - IO.enumCase(Kind, "function-enter", SledEntry::FunctionKinds::ENTRY); - IO.enumCase(Kind, "function-exit", SledEntry::FunctionKinds::EXIT); - IO.enumCase(Kind, "tail-exit", SledEntry::FunctionKinds::TAIL); - } -}; - -template <> struct MappingTraits<YAMLXRaySledEntry> { - static void mapping(IO &IO, YAMLXRaySledEntry &Entry) { - IO.mapRequired("id", Entry.FuncId); - IO.mapRequired("address", Entry.Address); - IO.mapRequired("function", Entry.Function); - IO.mapRequired("kind", Entry.Kind); - IO.mapRequired("always-instrument", Entry.AlwaysInstrument); - } - - static constexpr bool flow = true; -}; -} -} - -LLVM_YAML_IS_SEQUENCE_VECTOR(YAMLXRaySledEntry) - namespace { -llvm::Error LoadBinaryInstrELF( - StringRef Filename, std::deque<SledEntry> &OutputSleds, - InstrumentationMapExtractor::FunctionAddressMap &InstrMap, - InstrumentationMapExtractor::FunctionAddressReverseMap &FunctionIds) { - auto ObjectFile = object::ObjectFile::createObjectFile(Filename); - - if (!ObjectFile) - return ObjectFile.takeError(); - - // FIXME: Maybe support other ELF formats. For now, 64-bit Little Endian only. - if (!ObjectFile->getBinary()->isELF()) - return make_error<StringError>( - "File format not supported (only does ELF).", - std::make_error_code(std::errc::not_supported)); - if (ObjectFile->getBinary()->getArch() != Triple::x86_64) - return make_error<StringError>( - "File format not supported (only does ELF little endian 64-bit).", - std::make_error_code(std::errc::not_supported)); - - // Find the section named "xray_instr_map". - StringRef Contents = ""; - const auto &Sections = ObjectFile->getBinary()->sections(); - auto I = find_if(Sections, [&](object::SectionRef Section) { - StringRef Name = ""; - if (Section.getName(Name)) - return false; - return Name == "xray_instr_map"; - }); - if (I == Sections.end()) - return make_error<StringError>( - "Failed to find XRay instrumentation map.", - std::make_error_code(std::errc::not_supported)); - if (I->getContents(Contents)) - return make_error<StringError>( - "Failed to get contents of 'xray_instr_map' section.", - std::make_error_code(std::errc::executable_format_error)); - - // Copy the instrumentation map data into the Sleds data structure. - auto C = Contents.bytes_begin(); - static constexpr size_t ELF64SledEntrySize = 32; - - if ((C - Contents.bytes_end()) % ELF64SledEntrySize != 0) - return make_error<StringError>( - "Instrumentation map entries not evenly divisible by size of an XRay " - "sled entry in ELF64.", - std::make_error_code(std::errc::executable_format_error)); - - int32_t FuncId = 1; - uint64_t CurFn = 0; - std::deque<SledEntry> Sleds; - for (; C != Contents.bytes_end(); C += ELF64SledEntrySize) { - DataExtractor Extractor( - StringRef(reinterpret_cast<const char *>(C), ELF64SledEntrySize), true, - 8); - Sleds.push_back({}); - auto &Entry = Sleds.back(); - uint32_t OffsetPtr = 0; - Entry.Address = Extractor.getU64(&OffsetPtr); - Entry.Function = Extractor.getU64(&OffsetPtr); - auto Kind = Extractor.getU8(&OffsetPtr); - switch (Kind) { - case 0: // ENTRY - Entry.Kind = SledEntry::FunctionKinds::ENTRY; - break; - case 1: // EXIT - Entry.Kind = SledEntry::FunctionKinds::EXIT; - break; - case 2: // TAIL - Entry.Kind = SledEntry::FunctionKinds::TAIL; - break; - default: - return make_error<StringError>( - Twine("Encountered unknown sled type ") + "'" + Twine(int32_t{Kind}) + - "'.", - std::make_error_code(std::errc::executable_format_error)); - } - Entry.AlwaysInstrument = Extractor.getU8(&OffsetPtr) != 0; - - // We replicate the function id generation scheme implemented in the runtime - // here. Ideally we should be able to break it out, or output this map from - // the runtime, but that's a design point we can discuss later on. For now, - // we replicate the logic and move on. - if (CurFn == 0) { - CurFn = Entry.Function; - InstrMap[FuncId] = Entry.Function; - FunctionIds[Entry.Function] = FuncId; - } - if (Entry.Function != CurFn) { - ++FuncId; - CurFn = Entry.Function; - InstrMap[FuncId] = Entry.Function; - FunctionIds[Entry.Function] = FuncId; - } - } - OutputSleds = std::move(Sleds); - return llvm::Error::success(); -} - -Error LoadYAMLInstrMap( - StringRef Filename, std::deque<SledEntry> &Sleds, - InstrumentationMapExtractor::FunctionAddressMap &InstrMap, - InstrumentationMapExtractor::FunctionAddressReverseMap &FunctionIds) { - int Fd; - if (auto EC = sys::fs::openFileForRead(Filename, Fd)) - return make_error<StringError>( - Twine("Failed opening file '") + Filename + "' for reading.", EC); - - uint64_t FileSize; - if (auto EC = sys::fs::file_size(Filename, FileSize)) - return make_error<StringError>( - Twine("Failed getting size of file '") + Filename + "'.", EC); - - std::error_code EC; - sys::fs::mapped_file_region MappedFile( - Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC); - if (EC) - return make_error<StringError>( - Twine("Failed memory-mapping file '") + Filename + "'.", EC); - - std::vector<YAMLXRaySledEntry> YAMLSleds; - Input In(StringRef(MappedFile.data(), MappedFile.size())); - In >> YAMLSleds; - if (In.error()) - return make_error<StringError>( - Twine("Failed loading YAML document from '") + Filename + "'.", - In.error()); - - for (const auto &Y : YAMLSleds) { - InstrMap[Y.FuncId] = Y.Function; - FunctionIds[Y.Function] = Y.FuncId; - Sleds.push_back( - SledEntry{Y.Address, Y.Function, Y.Kind, Y.AlwaysInstrument}); - } - return Error::success(); -} - -} // namespace - -InstrumentationMapExtractor::InstrumentationMapExtractor(std::string Filename, - InputFormats Format, - Error &EC) { - ErrorAsOutParameter ErrAsOutputParam(&EC); - if (Filename.empty()) { - EC = Error::success(); - return; - } - switch (Format) { - case InputFormats::ELF: { - EC = handleErrors( - LoadBinaryInstrELF(Filename, Sleds, FunctionAddresses, FunctionIds), - [&](std::unique_ptr<ErrorInfoBase> E) { - return joinErrors( - make_error<StringError>( - Twine("Cannot extract instrumentation map from '") + - Filename + "'.", - std::make_error_code(std::errc::executable_format_error)), - std::move(E)); - }); - break; - } - case InputFormats::YAML: { - EC = handleErrors( - LoadYAMLInstrMap(Filename, Sleds, FunctionAddresses, FunctionIds), - [&](std::unique_ptr<ErrorInfoBase> E) { - return joinErrors( - make_error<StringError>( - Twine("Cannot load YAML instrumentation map from '") + - Filename + "'.", - std::make_error_code(std::errc::executable_format_error)), - std::move(E)); - }); - break; - } - } -} - -void InstrumentationMapExtractor::exportAsYAML(raw_ostream &OS) { +void exportAsYAML(const InstrumentationMap &Map, raw_ostream &OS) { // First we translate the sleds into the YAMLXRaySledEntry objects in a deque. std::vector<YAMLXRaySledEntry> YAMLSleds; - YAMLSleds.reserve(Sleds.size()); + auto Sleds = Map.sleds(); + YAMLSleds.reserve(std::distance(Sleds.begin(), Sleds.end())); for (const auto &Sled : Sleds) { - YAMLSleds.push_back({FunctionIds[Sled.Function], Sled.Address, - Sled.Function, Sled.Kind, Sled.AlwaysInstrument}); + auto FuncId = Map.getFunctionId(Sled.Function); + if (!FuncId) + return; + YAMLSleds.push_back({*FuncId, Sled.Address, Sled.Function, Sled.Kind, + Sled.AlwaysInstrument}); } Output Out(OS, nullptr, 0); Out << YAMLSleds; } +} // namespace + static CommandRegistration Unused(&Extract, []() -> Error { - Error Err = Error::success(); - xray::InstrumentationMapExtractor Extractor( - ExtractInput, InstrumentationMapExtractor::InputFormats::ELF, Err); - if (Err) - return Err; + auto InstrumentationMapOrError = loadInstrumentationMap(ExtractInput); + if (!InstrumentationMapOrError) + return joinErrors(make_error<StringError>( + Twine("Cannot extract instrumentation map from '") + + ExtractInput + "'.", + std::make_error_code(std::errc::invalid_argument)), + InstrumentationMapOrError.takeError()); std::error_code EC; raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::F_Text); if (EC) return make_error<StringError>( Twine("Cannot open file '") + ExtractOutput + "' for writing.", EC); - Extractor.exportAsYAML(OS); + exportAsYAML(*InstrumentationMapOrError, OS); return Error::success(); }); diff --git a/tools/llvm-xray/xray-extract.h b/tools/llvm-xray/xray-extract.h deleted file mode 100644 index 91e4db36805fe..0000000000000 --- a/tools/llvm-xray/xray-extract.h +++ /dev/null @@ -1,58 +0,0 @@ -//===- xray-extract.h - XRay Instrumentation Map Extraction ---------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Defines the interface for extracting the instrumentation map from an -// XRay-instrumented binary. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_XRAY_EXTRACT_H -#define LLVM_TOOLS_XRAY_EXTRACT_H - -#include <deque> -#include <map> -#include <string> -#include <unordered_map> - -#include "xray-sleds.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/raw_ostream.h" - -namespace llvm { -namespace xray { - -class InstrumentationMapExtractor { -public: - typedef std::unordered_map<int32_t, uint64_t> FunctionAddressMap; - typedef std::unordered_map<uint64_t, int32_t> FunctionAddressReverseMap; - - enum class InputFormats { ELF, YAML }; - -private: - std::deque<SledEntry> Sleds; - FunctionAddressMap FunctionAddresses; - FunctionAddressReverseMap FunctionIds; - -public: - /// Loads the instrumentation map from |Filename|. Updates |EC| in case there - /// were errors encountered opening the file. |Format| defines what the input - /// instrumentation map is in. - InstrumentationMapExtractor(std::string Filename, InputFormats Format, - Error &EC); - - const FunctionAddressMap &getFunctionAddresses() { return FunctionAddresses; } - - /// Exports the loaded function address map as YAML through |OS|. - void exportAsYAML(raw_ostream &OS); -}; - -} // namespace xray -} // namespace llvm - -#endif // LLVM_TOOLS_XRAY_EXTRACT_H diff --git a/tools/llvm-xray/xray-graph.cc b/tools/llvm-xray/xray-graph.cc new file mode 100644 index 0000000000000..9be0b70c2cdd8 --- /dev/null +++ b/tools/llvm-xray/xray-graph.cc @@ -0,0 +1,529 @@ +//===-- xray-graph.cc - XRay Function Call Graph Renderer -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Generate a DOT file to represent the function call graph encountered in +// the trace. +// +//===----------------------------------------------------------------------===// +#include <algorithm> +#include <cassert> +#include <cmath> +#include <system_error> +#include <utility> + +#include "xray-graph.h" +#include "xray-registry.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/XRay/InstrumentationMap.h" +#include "llvm/XRay/Trace.h" +#include "llvm/XRay/YAMLXRayRecord.h" + +using namespace llvm; +using namespace llvm::xray; + +// Setup llvm-xray graph subcommand and its options. +static cl::SubCommand GraphC("graph", "Generate function-call graph"); +static cl::opt<std::string> GraphInput(cl::Positional, + cl::desc("<xray log file>"), + cl::Required, cl::sub(GraphC)); + +static cl::opt<bool> + GraphKeepGoing("keep-going", cl::desc("Keep going on errors encountered"), + cl::sub(GraphC), cl::init(false)); +static cl::alias GraphKeepGoing2("k", cl::aliasopt(GraphKeepGoing), + cl::desc("Alias for -keep-going"), + cl::sub(GraphC)); + +static cl::opt<std::string> + GraphOutput("output", cl::value_desc("Output file"), cl::init("-"), + cl::desc("output file; use '-' for stdout"), cl::sub(GraphC)); +static cl::alias GraphOutput2("o", cl::aliasopt(GraphOutput), + cl::desc("Alias for -output"), cl::sub(GraphC)); + +static cl::opt<std::string> + GraphInstrMap("instr_map", + cl::desc("binary with the instrumrntation map, or " + "a separate instrumentation map"), + cl::value_desc("binary with xray_instr_map"), cl::sub(GraphC), + cl::init("")); +static cl::alias GraphInstrMap2("m", cl::aliasopt(GraphInstrMap), + cl::desc("alias for -instr_map"), + cl::sub(GraphC)); + +static cl::opt<bool> GraphDeduceSiblingCalls( + "deduce-sibling-calls", + cl::desc("Deduce sibling calls when unrolling function call stacks"), + cl::sub(GraphC), cl::init(false)); +static cl::alias + GraphDeduceSiblingCalls2("d", cl::aliasopt(GraphDeduceSiblingCalls), + cl::desc("Alias for -deduce-sibling-calls"), + cl::sub(GraphC)); + +static cl::opt<GraphRenderer::StatType> + GraphEdgeLabel("edge-label", + cl::desc("Output graphs with edges labeled with this field"), + cl::value_desc("field"), cl::sub(GraphC), + cl::init(GraphRenderer::StatType::NONE), + cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", + "Do not label Edges"), + clEnumValN(GraphRenderer::StatType::COUNT, + "count", "function call counts"), + clEnumValN(GraphRenderer::StatType::MIN, "min", + "minimum function durations"), + clEnumValN(GraphRenderer::StatType::MED, "med", + "median function durations"), + clEnumValN(GraphRenderer::StatType::PCT90, "90p", + "90th percentile durations"), + clEnumValN(GraphRenderer::StatType::PCT99, "99p", + "99th percentile durations"), + clEnumValN(GraphRenderer::StatType::MAX, "max", + "maximum function durations"), + clEnumValN(GraphRenderer::StatType::SUM, "sum", + "sum of call durations"))); +static cl::alias GraphEdgeLabel2("e", cl::aliasopt(GraphEdgeLabel), + cl::desc("Alias for -edge-label"), + cl::sub(GraphC)); + +static cl::opt<GraphRenderer::StatType> GraphVertexLabel( + "vertex-label", + cl::desc("Output graphs with vertices labeled with this field"), + cl::value_desc("field"), cl::sub(GraphC), + cl::init(GraphRenderer::StatType::NONE), + cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", + "Do not label Edges"), + clEnumValN(GraphRenderer::StatType::COUNT, "count", + "function call counts"), + clEnumValN(GraphRenderer::StatType::MIN, "min", + "minimum function durations"), + clEnumValN(GraphRenderer::StatType::MED, "med", + "median function durations"), + clEnumValN(GraphRenderer::StatType::PCT90, "90p", + "90th percentile durations"), + clEnumValN(GraphRenderer::StatType::PCT99, "99p", + "99th percentile durations"), + clEnumValN(GraphRenderer::StatType::MAX, "max", + "maximum function durations"), + clEnumValN(GraphRenderer::StatType::SUM, "sum", + "sum of call durations"))); +static cl::alias GraphVertexLabel2("v", cl::aliasopt(GraphVertexLabel), + cl::desc("Alias for -edge-label"), + cl::sub(GraphC)); + +static cl::opt<GraphRenderer::StatType> GraphEdgeColorType( + "color-edges", + cl::desc("Output graphs with edge colors determined by this field"), + cl::value_desc("field"), cl::sub(GraphC), + cl::init(GraphRenderer::StatType::NONE), + cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", + "Do not label Edges"), + clEnumValN(GraphRenderer::StatType::COUNT, "count", + "function call counts"), + clEnumValN(GraphRenderer::StatType::MIN, "min", + "minimum function durations"), + clEnumValN(GraphRenderer::StatType::MED, "med", + "median function durations"), + clEnumValN(GraphRenderer::StatType::PCT90, "90p", + "90th percentile durations"), + clEnumValN(GraphRenderer::StatType::PCT99, "99p", + "99th percentile durations"), + clEnumValN(GraphRenderer::StatType::MAX, "max", + "maximum function durations"), + clEnumValN(GraphRenderer::StatType::SUM, "sum", + "sum of call durations"))); +static cl::alias GraphEdgeColorType2("c", cl::aliasopt(GraphEdgeColorType), + cl::desc("Alias for -color-edges"), + cl::sub(GraphC)); + +static cl::opt<GraphRenderer::StatType> GraphVertexColorType( + "color-vertices", + cl::desc("Output graphs with vertex colors determined by this field"), + cl::value_desc("field"), cl::sub(GraphC), + cl::init(GraphRenderer::StatType::NONE), + cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", + "Do not label Edges"), + clEnumValN(GraphRenderer::StatType::COUNT, "count", + "function call counts"), + clEnumValN(GraphRenderer::StatType::MIN, "min", + "minimum function durations"), + clEnumValN(GraphRenderer::StatType::MED, "med", + "median function durations"), + clEnumValN(GraphRenderer::StatType::PCT90, "90p", + "90th percentile durations"), + clEnumValN(GraphRenderer::StatType::PCT99, "99p", + "99th percentile durations"), + clEnumValN(GraphRenderer::StatType::MAX, "max", + "maximum function durations"), + clEnumValN(GraphRenderer::StatType::SUM, "sum", + "sum of call durations"))); +static cl::alias GraphVertexColorType2("b", cl::aliasopt(GraphVertexColorType), + cl::desc("Alias for -edge-label"), + cl::sub(GraphC)); + +template <class T> T diff(T L, T R) { return std::max(L, R) - std::min(L, R); } + +// Updates the statistics for a GraphRenderer::TimeStat +static void updateStat(GraphRenderer::TimeStat &S, int64_t L) { + S.Count++; + if (S.Min > L || S.Min == 0) + S.Min = L; + if (S.Max < L) + S.Max = L; + S.Sum += L; +} + +// Evaluates an XRay record and performs accounting on it. +// +// If the record is an ENTER record it pushes the FuncID and TSC onto a +// structure representing the call stack for that function. +// If the record is an EXIT record it checks computes computes the ammount of +// time the function took to complete and then stores that information in an +// edge of the graph. If there is no matching ENTER record the function tries +// to recover by assuming that there were EXIT records which were missed, for +// example caused by tail call elimination and if the option is enabled then +// then tries to recover from this. +// +// This funciton will also error if the records are out of order, as the trace +// is expected to be sorted. +// +// The graph generated has an immaginary root for functions called by no-one at +// FuncId 0. +// +// FIXME: Refactor this and account subcommand to reduce code duplication. +Error GraphRenderer::accountRecord(const XRayRecord &Record) { + using std::make_error_code; + using std::errc; + if (CurrentMaxTSC == 0) + CurrentMaxTSC = Record.TSC; + + if (Record.TSC < CurrentMaxTSC) + return make_error<StringError>("Records not in order", + make_error_code(errc::invalid_argument)); + + auto &ThreadStack = PerThreadFunctionStack[Record.TId]; + switch (Record.Type) { + case RecordTypes::ENTER: { + if (G.count(Record.FuncId) == 0) + G[Record.FuncId].SymbolName = FuncIdHelper.SymbolOrNumber(Record.FuncId); + ThreadStack.push_back({Record.FuncId, Record.TSC}); + break; + } + case RecordTypes::EXIT: { + // FIXME: Refactor this and the account subcommand to reduce code + // duplication + if (ThreadStack.size() == 0 || ThreadStack.back().FuncId != Record.FuncId) { + if (!DeduceSiblingCalls) + return make_error<StringError>("No matching ENTRY record", + make_error_code(errc::invalid_argument)); + auto Parent = std::find_if( + ThreadStack.rbegin(), ThreadStack.rend(), + [&](const FunctionAttr &A) { return A.FuncId == Record.FuncId; }); + if (Parent == ThreadStack.rend()) + return make_error<StringError>( + "No matching Entry record in stack", + make_error_code(errc::invalid_argument)); // There is no matching + // Function for this exit. + while (ThreadStack.back().FuncId != Record.FuncId) { + TimestampT D = diff(ThreadStack.back().TSC, Record.TSC); + VertexIdentifier TopFuncId = ThreadStack.back().FuncId; + ThreadStack.pop_back(); + assert(ThreadStack.size() != 0); + EdgeIdentifier EI(ThreadStack.back().FuncId, TopFuncId); + auto &EA = G[EI]; + EA.Timings.push_back(D); + updateStat(EA.S, D); + updateStat(G[TopFuncId].S, D); + } + } + uint64_t D = diff(ThreadStack.back().TSC, Record.TSC); + ThreadStack.pop_back(); + VertexIdentifier VI = ThreadStack.empty() ? 0 : ThreadStack.back().FuncId; + EdgeIdentifier EI(VI, Record.FuncId); + auto &EA = G[EI]; + EA.Timings.push_back(D); + updateStat(EA.S, D); + updateStat(G[Record.FuncId].S, D); + break; + } + } + + return Error::success(); +} + +template <typename U> +void GraphRenderer::getStats(U begin, U end, GraphRenderer::TimeStat &S) { + if (begin == end) return; + std::ptrdiff_t MedianOff = S.Count / 2; + std::nth_element(begin, begin + MedianOff, end); + S.Median = *(begin + MedianOff); + std::ptrdiff_t Pct90Off = (S.Count * 9) / 10; + std::nth_element(begin, begin + Pct90Off, end); + S.Pct90 = *(begin + Pct90Off); + std::ptrdiff_t Pct99Off = (S.Count * 99) / 100; + std::nth_element(begin, begin + Pct99Off, end); + S.Pct99 = *(begin + Pct99Off); +} + +void GraphRenderer::updateMaxStats(const GraphRenderer::TimeStat &S, + GraphRenderer::TimeStat &M) { + M.Count = std::max(M.Count, S.Count); + M.Min = std::max(M.Min, S.Min); + M.Median = std::max(M.Median, S.Median); + M.Pct90 = std::max(M.Pct90, S.Pct90); + M.Pct99 = std::max(M.Pct99, S.Pct99); + M.Max = std::max(M.Max, S.Max); + M.Sum = std::max(M.Sum, S.Sum); +} + +void GraphRenderer::calculateEdgeStatistics() { + assert(!G.edges().empty()); + for (auto &E : G.edges()) { + auto &A = E.second; + assert(!A.Timings.empty()); + getStats(A.Timings.begin(), A.Timings.end(), A.S); + updateMaxStats(A.S, G.GraphEdgeMax); + } +} + +void GraphRenderer::calculateVertexStatistics() { + std::vector<uint64_t> TempTimings; + for (auto &V : G.vertices()) { + if (V.first != 0) { + for (auto &E : G.inEdges(V.first)) { + auto &A = E.second; + TempTimings.insert(TempTimings.end(), A.Timings.begin(), + A.Timings.end()); + } + getStats(TempTimings.begin(), TempTimings.end(), G[V.first].S); + updateMaxStats(G[V.first].S, G.GraphVertexMax); + TempTimings.clear(); + } + } +} + +// A Helper function for normalizeStatistics which normalises a single +// TimeStat element. +static void normalizeTimeStat(GraphRenderer::TimeStat &S, + double CycleFrequency) { + S.Min /= CycleFrequency; + S.Median /= CycleFrequency; + S.Max /= CycleFrequency; + S.Sum /= CycleFrequency; + S.Pct90 /= CycleFrequency; + S.Pct99 /= CycleFrequency; +} + +// Normalises the statistics in the graph for a given TSC frequency. +void GraphRenderer::normalizeStatistics(double CycleFrequency) { + for (auto &E : G.edges()) { + auto &S = E.second.S; + normalizeTimeStat(S, CycleFrequency); + } + for (auto &V : G.vertices()) { + auto &S = V.second.S; + normalizeTimeStat(S, CycleFrequency); + } + + normalizeTimeStat(G.GraphEdgeMax, CycleFrequency); + normalizeTimeStat(G.GraphVertexMax, CycleFrequency); +} + +// Returns a string containing the value of statistic field T +std::string +GraphRenderer::TimeStat::getAsString(GraphRenderer::StatType T) const { + std::string St; + raw_string_ostream S{St}; + switch (T) { + case GraphRenderer::StatType::COUNT: + S << Count; + break; + case GraphRenderer::StatType::MIN: + S << Min; + break; + case GraphRenderer::StatType::MED: + S << Median; + break; + case GraphRenderer::StatType::PCT90: + S << Pct90; + break; + case GraphRenderer::StatType::PCT99: + S << Pct99; + break; + case GraphRenderer::StatType::MAX: + S << Max; + break; + case GraphRenderer::StatType::SUM: + S << Sum; + break; + case GraphRenderer::StatType::NONE: + break; + } + return S.str(); +} + +// Returns the quotient between the property T of this and another TimeStat as +// a double +double GraphRenderer::TimeStat::compare(StatType T, const TimeStat &O) const { + double retval = 0; + switch (T) { + case GraphRenderer::StatType::COUNT: + retval = static_cast<double>(Count) / static_cast<double>(O.Count); + break; + case GraphRenderer::StatType::MIN: + retval = Min / O.Min; + break; + case GraphRenderer::StatType::MED: + retval = Median / O.Median; + break; + case GraphRenderer::StatType::PCT90: + retval = Pct90 / O.Pct90; + break; + case GraphRenderer::StatType::PCT99: + retval = Pct99 / O.Pct99; + break; + case GraphRenderer::StatType::MAX: + retval = Max / O.Max; + break; + case GraphRenderer::StatType::SUM: + retval = Sum / O.Sum; + break; + case GraphRenderer::StatType::NONE: + retval = 0.0; + break; + } + return std::sqrt( + retval); // the square root here provides more dynamic contrast for + // low runtime edges, giving better separation and + // coloring lower down the call stack. +} + +// Outputs a DOT format version of the Graph embedded in the GraphRenderer +// object on OS. It does this in the expected way by itterating +// through all edges then vertices and then outputting them and their +// annotations. +// +// FIXME: output more information, better presented. +void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H, + StatType ET, StatType EC, StatType VT, + StatType VC) { + G.GraphEdgeMax = {}; + G.GraphVertexMax = {}; + calculateEdgeStatistics(); + calculateVertexStatistics(); + + if (H.CycleFrequency) + normalizeStatistics(H.CycleFrequency); + + OS << "digraph xray {\n"; + + if (VT != StatType::NONE) + OS << "node [shape=record];\n"; + + for (const auto &E : G.edges()) { + const auto &S = E.second.S; + OS << "F" << E.first.first << " -> " + << "F" << E.first.second << " [label=\"" << S.getAsString(ET) << "\""; + if (EC != StatType::NONE) + OS << " color=\"" << CHelper.getColorString(S.compare(EC, G.GraphEdgeMax)) + << "\""; + OS << "];\n"; + } + + for (const auto &V : G.vertices()) { + const auto &VA = V.second; + if (V.first == 0) + continue; + OS << "F" << V.first << " [label=\"" << (VT != StatType::NONE ? "{" : "") + << (VA.SymbolName.size() > 40 ? VA.SymbolName.substr(0, 40) + "..." + : VA.SymbolName); + if (VT != StatType::NONE) + OS << "|" << VA.S.getAsString(VT) << "}\""; + else + OS << "\""; + if (VC != StatType::NONE) + OS << " color=\"" << CHelper.getColorString(VA.S.compare(VC, G.GraphVertexMax)) + << "\""; + OS << "];\n"; + } + OS << "}\n"; +} + +// Here we register and implement the llvm-xray graph subcommand. +// The bulk of this code reads in the options, opens the required files, uses +// those files to create a context for analysing the xray trace, then there is a +// short loop which actually analyses the trace, generates the graph and then +// outputs it as a DOT. +// +// FIXME: include additional filtering and annalysis passes to provide more +// specific useful information. +static CommandRegistration Unused(&GraphC, []() -> Error { + InstrumentationMap Map; + if (!GraphInstrMap.empty()) { + auto InstrumentationMapOrError = loadInstrumentationMap(GraphInstrMap); + if (!InstrumentationMapOrError) + return joinErrors( + make_error<StringError>( + Twine("Cannot open instrumentation map '") + GraphInstrMap + "'", + std::make_error_code(std::errc::invalid_argument)), + InstrumentationMapOrError.takeError()); + Map = std::move(*InstrumentationMapOrError); + } + + const auto &FunctionAddresses = Map.getFunctionAddresses(); + symbolize::LLVMSymbolizer::Options Opts( + symbolize::FunctionNameKind::LinkageName, true, true, false, ""); + symbolize::LLVMSymbolizer Symbolizer(Opts); + llvm::xray::FuncIdConversionHelper FuncIdHelper(GraphInstrMap, Symbolizer, + FunctionAddresses); + xray::GraphRenderer GR(FuncIdHelper, GraphDeduceSiblingCalls); + std::error_code EC; + raw_fd_ostream OS(GraphOutput, EC, sys::fs::OpenFlags::F_Text); + if (EC) + return make_error<StringError>( + Twine("Cannot open file '") + GraphOutput + "' for writing.", EC); + + auto TraceOrErr = loadTraceFile(GraphInput, true); + if (!TraceOrErr) + return joinErrors( + make_error<StringError>(Twine("Failed loading input file '") + + GraphInput + "'", + make_error_code(llvm::errc::invalid_argument)), + TraceOrErr.takeError()); + + auto &Trace = *TraceOrErr; + const auto &Header = Trace.getFileHeader(); + + // Here we generate the call graph from entries we find in the trace. + for (const auto &Record : Trace) { + auto E = GR.accountRecord(Record); + if (!E) + continue; + + for (const auto &ThreadStack : GR.getPerThreadFunctionStack()) { + errs() << "Thread ID: " << ThreadStack.first << "\n"; + auto Level = ThreadStack.second.size(); + for (const auto &Entry : llvm::reverse(ThreadStack.second)) + errs() << "#" << Level-- << "\t" + << FuncIdHelper.SymbolOrNumber(Entry.FuncId) << '\n'; + } + + if (!GraphKeepGoing) + return joinErrors(make_error<StringError>( + "Error encountered generating the call graph.", + std::make_error_code(std::errc::invalid_argument)), + std::move(E)); + + handleAllErrors(std::move(E), + [&](const ErrorInfoBase &E) { E.log(errs()); }); + } + GR.exportGraphAsDOT(OS, Header, GraphEdgeLabel, GraphEdgeColorType, + GraphVertexLabel, GraphVertexColorType); + return Error::success(); +}); diff --git a/tools/llvm-xray/xray-graph.h b/tools/llvm-xray/xray-graph.h new file mode 100644 index 0000000000000..1c7a3c0ef454b --- /dev/null +++ b/tools/llvm-xray/xray-graph.h @@ -0,0 +1,164 @@ +//===-- xray-graph.h - XRay Function Call Graph Renderer --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Generate a DOT file to represent the function call graph encountered in +// the trace. +// +//===----------------------------------------------------------------------===// + +#ifndef XRAY_GRAPH_H +#define XRAY_GRAPH_H + +#include <string> +#include <vector> + +#include "func-id-helper.h" +#include "xray-color-helper.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/XRay/Graph.h" +#include "llvm/XRay/Trace.h" +#include "llvm/XRay/XRayRecord.h" + +namespace llvm { +namespace xray { + +/// A class encapsulating the logic related to analyzing XRay traces, producting +/// Graphs from them and then exporting those graphs for review. +class GraphRenderer { +public: + /// An enum for enumerating the various statistics gathered on latencies + enum class StatType { NONE, COUNT, MIN, MED, PCT90, PCT99, MAX, SUM }; + + /// An inner struct for common timing statistics information + struct TimeStat { + uint64_t Count = 0; + double Min = 0; + double Median = 0; + double Pct90 = 0; + double Pct99 = 0; + double Max = 0; + double Sum = 0; + std::string getAsString(StatType T) const; + double compare(StatType T, const TimeStat &Other) const; + }; + typedef uint64_t TimestampT; + + /// An inner struct for storing edge attributes for our graph. Here the + /// attributes are mainly function call statistics. + /// + /// FIXME: expand to contain more information eg call latencies. + struct CallStats { + TimeStat S; + std::vector<TimestampT> Timings; + }; + + /// An Inner Struct for storing vertex attributes, at the moment just + /// SymbolNames, however in future we could store bulk function statistics. + /// + /// FIXME: Store more attributes based on instrumentation map. + struct FunctionStats { + std::string SymbolName; + TimeStat S; + }; + + struct FunctionAttr { + int32_t FuncId; + uint64_t TSC; + }; + + typedef SmallVector<FunctionAttr, 4> FunctionStack; + + typedef DenseMap<llvm::sys::ProcessInfo::ProcessId, FunctionStack> + PerThreadFunctionStackMap; + + class GraphT : public Graph<FunctionStats, CallStats, int32_t> { + public: + TimeStat GraphEdgeMax = {}; + TimeStat GraphVertexMax = {}; + }; + + GraphT G; + typedef typename decltype(G)::VertexIdentifier VertexIdentifier; + typedef typename decltype(G)::EdgeIdentifier EdgeIdentifier; + + /// Use a Map to store the Function stack for each thread whilst building the + /// graph. + /// + /// FIXME: Perhaps we can Build this into LatencyAccountant? or vise versa? + PerThreadFunctionStackMap PerThreadFunctionStack; + + /// Usefull object for getting human readable Symbol Names. + const FuncIdConversionHelper &FuncIdHelper; + bool DeduceSiblingCalls = false; + TimestampT CurrentMaxTSC = 0; + + /// A private function to help implement the statistic generation functions; + template <typename U> + void getStats(U begin, U end, GraphRenderer::TimeStat &S); + void updateMaxStats(const TimeStat &S, TimeStat &M); + + /// Calculates latency statistics for each edge and stores the data in the + /// Graph + void calculateEdgeStatistics(); + + /// Calculates latency statistics for each vertex and stores the data in the + /// Graph + void calculateVertexStatistics(); + + /// Normalises latency statistics for each edge and vertex by CycleFrequency; + void normalizeStatistics(double CycleFrequency); + + /// An object to color gradients + ColorHelper CHelper; + +public: + /// Takes in a reference to a FuncIdHelper in order to have ready access to + /// Symbol names. + explicit GraphRenderer(const FuncIdConversionHelper &FuncIdHelper, bool DSC) + : FuncIdHelper(FuncIdHelper), DeduceSiblingCalls(DSC), + CHelper(ColorHelper::SequentialScheme::OrRd) { + G[0] = {}; + } + + /// Process an Xray record and expand the graph. + /// + /// This Function will return true on success, or false if records are not + /// presented in per-thread call-tree DFS order. (That is for each thread the + /// Records should be in order runtime on an ideal system.) + /// + /// FIXME: Make this more robust against small irregularities. + Error accountRecord(const XRayRecord &Record); + + const PerThreadFunctionStackMap &getPerThreadFunctionStack() const { + return PerThreadFunctionStack; + } + + /// Output the Embedded graph in DOT format on \p OS, labeling the edges by + /// \p T + void exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H, + StatType EdgeLabel = StatType::NONE, + StatType EdgeColor = StatType::NONE, + StatType VertexLabel = StatType::NONE, + StatType VertexColor = StatType::NONE); + + /// Get a reference to the internal graph. + const GraphT &getGraph() { + calculateEdgeStatistics(); + calculateVertexStatistics(); + return G; + } +}; +} +} + +#endif // XRAY_GRAPH_H diff --git a/tools/llvm-xray/xray-sleds.h b/tools/llvm-xray/xray-sleds.h deleted file mode 100644 index 99279579ed471..0000000000000 --- a/tools/llvm-xray/xray-sleds.h +++ /dev/null @@ -1,32 +0,0 @@ -//===- xray-sleds.h - XRay Sleds Data Structure ---------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Defines the structure used to represent XRay instrumentation map entries. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_LLVM_XRAY_XRAY_SLEDS_H -#define LLVM_TOOLS_LLVM_XRAY_XRAY_SLEDS_H - -namespace llvm { -namespace xray { - -struct SledEntry { - enum class FunctionKinds { ENTRY, EXIT, TAIL }; - - uint64_t Address; - uint64_t Function; - FunctionKinds Kind; - bool AlwaysInstrument; -}; - -} // namespace xray -} // namespace llvm - -#endif // LLVM_TOOLS_LLVM_XRAY_XRAY_SLEDS_H diff --git a/tools/lto/lto.cpp b/tools/lto/lto.cpp index a1dd4ebbccba5..1b218a64cbf56 100644 --- a/tools/lto/lto.cpp +++ b/tools/lto/lto.cpp @@ -44,9 +44,13 @@ static cl::opt<bool> DisableGVNLoadPRE("disable-gvn-loadpre", cl::init(false), cl::desc("Do not run the GVN load PRE pass")); -static cl::opt<bool> -DisableLTOVectorization("disable-lto-vectorization", cl::init(false), - cl::desc("Do not run loop or slp vectorization during LTO")); +static cl::opt<bool> DisableLTOVectorization( + "disable-lto-vectorization", cl::init(false), + cl::desc("Do not run loop or slp vectorization during LTO")); + +static cl::opt<bool> EnableFreestanding( + "lto-freestanding", cl::init(false), + cl::desc("Enable Freestanding (disable builtins / TLI) during LTO")); #ifdef NDEBUG static bool VerifyByDefault = false; @@ -159,6 +163,7 @@ static void lto_add_attrs(lto_code_gen_t cg) { if (OptLevel < '0' || OptLevel > '3') report_fatal_error("Optimization level must be between 0 and 3"); CG->setOptLevel(OptLevel - '0'); + CG->setFreestanding(EnableFreestanding); } extern const char* lto_get_version() { @@ -267,7 +272,7 @@ lto_module_t lto_module_create_in_local_context(const void *mem, size_t length, lto_initialize(); llvm::TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); - // Create a local context. Ownership will be transfered to LTOModule. + // Create a local context. Ownership will be transferred to LTOModule. std::unique_ptr<LLVMContext> Context = llvm::make_unique<LLVMContext>(); Context->setDiagnosticHandler(diagnosticHandler, nullptr, true); @@ -464,6 +469,7 @@ thinlto_code_gen_t thinlto_create_codegen(void) { lto_initialize(); ThinLTOCodeGenerator *CodeGen = new ThinLTOCodeGenerator(); CodeGen->setTargetOptions(InitTargetOptionsFromCodeGenFlags()); + CodeGen->setFreestanding(EnableFreestanding); if (OptLevel.getNumOccurrences()) { if (OptLevel < '0' || OptLevel > '3') diff --git a/tools/msbuild/CMakeLists.txt b/tools/msbuild/CMakeLists.txt index 4f471e5408ba4..9d132ea58d5d9 100644 --- a/tools/msbuild/CMakeLists.txt +++ b/tools/msbuild/CMakeLists.txt @@ -1,4 +1,4 @@ -if (WIN32) +if (MSVC) # CPack will install a registry key in this format that we wish to reference. set(REG_KEY "${CPACK_PACKAGE_INSTALL_REGISTRY_KEY}") set(LIB_PATH_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}") diff --git a/tools/obj2yaml/CMakeLists.txt b/tools/obj2yaml/CMakeLists.txt index 0fab6d104dce9..ecd958d75b37b 100644 --- a/tools/obj2yaml/CMakeLists.txt +++ b/tools/obj2yaml/CMakeLists.txt @@ -11,5 +11,6 @@ add_llvm_tool(obj2yaml dwarf2yaml.cpp elf2yaml.cpp macho2yaml.cpp + wasm2yaml.cpp Error.cpp ) diff --git a/tools/obj2yaml/dwarf2yaml.cpp b/tools/obj2yaml/dwarf2yaml.cpp index cbf34ed5388a2..d41b44c068108 100644 --- a/tools/obj2yaml/dwarf2yaml.cpp +++ b/tools/obj2yaml/dwarf2yaml.cpp @@ -17,6 +17,13 @@ using namespace llvm; +void dumpInitialLength(DataExtractor &Data, uint32_t &Offset, + DWARFYAML::InitialLength &InitialLength) { + InitialLength.TotalLength = Data.getU32(&Offset); + if (InitialLength.isDWARF64()) + InitialLength.TotalLength64 = Data.getU64(&Offset); +} + void dumpDebugAbbrev(DWARFContextInMemory &DCtx, DWARFYAML::Data &Y) { auto AbbrevSetPtr = DCtx.getDebugAbbrev(); if (AbbrevSetPtr) { @@ -31,6 +38,8 @@ void dumpDebugAbbrev(DWARFContextInMemory &DCtx, DWARFYAML::Data &Y) { DWARFYAML::AttributeAbbrev AttAbrv; AttAbrv.Attribute = Attribute.Attr; AttAbrv.Form = Attribute.Form; + if (AttAbrv.Form == dwarf::DW_FORM_implicit_const) + AttAbrv.Value = *Attribute.ByteSizeOrValue; Abbrv.Attributes.push_back(AttAbrv); } Y.AbbrevDecls.push_back(Abbrv); @@ -55,7 +64,7 @@ void dumpDebugARanges(DWARFContextInMemory &DCtx, DWARFYAML::Data &Y) { while (Set.extract(ArangesData, &Offset)) { DWARFYAML::ARange Range; - Range.Length = Set.getHeader().Length; + Range.Length.setLength(Set.getHeader().Length); Range.Version = Set.getHeader().Version; Range.CuOffset = Set.getHeader().CuOffset; Range.AddrSize = Set.getHeader().AddrSize; @@ -74,11 +83,11 @@ void dumpPubSection(DWARFContextInMemory &DCtx, DWARFYAML::PubSection &Y, StringRef Section) { DataExtractor PubSectionData(Section, DCtx.isLittleEndian(), 0); uint32_t Offset = 0; - Y.Length = PubSectionData.getU32(&Offset); + dumpInitialLength(PubSectionData, Offset, Y.Length); Y.Version = PubSectionData.getU16(&Offset); Y.UnitOffset = PubSectionData.getU32(&Offset); Y.UnitSize = PubSectionData.getU32(&Offset); - while (Offset < Y.Length) { + while (Offset < Y.Length.getLength()) { DWARFYAML::PubEntry NewEntry; NewEntry.DieOffset = PubSectionData.getU32(&Offset); if (Y.IsGNUStyle) @@ -105,8 +114,10 @@ void dumpDebugPubSections(DWARFContextInMemory &DCtx, DWARFYAML::Data &Y) { void dumpDebugInfo(DWARFContextInMemory &DCtx, DWARFYAML::Data &Y) { for (const auto &CU : DCtx.compile_units()) { DWARFYAML::Unit NewUnit; - NewUnit.Length = CU->getLength(); + NewUnit.Length.setLength(CU->getLength()); NewUnit.Version = CU->getVersion(); + if(NewUnit.Version >= 5) + NewUnit.Type = (dwarf::UnitType)CU->getUnitType(); NewUnit.AbbrOffset = CU->getAbbreviations()->getOffset(); NewUnit.AddrSize = CU->getAddressByteSize(); for (auto DIE : CU->dies()) { @@ -126,7 +137,7 @@ void dumpDebugInfo(DWARFContextInMemory &DCtx, DWARFYAML::Data &Y) { DWARFYAML::FormValue NewValue; NewValue.Value = 0xDEADBEEFDEADBEEF; DWARFDie DIEWrapper(CU.get(), &DIE); - auto FormValue = DIEWrapper.getAttributeValue(AttrSpec.Attr); + auto FormValue = DIEWrapper.find(AttrSpec.Attr); if (!FormValue) return; auto Form = FormValue.getValue().getForm(); @@ -168,6 +179,8 @@ void dumpDebugInfo(DWARFContextInMemory &DCtx, DWARFYAML::Data &Y) { case dwarf::DW_FORM_data8: case dwarf::DW_FORM_sdata: case dwarf::DW_FORM_udata: + case dwarf::DW_FORM_ref_sup4: + case dwarf::DW_FORM_ref_sup8: if (auto Val = FormValue.getValue().getAsUnsignedConstant()) NewValue.Value = Val.getValue(); break; @@ -189,7 +202,6 @@ void dumpDebugInfo(DWARFContextInMemory &DCtx, DWARFYAML::Data &Y) { case dwarf::DW_FORM_GNU_strp_alt: case dwarf::DW_FORM_line_strp: case dwarf::DW_FORM_strp_sup: - case dwarf::DW_FORM_ref_sup: case dwarf::DW_FORM_GNU_str_index: if (auto Val = FormValue.getValue().getAsCStringOffset()) NewValue.Value = Val.getValue(); @@ -228,19 +240,14 @@ void dumpDebugLines(DWARFContextInMemory &DCtx, DWARFYAML::Data &Y) { if (!CUDIE) continue; if (auto StmtOffset = - CUDIE.getAttributeValueAsSectionOffset(dwarf::DW_AT_stmt_list)) { + dwarf::toSectionOffset(CUDIE.find(dwarf::DW_AT_stmt_list))) { DWARFYAML::LineTable DebugLines; DataExtractor LineData(DCtx.getLineSection().Data, DCtx.isLittleEndian(), CU->getAddressByteSize()); uint32_t Offset = *StmtOffset; - uint64_t SizeOfPrologueLength = 4; - DebugLines.TotalLength = LineData.getU32(&Offset); - uint64_t LineTableLength = DebugLines.TotalLength; - if (DebugLines.TotalLength == UINT32_MAX) { - DebugLines.TotalLength64 = LineData.getU64(&Offset); - LineTableLength = DebugLines.TotalLength64; - SizeOfPrologueLength = 8; - } + dumpInitialLength(LineData, Offset, DebugLines.Length); + uint64_t LineTableLength = DebugLines.Length.getLength(); + uint64_t SizeOfPrologueLength = DebugLines.Length.isDWARF64() ? 8 : 4; DebugLines.Version = LineData.getU16(&Offset); DebugLines.PrologueLength = LineData.getUnsigned(&Offset, SizeOfPrologueLength); diff --git a/tools/obj2yaml/elf2yaml.cpp b/tools/obj2yaml/elf2yaml.cpp index 697ab79d3b50e..9f9ef99265dcb 100644 --- a/tools/obj2yaml/elf2yaml.cpp +++ b/tools/obj2yaml/elf2yaml.cpp @@ -228,10 +228,18 @@ std::error_code ELFDumper<ELFT>::dumpRelocation(const RelT *Rel, return errorToErrorCode(StrTabOrErr.takeError()); StringRef StrTab = *StrTabOrErr; - Expected<StringRef> NameOrErr = Sym->getName(StrTab); - if (!NameOrErr) - return errorToErrorCode(NameOrErr.takeError()); - R.Symbol = NameOrErr.get(); + if (Sym) { + Expected<StringRef> NameOrErr = Sym->getName(StrTab); + if (!NameOrErr) + return errorToErrorCode(NameOrErr.takeError()); + R.Symbol = NameOrErr.get(); + } else { + // We have some edge cases of relocations without a symbol associated, + // e.g. an object containing the invalid (according to the System V + // ABI) R_X86_64_NONE reloc. Create a symbol with an empty name instead + // of crashing. + R.Symbol = ""; + } return obj2yaml_error::success; } diff --git a/tools/obj2yaml/macho2yaml.cpp b/tools/obj2yaml/macho2yaml.cpp index 0c30dc7575ecc..9ad2a6d979f58 100644 --- a/tools/obj2yaml/macho2yaml.cpp +++ b/tools/obj2yaml/macho2yaml.cpp @@ -163,6 +163,23 @@ const char *MachODumper::processLoadCommandData<MachO::rpath_command>( return readString<MachO::rpath_command>(LC, LoadCmd); } +template <> +const char *MachODumper::processLoadCommandData<MachO::build_version_command>( + MachOYAML::LoadCommand &LC, + const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd) { + auto Start = LoadCmd.Ptr + sizeof(MachO::build_version_command); + auto NTools = LC.Data.build_version_command_data.ntools; + for (unsigned i = 0; i < NTools; ++i) { + auto Curr = Start + i * sizeof(MachO::build_tool_version); + MachO::build_tool_version BV; + memcpy((void *)&BV, Curr, sizeof(MachO::build_tool_version)); + if (Obj.isLittleEndian() != sys::IsLittleEndianHost) + MachO::swapStruct(BV); + LC.Tools.push_back(BV); + } + return Start + NTools * sizeof(MachO::build_tool_version); +} + Expected<std::unique_ptr<MachOYAML::Object>> MachODumper::dump() { auto Y = make_unique<MachOYAML::Object>(); Y->IsLittleEndian = Obj.isLittleEndian(); diff --git a/tools/obj2yaml/obj2yaml.cpp b/tools/obj2yaml/obj2yaml.cpp index 3f9373ee17e38..31712af263627 100644 --- a/tools/obj2yaml/obj2yaml.cpp +++ b/tools/obj2yaml/obj2yaml.cpp @@ -24,6 +24,8 @@ static std::error_code dumpObject(const ObjectFile &Obj) { return coff2yaml(outs(), cast<COFFObjectFile>(Obj)); if (Obj.isELF()) return elf2yaml(outs(), Obj); + if (Obj.isWasm()) + return wasm2yaml(outs(), cast<WasmObjectFile>(Obj)); return obj2yaml_error::unsupported_obj_file_format; } diff --git a/tools/obj2yaml/obj2yaml.h b/tools/obj2yaml/obj2yaml.h index 70d4ebdd3d14e..69c753296efda 100644 --- a/tools/obj2yaml/obj2yaml.h +++ b/tools/obj2yaml/obj2yaml.h @@ -14,6 +14,7 @@ #define LLVM_TOOLS_OBJ2YAML_OBJ2YAML_H #include "llvm/Object/COFF.h" +#include "llvm/Object/Wasm.h" #include "llvm/Support/raw_ostream.h" #include <system_error> @@ -23,6 +24,8 @@ std::error_code elf2yaml(llvm::raw_ostream &Out, const llvm::object::ObjectFile &Obj); std::error_code macho2yaml(llvm::raw_ostream &Out, const llvm::object::Binary &Obj); +std::error_code wasm2yaml(llvm::raw_ostream &Out, + const llvm::object::WasmObjectFile &Obj); // Forward decls for dwarf2yaml namespace llvm { diff --git a/tools/obj2yaml/wasm2yaml.cpp b/tools/obj2yaml/wasm2yaml.cpp new file mode 100644 index 0000000000000..f6b530c41969d --- /dev/null +++ b/tools/obj2yaml/wasm2yaml.cpp @@ -0,0 +1,219 @@ +//===------ utils/wasm2yaml.cpp - obj2yaml conversion tool ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "obj2yaml.h" +#include "llvm/Object/COFF.h" +#include "llvm/ObjectYAML/WasmYAML.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/YAMLTraits.h" + +using namespace llvm; + +namespace { + +class WasmDumper { + const object::WasmObjectFile &Obj; + +public: + WasmDumper(const object::WasmObjectFile &O) : Obj(O) {} + ErrorOr<WasmYAML::Object *> dump(); +}; + +ErrorOr<WasmYAML::Object *> WasmDumper::dump() { + auto Y = make_unique<WasmYAML::Object>(); + + // Dump header + Y->Header.Version = Obj.getHeader().Version; + + // Dump sections + for (const auto &Sec : Obj.sections()) { + const object::WasmSection &WasmSec = Obj.getWasmSection(Sec); + std::unique_ptr<WasmYAML::Section> S; + switch (WasmSec.Type) { + case wasm::WASM_SEC_CUSTOM: { + if (WasmSec.Name.startswith("reloc.")) { + // Relocations are attached the sections they apply to rather than + // being represented as a custom section in the YAML output. + continue; + } + auto CustomSec = make_unique<WasmYAML::CustomSection>(); + CustomSec->Name = WasmSec.Name; + CustomSec->Payload = yaml::BinaryRef(WasmSec.Content); + S = std::move(CustomSec); + break; + } + case wasm::WASM_SEC_TYPE: { + auto TypeSec = make_unique<WasmYAML::TypeSection>(); + uint32_t Index = 0; + for (const auto &FunctionSig : Obj.types()) { + WasmYAML::Signature Sig; + Sig.Index = Index++; + Sig.ReturnType = FunctionSig.ReturnType; + for (const auto &ParamType : FunctionSig.ParamTypes) + Sig.ParamTypes.push_back(ParamType); + TypeSec->Signatures.push_back(Sig); + } + S = std::move(TypeSec); + break; + } + case wasm::WASM_SEC_IMPORT: { + auto ImportSec = make_unique<WasmYAML::ImportSection>(); + for (auto &Import : Obj.imports()) { + WasmYAML::Import Ex; + Ex.Module = Import.Module; + Ex.Field = Import.Field; + Ex.Kind = Import.Kind; + if (Ex.Kind == wasm::WASM_EXTERNAL_FUNCTION) { + Ex.SigIndex = Import.SigIndex; + } else if (Ex.Kind == wasm::WASM_EXTERNAL_GLOBAL) { + Ex.GlobalType = Import.GlobalType; + Ex.GlobalMutable = Import.GlobalMutable; + } + ImportSec->Imports.push_back(Ex); + } + S = std::move(ImportSec); + break; + } + case wasm::WASM_SEC_FUNCTION: { + auto FuncSec = make_unique<WasmYAML::FunctionSection>(); + for (const auto &Func : Obj.functionTypes()) { + FuncSec->FunctionTypes.push_back(Func); + } + S = std::move(FuncSec); + break; + } + case wasm::WASM_SEC_TABLE: { + auto TableSec = make_unique<WasmYAML::TableSection>(); + for (auto &Table : Obj.tables()) { + WasmYAML::Table T; + T.ElemType = Table.ElemType; + T.TableLimits.Flags = Table.Limits.Flags; + T.TableLimits.Initial = Table.Limits.Initial; + T.TableLimits.Maximum = Table.Limits.Maximum; + TableSec->Tables.push_back(T); + } + S = std::move(TableSec); + break; + } + case wasm::WASM_SEC_MEMORY: { + auto MemorySec = make_unique<WasmYAML::MemorySection>(); + for (auto &Memory : Obj.memories()) { + WasmYAML::Limits L; + L.Flags = Memory.Flags; + L.Initial = Memory.Initial; + L.Maximum = Memory.Maximum; + MemorySec->Memories.push_back(L); + } + S = std::move(MemorySec); + break; + } + case wasm::WASM_SEC_GLOBAL: { + auto GlobalSec = make_unique<WasmYAML::GlobalSection>(); + for (auto &Global : Obj.globals()) { + WasmYAML::Global G; + G.Type = Global.Type; + G.Mutable = Global.Mutable; + G.InitExpr = Global.InitExpr; + GlobalSec->Globals.push_back(G); + } + S = std::move(GlobalSec); + break; + } + case wasm::WASM_SEC_START: { + auto StartSec = make_unique<WasmYAML::StartSection>(); + StartSec->StartFunction = Obj.startFunction(); + S = std::move(StartSec); + break; + } + case wasm::WASM_SEC_EXPORT: { + auto ExportSec = make_unique<WasmYAML::ExportSection>(); + for (auto &Export : Obj.exports()) { + WasmYAML::Export Ex; + Ex.Name = Export.Name; + Ex.Kind = Export.Kind; + Ex.Index = Export.Index; + ExportSec->Exports.push_back(Ex); + } + S = std::move(ExportSec); + break; + } + case wasm::WASM_SEC_ELEM: { + auto ElemSec = make_unique<WasmYAML::ElemSection>(); + for (auto &Segment : Obj.elements()) { + WasmYAML::ElemSegment Seg; + Seg.TableIndex = Segment.TableIndex; + Seg.Offset = Segment.Offset; + for (auto &Func : Segment.Functions) { + Seg.Functions.push_back(Func); + } + ElemSec->Segments.push_back(Seg); + } + S = std::move(ElemSec); + break; + } + case wasm::WASM_SEC_CODE: { + auto CodeSec = make_unique<WasmYAML::CodeSection>(); + for (auto &Func : Obj.functions()) { + WasmYAML::Function Function; + for (auto &Local : Func.Locals) { + WasmYAML::LocalDecl LocalDecl; + LocalDecl.Type = Local.Type; + LocalDecl.Count = Local.Count; + Function.Locals.push_back(LocalDecl); + } + Function.Body = yaml::BinaryRef(Func.Body); + CodeSec->Functions.push_back(Function); + } + S = std::move(CodeSec); + break; + } + case wasm::WASM_SEC_DATA: { + auto DataSec = make_unique<WasmYAML::DataSection>(); + for (auto &Segment : Obj.dataSegments()) { + WasmYAML::DataSegment Seg; + Seg.Index = Segment.Index; + Seg.Offset = Segment.Offset; + Seg.Content = yaml::BinaryRef(Segment.Content); + DataSec->Segments.push_back(Seg); + } + S = std::move(DataSec); + break; + } + default: + llvm_unreachable("Unknown section type"); + break; + } + for (const wasm::WasmRelocation &Reloc: WasmSec.Relocations) { + WasmYAML::Relocation R; + R.Type = Reloc.Type; + R.Index = Reloc.Index; + R.Offset = Reloc.Offset; + R.Addend = Reloc.Addend; + S->Relocations.push_back(R); + } + Y->Sections.push_back(std::move(S)); + } + + return Y.release(); +} + +} // namespace + +std::error_code wasm2yaml(raw_ostream &Out, const object::WasmObjectFile &Obj) { + WasmDumper Dumper(Obj); + ErrorOr<WasmYAML::Object *> YAMLOrErr = Dumper.dump(); + if (std::error_code EC = YAMLOrErr.getError()) + return EC; + + std::unique_ptr<WasmYAML::Object> YAML(YAMLOrErr.get()); + yaml::Output Yout(Out); + Yout << *YAML; + + return std::error_code(); +} diff --git a/tools/opt/GraphPrinters.cpp b/tools/opt/GraphPrinters.cpp index 640edfee41dec..a8bb12f3e0184 100644 --- a/tools/opt/GraphPrinters.cpp +++ b/tools/opt/GraphPrinters.cpp @@ -35,7 +35,7 @@ namespace { } bool runOnFunction(Function &F) override { - getAnalysis<DominatorTreeWrapperPass>().dump(); + getAnalysis<DominatorTreeWrapperPass>().print(dbgs()); return false; } }; diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index a93c06c1d13d0..40459e559986b 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -102,6 +102,11 @@ static cl::opt<bool> OutputThinLTOBC("thinlto-bc", cl::desc("Write output as ThinLTO-ready bitcode")); +static cl::opt<std::string> ThinLinkBitcodeFile( + "thin-link-bitcode-file", cl::value_desc("filename"), + cl::desc( + "A file in which to write minimized bitcode for the thin link only")); + static cl::opt<bool> NoVerify("disable-verify", cl::desc("Do not run the verifier"), cl::Hidden); @@ -201,10 +206,10 @@ static cl::opt<bool> PrintBreakpoints("print-breakpoints-for-testing", cl::desc("Print select breakpoints location for testing")); -static cl::opt<std::string> -DefaultDataLayout("default-data-layout", - cl::desc("data layout string to use if not specified by module"), - cl::value_desc("layout-string"), cl::init("")); +static cl::opt<std::string> ClDataLayout("data-layout", + cl::desc("data layout string to use"), + cl::value_desc("layout-string"), + cl::init("")); static cl::opt<bool> PreserveBitcodeUseListOrder( "preserve-bc-uselistorder", @@ -268,7 +273,7 @@ static void AddOptimizationPasses(legacy::PassManagerBase &MPM, if (DisableInline) { // No inlining pass } else if (OptLevel > 1) { - Builder.Inliner = createFunctionInliningPass(OptLevel, SizeLevel); + Builder.Inliner = createFunctionInliningPass(OptLevel, SizeLevel, false); } else { Builder.Inliner = createAlwaysInlinerLegacyPass(); } @@ -287,13 +292,8 @@ static void AddOptimizationPasses(legacy::PassManagerBase &MPM, Builder.SLPVectorize = DisableSLPVectorization ? false : OptLevel > 1 && SizeLevel < 2; - // Add target-specific passes that need to run as early as possible. if (TM) - Builder.addExtension( - PassManagerBuilder::EP_EarlyAsPossible, - [&](const PassManagerBuilder &, legacy::PassManagerBase &PM) { - TM->addEarlyAsPossiblePasses(PM); - }); + TM->adjustPassManager(Builder); if (Coroutines) addCoroutinePassesToExtensionPoints(Builder); @@ -453,12 +453,15 @@ int main(int argc, char **argv) { return 1; } - // If we are supposed to override the target triple, do so now. + // If we are supposed to override the target triple or data layout, do so now. if (!TargetTriple.empty()) M->setTargetTriple(Triple::normalize(TargetTriple)); + if (!ClDataLayout.empty()) + M->setDataLayout(ClDataLayout); // Figure out what stream we are supposed to write to... std::unique_ptr<tool_output_file> Out; + std::unique_ptr<tool_output_file> ThinLinkOut; if (NoOutput) { if (!OutputFilename.empty()) errs() << "WARNING: The -o (output filename) option is ignored when\n" @@ -474,6 +477,15 @@ int main(int argc, char **argv) { errs() << EC.message() << '\n'; return 1; } + + if (!ThinLinkBitcodeFile.empty()) { + ThinLinkOut.reset( + new tool_output_file(ThinLinkBitcodeFile, EC, sys::fs::F_None)); + if (EC) { + errs() << EC.message() << '\n'; + return 1; + } + } } Triple ModuleTriple(M->getTargetTriple()); @@ -535,12 +547,6 @@ int main(int argc, char **argv) { TLII.disableAllFunctions(); Passes.add(new TargetLibraryInfoWrapperPass(TLII)); - // Add an appropriate DataLayout instance for this module. - const DataLayout &DL = M->getDataLayout(); - if (DL.isDefault() && !DefaultDataLayout.empty()) { - M->setDataLayout(DefaultDataLayout); - } - // Add internal analysis passes from the target machine. Passes.add(createTargetTransformInfoWrapperPass(TM ? TM->getTargetIRAnalysis() : TargetIRAnalysis())); @@ -709,7 +715,8 @@ int main(int argc, char **argv) { report_fatal_error("Text output is incompatible with -module-hash"); Passes.add(createPrintModulePass(*OS, "", PreserveAssemblyUseListOrder)); } else if (OutputThinLTOBC) - Passes.add(createWriteThinLTOBitcodePass(*OS)); + Passes.add(createWriteThinLTOBitcodePass( + *OS, ThinLinkOut ? &ThinLinkOut->os() : nullptr)); else Passes.add(createBitcodeWriterPass(*OS, PreserveBitcodeUseListOrder, EmitSummaryIndex, EmitModuleHash)); @@ -756,5 +763,8 @@ int main(int argc, char **argv) { if (YamlFile) YamlFile->keep(); + if (ThinLinkOut) + ThinLinkOut->keep(); + return 0; } diff --git a/tools/sancov/coverage-report-server.py b/tools/sancov/coverage-report-server.py index ac3206cba393b..428276f95d3b3 100755 --- a/tools/sancov/coverage-report-server.py +++ b/tools/sancov/coverage-report-server.py @@ -138,7 +138,7 @@ class ServerHandler(http.server.BaseHTTPRequestHandler): if not file_coverage: continue filelist.append( - "<tr><td><a href=\"/{name}\">{name}</a></td>" + "<tr><td><a href=\"./{name}\">{name}</a></td>" "<td>{coverage}%</td></tr>".format( name=html.escape(filename, quote=True), coverage=format_pct(file_coverage))) @@ -165,7 +165,7 @@ class ServerHandler(http.server.BaseHTTPRequestHandler): ["<span class='{cls}'>{line} </span>".format( line=html.escape(line.rstrip()), cls=linemap.get(line_no, "")) - for line_no, line in enumerate(f)]) + for line_no, line in enumerate(f, start=1)]) response = string.Template(CONTENT_PAGE_TMPL).safe_substitute( path=self.path[1:], diff --git a/tools/sancov/sancov.cc b/tools/sancov/sancov.cc index ff2039de35ebe..7f103ebb904b6 100644 --- a/tools/sancov/sancov.cc +++ b/tools/sancov/sancov.cc @@ -96,7 +96,8 @@ cl::opt<ActionType> Action( static cl::list<std::string> ClInputFiles(cl::Positional, cl::OneOrMore, - cl::desc("(<binary file>|<.sancov file>)...")); + cl::desc("<action> <binary files...> <.sancov files...> " + "<.symcov files...>")); static cl::opt<bool> ClDemangle("demangle", cl::init(true), cl::desc("Print demangled function name.")); @@ -179,7 +180,7 @@ struct CoverageStats { // --------- ERROR HANDLING --------- static void fail(const llvm::Twine &E) { - errs() << "Error: " << E << "\n"; + errs() << "ERROR: " << E << "\n"; exit(1); } @@ -191,7 +192,7 @@ static void failIf(bool B, const llvm::Twine &E) { static void failIfError(std::error_code Error) { if (!Error) return; - errs() << "Error: " << Error.message() << "(" << Error.value() << ")\n"; + errs() << "ERROR: " << Error.message() << "(" << Error.value() << ")\n"; exit(1); } @@ -201,7 +202,7 @@ template <typename T> static void failIfError(const ErrorOr<T> &E) { static void failIfError(Error Err) { if (Err) { - logAllUnhandledErrors(std::move(Err), errs(), "Error: "); + logAllUnhandledErrors(std::move(Err), errs(), "ERROR: "); exit(1); } } @@ -946,7 +947,15 @@ symbolize(const RawCoverage &Data, const std::string ObjectFile) { Hasher.update((*BufOrErr)->getBuffer()); Coverage->BinaryHash = toHex(Hasher.final()); + Blacklists B; + auto Symbolizer(createSymbolizer()); + for (uint64_t Addr : *Data.Addrs) { + auto LineInfo = Symbolizer->symbolizeCode(ObjectFile, Addr); + failIfError(LineInfo); + if (B.isBlacklisted(*LineInfo)) + continue; + Coverage->CoveredIds.insert(utohexstr(Addr, true)); } @@ -1077,6 +1086,9 @@ static void readAndPrintRawCoverage(const std::vector<std::string> &FileNames, static std::unique_ptr<SymbolizedCoverage> merge(const std::vector<std::unique_ptr<SymbolizedCoverage>> &Coverages) { + if (Coverages.empty()) + return nullptr; + auto Result = make_unique<SymbolizedCoverage>(); for (size_t I = 0; I < Coverages.size(); ++I) { @@ -1159,11 +1171,17 @@ readSymbolizeAndMergeCmdArguments(std::vector<std::string> FileNames) { CoverageByObjFile[Iter->second].push_back(FileName); }; + for (const auto &Pair : ObjFiles) { + auto FileName = Pair.second; + if (CoverageByObjFile.find(FileName) == CoverageByObjFile.end()) + errs() << "WARNING: No coverage file for " << FileName << "\n"; + } + // Read raw coverage and symbolize it. for (const auto &Pair : CoverageByObjFile) { if (findSanitizerCovFunctions(Pair.first).empty()) { errs() - << "Ignoring " << Pair.first + << "WARNING: Ignoring " << Pair.first << " and its coverage because __sanitizer_cov* functions were not " "found.\n"; continue; @@ -1192,7 +1210,17 @@ int main(int Argc, char **Argv) { llvm::InitializeAllTargetMCs(); llvm::InitializeAllDisassemblers(); - cl::ParseCommandLineOptions(Argc, Argv, "Sanitizer Coverage Processing Tool"); + cl::ParseCommandLineOptions(Argc, Argv, + "Sanitizer Coverage Processing Tool (sancov)\n\n" + " This tool can extract various coverage-related information from: \n" + " coverage-instrumented binary files, raw .sancov files and their " + "symbolized .symcov version.\n" + " Depending on chosen action the tool expects different input files:\n" + " -print-coverage-pcs - coverage-instrumented binary files\n" + " -print-coverage - .sancov files\n" + " <other actions> - .sancov files & corresponding binary " + "files, .symcov files\n" + ); // -print doesn't need object files. if (Action == PrintAction) { diff --git a/tools/yaml2obj/CMakeLists.txt b/tools/yaml2obj/CMakeLists.txt index 5e726496003f7..a885547598d85 100644 --- a/tools/yaml2obj/CMakeLists.txt +++ b/tools/yaml2obj/CMakeLists.txt @@ -8,7 +8,7 @@ set(LLVM_LINK_COMPONENTS add_llvm_tool(yaml2obj yaml2obj.cpp yaml2coff.cpp - yaml2dwarf.cpp yaml2elf.cpp yaml2macho.cpp + yaml2wasm.cpp ) diff --git a/tools/yaml2obj/yaml2dwarf.cpp b/tools/yaml2obj/yaml2dwarf.cpp deleted file mode 100644 index 3ceb7772b9691..0000000000000 --- a/tools/yaml2obj/yaml2dwarf.cpp +++ /dev/null @@ -1,330 +0,0 @@ -//===- yaml2dwarf - Convert YAML to DWARF binary data ---------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// \brief The DWARF component of yaml2obj. -/// -//===----------------------------------------------------------------------===// - -#include "llvm/ObjectYAML/DWARFYAML.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/LEB128.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/SwapByteOrder.h" - -#include <algorithm> - -using namespace llvm; - -template <typename T> -void writeInteger(T Integer, raw_ostream &OS, bool IsLittleEndian) { - if (IsLittleEndian != sys::IsLittleEndianHost) - sys::swapByteOrder(Integer); - OS.write(reinterpret_cast<char *>(&Integer), sizeof(T)); -} - -void writeVariableSizedInteger(uint64_t Integer, size_t Size, raw_ostream &OS, - bool IsLittleEndian) { - if (8 == Size) - writeInteger((uint64_t)Integer, OS, IsLittleEndian); - else if (4 == Size) - writeInteger((uint32_t)Integer, OS, IsLittleEndian); - else if (2 == Size) - writeInteger((uint16_t)Integer, OS, IsLittleEndian); - else if (1 == Size) - writeInteger((uint8_t)Integer, OS, IsLittleEndian); - else - assert(false && "Invalid integer write size."); -} - -void ZeroFillBytes(raw_ostream &OS, size_t Size) { - std::vector<uint8_t> FillData; - FillData.insert(FillData.begin(), Size, 0); - OS.write(reinterpret_cast<char *>(FillData.data()), Size); -} - -void yaml2debug_str(raw_ostream &OS, const DWARFYAML::Data &DI) { - for (auto Str : DI.DebugStrings) { - OS.write(Str.data(), Str.size()); - OS.write('\0'); - } -} - -void yaml2debug_abbrev(raw_ostream &OS, const DWARFYAML::Data &DI) { - for (auto AbbrevDecl : DI.AbbrevDecls) { - encodeULEB128(AbbrevDecl.Code, OS); - encodeULEB128(AbbrevDecl.Tag, OS); - OS.write(AbbrevDecl.Children); - for (auto Attr : AbbrevDecl.Attributes) { - encodeULEB128(Attr.Attribute, OS); - encodeULEB128(Attr.Form, OS); - } - encodeULEB128(0, OS); - encodeULEB128(0, OS); - } -} - -void yaml2debug_aranges(raw_ostream &OS, const DWARFYAML::Data &DI) { - for (auto Range : DI.ARanges) { - auto HeaderStart = OS.tell(); - writeInteger((uint32_t)Range.Length, OS, DI.IsLittleEndian); - writeInteger((uint16_t)Range.Version, OS, DI.IsLittleEndian); - writeInteger((uint32_t)Range.CuOffset, OS, DI.IsLittleEndian); - writeInteger((uint8_t)Range.AddrSize, OS, DI.IsLittleEndian); - writeInteger((uint8_t)Range.SegSize, OS, DI.IsLittleEndian); - - auto HeaderSize = OS.tell() - HeaderStart; - auto FirstDescriptor = alignTo(HeaderSize, Range.AddrSize * 2); - ZeroFillBytes(OS, FirstDescriptor - HeaderSize); - - for (auto Descriptor : Range.Descriptors) { - writeVariableSizedInteger(Descriptor.Address, Range.AddrSize, OS, - DI.IsLittleEndian); - writeVariableSizedInteger(Descriptor.Length, Range.AddrSize, OS, - DI.IsLittleEndian); - } - ZeroFillBytes(OS, Range.AddrSize * 2); - } -} - -void yaml2pubsection(raw_ostream &OS, const DWARFYAML::PubSection &Sect, - bool IsLittleEndian) { - writeInteger((uint32_t)Sect.Length, OS, IsLittleEndian); - writeInteger((uint16_t)Sect.Version, OS, IsLittleEndian); - writeInteger((uint32_t)Sect.UnitOffset, OS, IsLittleEndian); - writeInteger((uint32_t)Sect.UnitSize, OS, IsLittleEndian); - for (auto Entry : Sect.Entries) { - writeInteger((uint32_t)Entry.DieOffset, OS, IsLittleEndian); - if (Sect.IsGNUStyle) - writeInteger((uint32_t)Entry.Descriptor, OS, IsLittleEndian); - OS.write(Entry.Name.data(), Entry.Name.size()); - OS.write('\0'); - } -} - -void yaml2debug_info(raw_ostream &OS, const DWARFYAML::Data &DI) { - - for (auto CU : DI.CompileUnits) { - writeInteger((uint32_t)CU.Length, OS, DI.IsLittleEndian); - writeInteger((uint16_t)CU.Version, OS, DI.IsLittleEndian); - writeInteger((uint32_t)CU.AbbrOffset, OS, DI.IsLittleEndian); - writeInteger((uint8_t)CU.AddrSize, OS, DI.IsLittleEndian); - - auto FirstAbbrevCode = CU.Entries[0].AbbrCode; - - for (auto Entry : CU.Entries) { - encodeULEB128(Entry.AbbrCode, OS); - if (Entry.AbbrCode == 0u) - continue; - bool Indirect = false; - assert(Entry.AbbrCode - FirstAbbrevCode < DI.AbbrevDecls.size() && - "Out of range AbbCode"); - auto &Abbrev = DI.AbbrevDecls[Entry.AbbrCode - FirstAbbrevCode]; - - auto FormVal = Entry.Values.begin(); - auto AbbrForm = Abbrev.Attributes.begin(); - for (; - FormVal != Entry.Values.end() && AbbrForm != Abbrev.Attributes.end(); - ++FormVal, ++AbbrForm) { - dwarf::Form Form = AbbrForm->Form; - do { - Indirect = false; - switch (Form) { - case dwarf::DW_FORM_addr: - writeVariableSizedInteger(FormVal->Value, CU.AddrSize, OS, - DI.IsLittleEndian); - break; - case dwarf::DW_FORM_ref_addr: { - // TODO: Handle DWARF32/DWARF64 after Line Table data is done - auto writeSize = CU.Version == 2 ? CU.AddrSize : 4; - writeVariableSizedInteger(FormVal->Value, writeSize, OS, - DI.IsLittleEndian); - break; - } - case dwarf::DW_FORM_exprloc: - case dwarf::DW_FORM_block: - encodeULEB128(FormVal->BlockData.size(), OS); - OS.write(reinterpret_cast<char *>(&FormVal->BlockData[0]), - FormVal->BlockData.size()); - break; - case dwarf::DW_FORM_block1: { - auto writeSize = FormVal->BlockData.size(); - writeInteger((uint8_t)writeSize, OS, DI.IsLittleEndian); - OS.write(reinterpret_cast<char *>(&FormVal->BlockData[0]), - FormVal->BlockData.size()); - break; - } - case dwarf::DW_FORM_block2: { - auto writeSize = FormVal->BlockData.size(); - writeInteger((uint16_t)writeSize, OS, DI.IsLittleEndian); - OS.write(reinterpret_cast<char *>(&FormVal->BlockData[0]), - FormVal->BlockData.size()); - break; - } - case dwarf::DW_FORM_block4: { - auto writeSize = FormVal->BlockData.size(); - writeInteger((uint32_t)writeSize, OS, DI.IsLittleEndian); - OS.write(reinterpret_cast<char *>(&FormVal->BlockData[0]), - FormVal->BlockData.size()); - break; - } - case dwarf::DW_FORM_data1: - case dwarf::DW_FORM_ref1: - case dwarf::DW_FORM_flag: - writeInteger((uint8_t)FormVal->Value, OS, DI.IsLittleEndian); - break; - case dwarf::DW_FORM_data2: - case dwarf::DW_FORM_ref2: - writeInteger((uint16_t)FormVal->Value, OS, DI.IsLittleEndian); - break; - case dwarf::DW_FORM_data4: - case dwarf::DW_FORM_ref4: - writeInteger((uint32_t)FormVal->Value, OS, DI.IsLittleEndian); - break; - case dwarf::DW_FORM_data8: - case dwarf::DW_FORM_ref8: - writeInteger((uint64_t)FormVal->Value, OS, DI.IsLittleEndian); - break; - case dwarf::DW_FORM_sdata: - encodeSLEB128(FormVal->Value, OS); - break; - case dwarf::DW_FORM_udata: - case dwarf::DW_FORM_ref_udata: - encodeULEB128(FormVal->Value, OS); - break; - case dwarf::DW_FORM_string: - OS.write(FormVal->CStr.data(), FormVal->CStr.size()); - OS.write('\0'); - break; - case dwarf::DW_FORM_indirect: - encodeULEB128(FormVal->Value, OS); - Indirect = true; - Form = static_cast<dwarf::Form>((uint64_t)FormVal->Value); - ++FormVal; - break; - case dwarf::DW_FORM_strp: - case dwarf::DW_FORM_sec_offset: - case dwarf::DW_FORM_GNU_ref_alt: - case dwarf::DW_FORM_GNU_strp_alt: - case dwarf::DW_FORM_line_strp: - case dwarf::DW_FORM_strp_sup: - case dwarf::DW_FORM_ref_sup: - // TODO: Handle DWARF32/64 - writeInteger((uint32_t)FormVal->Value, OS, DI.IsLittleEndian); - break; - case dwarf::DW_FORM_ref_sig8: - writeInteger((uint64_t)FormVal->Value, OS, DI.IsLittleEndian); - break; - case dwarf::DW_FORM_GNU_addr_index: - case dwarf::DW_FORM_GNU_str_index: - encodeULEB128(FormVal->Value, OS); - break; - default: - break; - } - } while (Indirect); - } - } - } -} - -void yaml2FileEntry(raw_ostream &OS, const DWARFYAML::File &File) { - OS.write(File.Name.data(), File.Name.size()); - OS.write('\0'); - encodeULEB128(File.DirIdx, OS); - encodeULEB128(File.ModTime, OS); - encodeULEB128(File.Length, OS); -} - -void yaml2debug_line(raw_ostream &OS, const DWARFYAML::Data &DI) { - for (const auto LineTable : DI.DebugLines) { - writeInteger((uint32_t)LineTable.TotalLength, OS, DI.IsLittleEndian); - uint64_t SizeOfPrologueLength = 4; - if (LineTable.TotalLength == UINT32_MAX) { - writeInteger((uint64_t)LineTable.TotalLength64, OS, DI.IsLittleEndian); - SizeOfPrologueLength = 8; - } - writeInteger((uint16_t)LineTable.Version, OS, DI.IsLittleEndian); - writeVariableSizedInteger(LineTable.PrologueLength, SizeOfPrologueLength, - OS, DI.IsLittleEndian); - writeInteger((uint8_t)LineTable.MinInstLength, OS, DI.IsLittleEndian); - if (LineTable.Version >= 4) - writeInteger((uint8_t)LineTable.MaxOpsPerInst, OS, DI.IsLittleEndian); - writeInteger((uint8_t)LineTable.DefaultIsStmt, OS, DI.IsLittleEndian); - writeInteger((uint8_t)LineTable.LineBase, OS, DI.IsLittleEndian); - writeInteger((uint8_t)LineTable.LineRange, OS, DI.IsLittleEndian); - writeInteger((uint8_t)LineTable.OpcodeBase, OS, DI.IsLittleEndian); - - for (auto OpcodeLength : LineTable.StandardOpcodeLengths) - writeInteger((uint8_t)OpcodeLength, OS, DI.IsLittleEndian); - - for (auto IncludeDir : LineTable.IncludeDirs) { - OS.write(IncludeDir.data(), IncludeDir.size()); - OS.write('\0'); - } - OS.write('\0'); - - for (auto File : LineTable.Files) - yaml2FileEntry(OS, File); - OS.write('\0'); - - for (auto Op : LineTable.Opcodes) { - writeInteger((uint8_t)Op.Opcode, OS, DI.IsLittleEndian); - if (Op.Opcode == 0) { - encodeULEB128(Op.ExtLen, OS); - writeInteger((uint8_t)Op.SubOpcode, OS, DI.IsLittleEndian); - switch (Op.SubOpcode) { - case dwarf::DW_LNE_set_address: - case dwarf::DW_LNE_set_discriminator: - writeVariableSizedInteger(Op.Data, DI.CompileUnits[0].AddrSize, OS, - DI.IsLittleEndian); - break; - case dwarf::DW_LNE_define_file: - yaml2FileEntry(OS, Op.FileEntry); - break; - case dwarf::DW_LNE_end_sequence: - break; - default: - for (auto OpByte : Op.UnknownOpcodeData) - writeInteger((uint8_t)OpByte, OS, DI.IsLittleEndian); - } - } else if (Op.Opcode < LineTable.OpcodeBase) { - switch (Op.Opcode) { - case dwarf::DW_LNS_copy: - case dwarf::DW_LNS_negate_stmt: - case dwarf::DW_LNS_set_basic_block: - case dwarf::DW_LNS_const_add_pc: - case dwarf::DW_LNS_set_prologue_end: - case dwarf::DW_LNS_set_epilogue_begin: - break; - - case dwarf::DW_LNS_advance_pc: - case dwarf::DW_LNS_set_file: - case dwarf::DW_LNS_set_column: - case dwarf::DW_LNS_set_isa: - encodeULEB128(Op.Data, OS); - break; - - case dwarf::DW_LNS_advance_line: - encodeSLEB128(Op.SData, OS); - break; - - case dwarf::DW_LNS_fixed_advance_pc: - writeInteger((uint16_t)Op.Data, OS, DI.IsLittleEndian); - break; - - default: - for (auto OpData : Op.StandardOpcodeData) { - encodeULEB128(OpData, OS); - } - } - } - } - } -} diff --git a/tools/yaml2obj/yaml2macho.cpp b/tools/yaml2obj/yaml2macho.cpp index cbc4d7ff50d5e..92b736e5298e3 100644 --- a/tools/yaml2obj/yaml2macho.cpp +++ b/tools/yaml2obj/yaml2macho.cpp @@ -14,6 +14,7 @@ #include "yaml2obj.h" #include "llvm/ObjectYAML/ObjectYAML.h" +#include "llvm/ObjectYAML/DWARFEmitter.h" #include "llvm/Support/Error.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/MachO.h" @@ -179,6 +180,21 @@ size_t writeLoadCommandData<MachO::rpath_command>(MachOYAML::LoadCommand &LC, return writePayloadString(LC, OS); } +template <> +size_t writeLoadCommandData<MachO::build_version_command>( + MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { + size_t BytesWritten = 0; + for (const auto &T : LC.Tools) { + struct MachO::build_tool_version tool = T; + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(tool); + OS.write(reinterpret_cast<const char *>(&tool), + sizeof(MachO::build_tool_version)); + BytesWritten += sizeof(MachO::build_tool_version); + } + return BytesWritten; +} + void ZeroFillBytes(raw_ostream &OS, size_t Size) { std::vector<uint8_t> FillData; FillData.insert(FillData.begin(), Size, 0); @@ -269,19 +285,21 @@ Error MachOWriter::writeSectionData(raw_ostream &OS) { "Wrote too much data somewhere, section offsets don't line up."); if (0 == strncmp(&Sec.segname[0], "__DWARF", 16)) { if (0 == strncmp(&Sec.sectname[0], "__debug_str", 16)) { - yaml2debug_str(OS, Obj.DWARF); + DWARFYAML::EmitDebugStr(OS, Obj.DWARF); } else if (0 == strncmp(&Sec.sectname[0], "__debug_abbrev", 16)) { - yaml2debug_abbrev(OS, Obj.DWARF); + DWARFYAML::EmitDebugAbbrev(OS, Obj.DWARF); } else if (0 == strncmp(&Sec.sectname[0], "__debug_aranges", 16)) { - yaml2debug_aranges(OS, Obj.DWARF); + DWARFYAML::EmitDebugAranges(OS, Obj.DWARF); } else if (0 == strncmp(&Sec.sectname[0], "__debug_pubnames", 16)) { - yaml2pubsection(OS, Obj.DWARF.PubNames, Obj.IsLittleEndian); + DWARFYAML::EmitPubSection(OS, Obj.DWARF.PubNames, + Obj.IsLittleEndian); } else if (0 == strncmp(&Sec.sectname[0], "__debug_pubtypes", 16)) { - yaml2pubsection(OS, Obj.DWARF.PubTypes, Obj.IsLittleEndian); + DWARFYAML::EmitPubSection(OS, Obj.DWARF.PubTypes, + Obj.IsLittleEndian); } else if (0 == strncmp(&Sec.sectname[0], "__debug_info", 16)) { - yaml2debug_info(OS, Obj.DWARF); + DWARFYAML::EmitDebugInfo(OS, Obj.DWARF); } else if (0 == strncmp(&Sec.sectname[0], "__debug_line", 16)) { - yaml2debug_line(OS, Obj.DWARF); + DWARFYAML::EmitDebugLine(OS, Obj.DWARF); } } else { // Fills section data with 0xDEADBEEF diff --git a/tools/yaml2obj/yaml2obj.cpp b/tools/yaml2obj/yaml2obj.cpp index f746d84a38985..e64e3dc1d1798 100644 --- a/tools/yaml2obj/yaml2obj.cpp +++ b/tools/yaml2obj/yaml2obj.cpp @@ -40,31 +40,33 @@ DocNum("docnum", cl::init(1), static cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename")); +LLVM_ATTRIBUTE_NORETURN static void error(Twine Message) { + errs() << Message << "\n"; + exit(1); +} + static int convertYAML(yaml::Input &YIn, raw_ostream &Out) { unsigned CurDocNum = 0; do { if (++CurDocNum == DocNum) { yaml::YamlObjectFile Doc; YIn >> Doc; - if (YIn.error()) { - errs() << "yaml2obj: Failed to parse YAML file!\n"; - return 1; - } - + if (YIn.error()) + error("yaml2obj: Failed to parse YAML file!"); if (Doc.Elf) return yaml2elf(*Doc.Elf, Out); if (Doc.Coff) return yaml2coff(*Doc.Coff, Out); if (Doc.MachO || Doc.FatMachO) return yaml2macho(Doc, Out); - errs() << "yaml2obj: Unknown document type!\n"; - return 1; + if (Doc.Wasm) + return yaml2wasm(*Doc.Wasm, Out); + error("yaml2obj: Unknown document type!"); } } while (YIn.nextDocument()); - errs() << "yaml2obj: Cannot find the " << DocNum - << llvm::getOrdinalSuffix(DocNum) << " document\n"; - return 1; + error("yaml2obj: Cannot find the " + utostr(DocNum) + + llvm::getOrdinalSuffix(DocNum) + " document"); } int main(int argc, char **argv) { @@ -79,10 +81,8 @@ int main(int argc, char **argv) { std::error_code EC; std::unique_ptr<tool_output_file> Out( new tool_output_file(OutputFilename, EC, sys::fs::F_None)); - if (EC) { - errs() << EC.message() << '\n'; - return 1; - } + if (EC) + error("yaml2obj: Error opening '" + OutputFilename + "': " + EC.message()); ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getFileOrSTDIN(Input); diff --git a/tools/yaml2obj/yaml2obj.h b/tools/yaml2obj/yaml2obj.h index 4a637366e1a16..cb8f11904916c 100644 --- a/tools/yaml2obj/yaml2obj.h +++ b/tools/yaml2obj/yaml2obj.h @@ -23,9 +23,8 @@ namespace ELFYAML { struct Object; } -namespace DWARFYAML { -struct Data; -struct PubSection; +namespace WasmYAML { +struct Object; } namespace yaml { @@ -37,15 +36,6 @@ struct YamlObjectFile; int yaml2coff(llvm::COFFYAML::Object &Doc, llvm::raw_ostream &Out); int yaml2elf(llvm::ELFYAML::Object &Doc, llvm::raw_ostream &Out); int yaml2macho(llvm::yaml::YamlObjectFile &Doc, llvm::raw_ostream &Out); - -void yaml2debug_abbrev(llvm::raw_ostream &OS, const llvm::DWARFYAML::Data &DI); -void yaml2debug_str(llvm::raw_ostream &OS, const llvm::DWARFYAML::Data &DI); - -void yaml2debug_aranges(llvm::raw_ostream &OS, const llvm::DWARFYAML::Data &DI); -void yaml2pubsection(llvm::raw_ostream &OS, - const llvm::DWARFYAML::PubSection &Sect, - bool IsLittleEndian); -void yaml2debug_info(llvm::raw_ostream &OS, const llvm::DWARFYAML::Data &DI); -void yaml2debug_line(llvm::raw_ostream &OS, const llvm::DWARFYAML::Data &DI); +int yaml2wasm(llvm::WasmYAML::Object &Doc, llvm::raw_ostream &Out); #endif diff --git a/tools/yaml2obj/yaml2wasm.cpp b/tools/yaml2obj/yaml2wasm.cpp new file mode 100644 index 0000000000000..55267ce0392d9 --- /dev/null +++ b/tools/yaml2obj/yaml2wasm.cpp @@ -0,0 +1,377 @@ +//===- yaml2wasm - Convert YAML to a Wasm object file --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief The Wasm component of yaml2obj. +/// +//===----------------------------------------------------------------------===// +// +#include "yaml2obj.h" +#include "llvm/Object/Wasm.h" +#include "llvm/ObjectYAML/ObjectYAML.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/LEB128.h" + +using namespace llvm; + +/// This parses a yaml stream that represents a Wasm object file. +/// See docs/yaml2obj for the yaml scheema. +class WasmWriter { +public: + WasmWriter(WasmYAML::Object &Obj) : Obj(Obj) {} + int writeWasm(raw_ostream &OS); + int writeRelocSection(raw_ostream &OS, WasmYAML::Section &Sec); + int writeSectionContent(raw_ostream &OS, WasmYAML::CustomSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::TypeSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::ImportSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::FunctionSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::TableSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::MemorySection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::GlobalSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::ExportSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::StartSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::ElemSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::CodeSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::DataSection &Section); + +private: + WasmYAML::Object &Obj; +}; + +static int writeUint64(raw_ostream &OS, uint64_t Value) { + char Data[sizeof(Value)]; + support::endian::write64le(Data, Value); + OS.write(Data, sizeof(Data)); + return 0; +} + +static int writeUint32(raw_ostream &OS, uint32_t Value) { + char Data[sizeof(Value)]; + support::endian::write32le(Data, Value); + OS.write(Data, sizeof(Data)); + return 0; +} + +static int writeUint8(raw_ostream &OS, uint8_t Value) { + char Data[sizeof(Value)]; + memcpy(Data, &Value, sizeof(Data)); + OS.write(Data, sizeof(Data)); + return 0; +} + +static int writeStringRef(StringRef &Str, raw_ostream &OS) { + encodeULEB128(Str.size(), OS); + OS << Str; + return 0; +} + +static int writeLimits(WasmYAML::Limits Lim, raw_ostream &OS) { + encodeULEB128(Lim.Flags, OS); + encodeULEB128(Lim.Initial, OS); + if (Lim.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX) + encodeULEB128(Lim.Maximum, OS); + return 0; +} + +static int writeInitExpr(wasm::WasmInitExpr InitExpr, raw_ostream &OS) { + writeUint8(OS, InitExpr.Opcode); + switch (InitExpr.Opcode) { + case wasm::WASM_OPCODE_I32_CONST: + encodeSLEB128(InitExpr.Value.Int32, OS); + break; + case wasm::WASM_OPCODE_I64_CONST: + encodeSLEB128(InitExpr.Value.Int64, OS); + break; + case wasm::WASM_OPCODE_F32_CONST: + writeUint32(OS, InitExpr.Value.Float32); + break; + case wasm::WASM_OPCODE_F64_CONST: + writeUint64(OS, InitExpr.Value.Float64); + break; + case wasm::WASM_OPCODE_GET_GLOBAL: + encodeULEB128(InitExpr.Value.Global, OS); + break; + default: + errs() << "Unknown opcode in init_expr: " << InitExpr.Opcode; + return 1; + } + writeUint8(OS, wasm::WASM_OPCODE_END); + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::CustomSection &Section) { + // writeStringRef(Section.Name, OS); + // encodeULEB128(Section.Payload.binary_size(), OS); + Section.Payload.writeAsBinary(OS); + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::TypeSection &Section) { + encodeULEB128(Section.Signatures.size(), OS); + for (auto &Sig : Section.Signatures) { + encodeSLEB128(Sig.Form, OS); + encodeULEB128(Sig.ParamTypes.size(), OS); + for (auto ParamType : Sig.ParamTypes) + encodeSLEB128(ParamType, OS); + if (Sig.ReturnType == wasm::WASM_TYPE_NORESULT) { + encodeSLEB128(0, OS); + } else { + encodeULEB128(1, OS); + encodeSLEB128(Sig.ReturnType, OS); + } + } + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::ImportSection &Section) { + encodeULEB128(Section.Imports.size(), OS); + for (auto &Import : Section.Imports) { + writeStringRef(Import.Module, OS); + writeStringRef(Import.Field, OS); + encodeULEB128(Import.Kind, OS); + switch (Import.Kind) { + case wasm::WASM_EXTERNAL_FUNCTION: + encodeULEB128(Import.SigIndex, OS); + break; + case wasm::WASM_EXTERNAL_GLOBAL: + encodeSLEB128(Import.GlobalType, OS); + writeUint8(OS, Import.GlobalMutable); + break; + default: + errs() << "Unknown import type: " << Import.Kind; + return 1; + } + } + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::FunctionSection &Section) { + encodeULEB128(Section.FunctionTypes.size(), OS); + for (uint32_t FuncType : Section.FunctionTypes) { + encodeULEB128(FuncType, OS); + } + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::ExportSection &Section) { + encodeULEB128(Section.Exports.size(), OS); + for (auto &Export : Section.Exports) { + writeStringRef(Export.Name, OS); + encodeULEB128(Export.Kind, OS); + encodeULEB128(Export.Index, OS); + } + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::StartSection &Section) { + encodeULEB128(Section.StartFunction, OS); + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::TableSection &Section) { + encodeULEB128(Section.Tables.size(), OS); + for (auto &Table : Section.Tables) { + encodeSLEB128(Table.ElemType, OS); + writeLimits(Table.TableLimits, OS); + } + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::MemorySection &Section) { + encodeULEB128(Section.Memories.size(), OS); + for (auto &Mem : Section.Memories) { + writeLimits(Mem, OS); + } + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::GlobalSection &Section) { + encodeULEB128(Section.Globals.size(), OS); + for (auto &Global : Section.Globals) { + encodeSLEB128(Global.Type, OS); + writeUint8(OS, Global.Mutable); + writeInitExpr(Global.InitExpr, OS); + } + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::ElemSection &Section) { + encodeULEB128(Section.Segments.size(), OS); + for (auto &Segment : Section.Segments) { + encodeULEB128(Segment.TableIndex, OS); + writeInitExpr(Segment.Offset, OS); + + encodeULEB128(Segment.Functions.size(), OS); + for (auto &Function : Segment.Functions) { + encodeULEB128(Function, OS); + } + } + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::CodeSection &Section) { + encodeULEB128(Section.Functions.size(), OS); + for (auto &Func : Section.Functions) { + std::string OutString; + raw_string_ostream StringStream(OutString); + + encodeULEB128(Func.Locals.size(), StringStream); + for (auto &LocalDecl : Func.Locals) { + encodeULEB128(LocalDecl.Count, StringStream); + encodeSLEB128(LocalDecl.Type, StringStream); + } + + Func.Body.writeAsBinary(StringStream); + + // Write the section size followed by the content + StringStream.flush(); + encodeULEB128(OutString.size(), OS); + OS << OutString; + } + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::DataSection &Section) { + encodeULEB128(Section.Segments.size(), OS); + for (auto &Segment : Section.Segments) { + encodeULEB128(Segment.Index, OS); + writeInitExpr(Segment.Offset, OS); + encodeULEB128(Segment.Content.binary_size(), OS); + Segment.Content.writeAsBinary(OS); + } + return 0; +} + +int WasmWriter::writeRelocSection(raw_ostream &OS, + WasmYAML::Section &Sec) { + StringRef Name; + switch (Sec.Type) { + case wasm::WASM_SEC_CODE: + Name = "reloc.CODE"; + break; + case wasm::WASM_SEC_DATA: + Name = "reloc.DATA"; + break; + default: + llvm_unreachable("not yet implemented"); + return 1; + } + + writeStringRef(Name, OS); + encodeULEB128(Sec.Type, OS); + encodeULEB128(Sec.Relocations.size(), OS); + + for (auto Reloc: Sec.Relocations) { + encodeULEB128(Reloc.Type, OS); + encodeULEB128(Reloc.Offset, OS); + encodeULEB128(Reloc.Index, OS); + switch (Reloc.Type) { + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_LEB: + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_SLEB: + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_I32: + encodeULEB128(Reloc.Addend, OS); + } + } + return 0; +} + + +int WasmWriter::writeWasm(raw_ostream &OS) { + // Write headers + OS.write(wasm::WasmMagic, sizeof(wasm::WasmMagic)); + writeUint32(OS, Obj.Header.Version); + + // Write each section + for (const std::unique_ptr<WasmYAML::Section> &Sec : Obj.Sections) { + encodeULEB128(Sec->Type, OS); + + std::string OutString; + raw_string_ostream StringStream(OutString); + if (auto S = dyn_cast<WasmYAML::CustomSection>(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast<WasmYAML::TypeSection>(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast<WasmYAML::ImportSection>(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast<WasmYAML::FunctionSection>(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast<WasmYAML::TableSection>(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast<WasmYAML::MemorySection>(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast<WasmYAML::GlobalSection>(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast<WasmYAML::ExportSection>(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast<WasmYAML::StartSection>(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast<WasmYAML::ElemSection>(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast<WasmYAML::CodeSection>(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast<WasmYAML::DataSection>(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else { + errs() << "Unknown section type: " << Sec->Type << "\n"; + return 1; + } + StringStream.flush(); + + // Write the section size followed by the content + encodeULEB128(OutString.size(), OS); + OS << OutString; + } + + // write reloc sections for any section that have relocations + for (const std::unique_ptr<WasmYAML::Section> &Sec : Obj.Sections) { + if (Sec->Relocations.empty()) + continue; + + encodeULEB128(wasm::WASM_SEC_CUSTOM, OS); + std::string OutString; + raw_string_ostream StringStream(OutString); + writeRelocSection(StringStream, *Sec); + StringStream.flush(); + + encodeULEB128(OutString.size(), OS); + OS << OutString; + } + + return 0; +} + +int yaml2wasm(llvm::WasmYAML::Object &Doc, raw_ostream &Out) { + WasmWriter Writer(Doc); + + return Writer.writeWasm(Out); +} |