diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-07-13 19:25:18 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-07-13 19:25:18 +0000 |
commit | ca089b24d48ef6fa8da2d0bb8c25bb802c4a95c0 (patch) | |
tree | 3a28a772df9b17aef34f49e3c727965ad28c0c93 /tools | |
parent | 9df3605dea17e84f8183581f6103bd0c79e2a606 (diff) |
Notes
Diffstat (limited to 'tools')
38 files changed, 2058 insertions, 382 deletions
diff --git a/tools/gold/gold-plugin.cpp b/tools/gold/gold-plugin.cpp index cf207d9dbbb3f..6d011bab079d5 100644 --- a/tools/gold/gold-plugin.cpp +++ b/tools/gold/gold-plugin.cpp @@ -477,7 +477,7 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, std::unique_ptr<InputFile> Obj = std::move(*ObjOrErr); - Modules.resize(Modules.size() + 1); + Modules.emplace_back(); claimed_file &cf = Modules.back(); cf.handle = file->handle; diff --git a/tools/lli/OrcLazyJIT.cpp b/tools/lli/OrcLazyJIT.cpp index 2e15894152f98..f1a752e0790de 100644 --- a/tools/lli/OrcLazyJIT.cpp +++ b/tools/lli/OrcLazyJIT.cpp @@ -148,18 +148,19 @@ int llvm::runOrcLazyJIT(std::vector<std::unique_ptr<Module>> Ms, // Add the module, look up main and run it. for (auto &M : Ms) - J.addModule(std::shared_ptr<Module>(std::move(M))); - auto MainSym = J.findSymbol("main"); - - if (!MainSym) { + cantFail(J.addModule(std::shared_ptr<Module>(std::move(M)))); + + if (auto MainSym = J.findSymbol("main")) { + typedef int (*MainFnPtr)(int, const char*[]); + std::vector<const char *> ArgV; + for (auto &Arg : Args) + ArgV.push_back(Arg.c_str()); + auto Main = fromTargetAddress<MainFnPtr>(cantFail(MainSym.getAddress())); + return Main(ArgV.size(), (const char**)ArgV.data()); + } else if (auto Err = MainSym.takeError()) + logAllUnhandledErrors(std::move(Err), llvm::errs(), ""); + else errs() << "Could not find main function.\n"; - return 1; - } - using MainFnPtr = int (*)(int, const char*[]); - std::vector<const char *> ArgV; - for (auto &Arg : Args) - ArgV.push_back(Arg.c_str()); - auto Main = fromTargetAddress<MainFnPtr>(MainSym.getAddress()); - return Main(ArgV.size(), (const char**)ArgV.data()); + return 1; } diff --git a/tools/lli/OrcLazyJIT.h b/tools/lli/OrcLazyJIT.h index fc02a10b514e0..47a2acc4d7e60 100644 --- a/tools/lli/OrcLazyJIT.h +++ b/tools/lli/OrcLazyJIT.h @@ -61,7 +61,8 @@ public: IndirectStubsManagerBuilder IndirectStubsMgrBuilder, bool InlineStubs) : TM(std::move(TM)), DL(this->TM->createDataLayout()), - CCMgr(std::move(CCMgr)), + CCMgr(std::move(CCMgr)), + ObjectLayer([]() { return std::make_shared<SectionMemoryManager>(); }), CompileLayer(ObjectLayer, orc::SimpleCompiler(*this->TM)), IRDumpLayer(CompileLayer, createDebugDumper()), CODLayer(IRDumpLayer, extractSingleFunction, *this->CCMgr, @@ -74,10 +75,14 @@ public: CXXRuntimeOverrides.runDestructors(); // Run any IR destructors. for (auto &DtorRunner : IRStaticDestructorRunners) - DtorRunner.runViaLayer(CODLayer); + if (auto Err = DtorRunner.runViaLayer(CODLayer)) { + // FIXME: OrcLazyJIT should probably take a "shutdownError" callback to + // report these errors on. + report_fatal_error(std::move(Err)); + } } - void addModule(std::shared_ptr<Module> M) { + Error addModule(std::shared_ptr<Module> M) { if (M->getDataLayout().isDefault()) M->setDataLayout(DL); @@ -124,21 +129,27 @@ public: ); // Add the module to the JIT. - ModulesHandle = - CODLayer.addModule(std::move(M), - llvm::make_unique<SectionMemoryManager>(), - std::move(Resolver)); + if (auto ModulesHandleOrErr = + CODLayer.addModule(std::move(M), std::move(Resolver))) + ModulesHandle = std::move(*ModulesHandleOrErr); + else + return ModulesHandleOrErr.takeError(); + } else - CODLayer.addExtraModule(ModulesHandle, std::move(M)); + if (auto Err = CODLayer.addExtraModule(ModulesHandle, std::move(M))) + return Err; // Run the static constructors, and save the static destructor runner for // execution when the JIT is torn down. orc::CtorDtorRunner<CODLayerT> CtorRunner(std::move(CtorNames), ModulesHandle); - CtorRunner.runViaLayer(CODLayer); + if (auto Err = CtorRunner.runViaLayer(CODLayer)) + return Err; IRStaticDestructorRunners.emplace_back(std::move(DtorNames), ModulesHandle); + + return Error::success(); } JITSymbol findSymbol(const std::string &Name) { diff --git a/tools/lli/RemoteJITUtils.h b/tools/lli/RemoteJITUtils.h index 3c82f73ff0724..4e948413865cb 100644 --- a/tools/lli/RemoteJITUtils.h +++ b/tools/lli/RemoteJITUtils.h @@ -84,7 +84,7 @@ public: this->MemMgr = std::move(MemMgr); } - void setResolver(std::unique_ptr<JITSymbolResolver> Resolver) { + void setResolver(std::shared_ptr<JITSymbolResolver> Resolver) { this->Resolver = std::move(Resolver); } @@ -145,7 +145,7 @@ public: private: std::unique_ptr<RuntimeDyld::MemoryManager> MemMgr; - std::unique_ptr<JITSymbolResolver> Resolver; + std::shared_ptr<JITSymbolResolver> Resolver; }; } diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp index f228a36194573..091ca22b4e82c 100644 --- a/tools/lli/lli.cpp +++ b/tools/lli/lli.cpp @@ -646,7 +646,7 @@ int main(int argc, char **argv, char * const *envp) { // else == "if (RemoteMCJIT)" // Remote target MCJIT doesn't (yet) support static constructors. No reason - // it couldn't. This is a limitation of the LLI implemantation, not the + // it couldn't. This is a limitation of the LLI implementation, not the // MCJIT itself. FIXME. // Lanch the remote process and get a channel to it. diff --git a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp index 528247c2dbc3b..529bdf5b7d933 100644 --- a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp +++ b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp @@ -71,6 +71,10 @@ static cl::opt<bool> ShowBinaryBlobs("show-binary-blobs", cl::desc("Print binary blobs using hex escapes")); +static cl::opt<std::string> CheckHash( + "check-hash", + cl::desc("Check module hash using the argument as a string table")); + namespace { /// CurStreamTypeType - A type for CurStreamType @@ -652,13 +656,15 @@ static bool ParseBlock(BitstreamCursor &Stream, BitstreamBlockInfo &BlockInfo, } // If we found a module hash, let's verify that it matches! - if (BlockID == bitc::MODULE_BLOCK_ID && Code == bitc::MODULE_CODE_HASH) { + if (BlockID == bitc::MODULE_BLOCK_ID && Code == bitc::MODULE_CODE_HASH && + !CheckHash.empty()) { if (Record.size() != 5) outs() << " (invalid)"; else { // Recompute the hash and compare it to the one in the bitcode SHA1 Hasher; StringRef Hash; + Hasher.update(CheckHash); { int BlockSize = (CurrentRecordPos / 8) - BlockEntryPos; auto Ptr = Stream.getPointerToByte(BlockEntryPos, BlockSize); diff --git a/tools/llvm-c-test/echo.cpp b/tools/llvm-c-test/echo.cpp index 52ce85c577821..966c0083bf872 100644 --- a/tools/llvm-c-test/echo.cpp +++ b/tools/llvm-c-test/echo.cpp @@ -765,7 +765,7 @@ static void declare_symbols(LLVMModuleRef Src, LLVMModuleRef M) { LLVMValueRef Next = nullptr; if (!Begin) { if (End != nullptr) - report_fatal_error("Range has an end but no begining"); + report_fatal_error("Range has an end but no beginning"); goto FunDecl; } @@ -794,7 +794,7 @@ FunDecl: End = LLVMGetLastFunction(Src); if (!Begin) { if (End != nullptr) - report_fatal_error("Range has an end but no begining"); + report_fatal_error("Range has an end but no beginning"); return; } @@ -844,7 +844,7 @@ static void clone_symbols(LLVMModuleRef Src, LLVMModuleRef M) { LLVMValueRef Next = nullptr; if (!Begin) { if (End != nullptr) - report_fatal_error("Range has an end but no begining"); + report_fatal_error("Range has an end but no beginning"); goto FunClone; } @@ -885,7 +885,7 @@ FunClone: End = LLVMGetLastFunction(Src); if (!Begin) { if (End != nullptr) - report_fatal_error("Range has an end but no begining"); + report_fatal_error("Range has an end but no beginning"); return; } diff --git a/tools/llvm-cov/CodeCoverage.cpp b/tools/llvm-cov/CodeCoverage.cpp index 6179c760d5b20..3cbd6591134b0 100644 --- a/tools/llvm-cov/CodeCoverage.cpp +++ b/tools/llvm-cov/CodeCoverage.cpp @@ -32,6 +32,7 @@ #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/Threading.h" #include "llvm/Support/ThreadPool.h" #include "llvm/Support/ToolOutputFile.h" #include <functional> @@ -705,6 +706,12 @@ int CodeCoverageTool::show(int argc, const char **argv, "project-title", cl::Optional, cl::desc("Set project title for the coverage report")); + cl::opt<unsigned> NumThreads( + "num-threads", cl::init(0), + cl::desc("Number of merge threads to use (default: autodetect)")); + cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), + cl::aliasopt(NumThreads)); + auto Err = commandLineParser(argc, argv); if (Err) return Err; @@ -790,15 +797,19 @@ int CodeCoverageTool::show(int argc, const char **argv, } } - // FIXME: Sink the hardware_concurrency() == 1 check into ThreadPool. - if (!ViewOpts.hasOutputDirectory() || - std::thread::hardware_concurrency() == 1) { + // If NumThreads is not specified, auto-detect a good default. + if (NumThreads == 0) + NumThreads = + std::max(1U, std::min(llvm::heavyweight_hardware_concurrency(), + unsigned(SourceFiles.size()))); + + if (!ViewOpts.hasOutputDirectory() || NumThreads == 1) { for (const std::string &SourceFile : SourceFiles) writeSourceFileView(SourceFile, Coverage.get(), Printer.get(), ShowFilenames); } else { // In -output-dir mode, it's safe to use multiple threads to print files. - ThreadPool Pool; + ThreadPool Pool(NumThreads); for (const std::string &SourceFile : SourceFiles) Pool.async(&CodeCoverageTool::writeSourceFileView, this, SourceFile, Coverage.get(), Printer.get(), ShowFilenames); diff --git a/tools/llvm-lto/llvm-lto.cpp b/tools/llvm-lto/llvm-lto.cpp index ccc673be45702..87cd13ad70de9 100644 --- a/tools/llvm-lto/llvm-lto.cpp +++ b/tools/llvm-lto/llvm-lto.cpp @@ -383,7 +383,7 @@ loadAllFilesForIndex(const ModuleSummaryIndex &Index) { for (auto &ModPath : Index.modulePaths()) { const auto &Filename = ModPath.first(); - auto CurrentActivity = "loading file '" + Filename + "'"; + std::string CurrentActivity = ("loading file '" + Filename + "'").str(); auto InputOrErr = MemoryBuffer::getFile(Filename); error(InputOrErr, "error " + CurrentActivity); InputBuffers.push_back(std::move(*InputOrErr)); @@ -475,7 +475,7 @@ private: std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers; for (unsigned i = 0; i < InputFilenames.size(); ++i) { auto &Filename = InputFilenames[i]; - StringRef CurrentActivity = "loading file '" + Filename + "'"; + std::string CurrentActivity = "loading file '" + Filename + "'"; auto InputOrErr = MemoryBuffer::getFile(Filename); error(InputOrErr, "error " + CurrentActivity); InputBuffers.push_back(std::move(*InputOrErr)); @@ -710,7 +710,7 @@ private: std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers; for (unsigned i = 0; i < InputFilenames.size(); ++i) { auto &Filename = InputFilenames[i]; - StringRef CurrentActivity = "loading file '" + Filename + "'"; + std::string CurrentActivity = "loading file '" + Filename + "'"; auto InputOrErr = MemoryBuffer::getFile(Filename); error(InputOrErr, "error " + CurrentActivity); InputBuffers.push_back(std::move(*InputOrErr)); diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp index be5635a3d4c69..812f1af3ac680 100644 --- a/tools/llvm-objdump/llvm-objdump.cpp +++ b/tools/llvm-objdump/llvm-objdump.cpp @@ -1032,7 +1032,7 @@ static std::error_code getRelocationValueString(const MachOObjectFile *Obj, case MachO::ARM_RELOC_HALF_SECTDIFF: { // Half relocations steal a bit from the length field to encode // whether this is an upper16 or a lower16 relocation. - bool isUpper = Obj->getAnyRelocationLength(RE) >> 1; + bool isUpper = (Obj->getAnyRelocationLength(RE) & 0x1) == 1; if (isUpper) fmt << ":upper16:("; diff --git a/tools/llvm-pdbutil/CMakeLists.txt b/tools/llvm-pdbutil/CMakeLists.txt index 7a3245424efc6..bc28e6bdd7eaa 100644 --- a/tools/llvm-pdbutil/CMakeLists.txt +++ b/tools/llvm-pdbutil/CMakeLists.txt @@ -11,6 +11,7 @@ add_llvm_tool(llvm-pdbutil Analyze.cpp BytesOutputStyle.cpp Diff.cpp + DiffPrinter.cpp DumpOutputStyle.cpp llvm-pdbutil.cpp FormatUtil.cpp diff --git a/tools/llvm-pdbutil/Diff.cpp b/tools/llvm-pdbutil/Diff.cpp index 9b38ae1d603ef..aad4e1bf14279 100644 --- a/tools/llvm-pdbutil/Diff.cpp +++ b/tools/llvm-pdbutil/Diff.cpp @@ -9,22 +9,162 @@ #include "Diff.h" +#include "DiffPrinter.h" +#include "FormatUtil.h" #include "StreamUtil.h" #include "llvm-pdbutil.h" +#include "llvm/ADT/StringSet.h" + +#include "llvm/DebugInfo/PDB/Native/DbiStream.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/PDBStringTable.h" #include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/FormatAdapters.h" #include "llvm/Support/FormatProviders.h" #include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/Path.h" using namespace llvm; using namespace llvm::pdb; +namespace { +// Compare and format two stream numbers. Stream numbers are considered +// identical if they contain the same value, equivalent if they are both +// the invalid stream or neither is the invalid stream, and different if +// one is the invalid stream and another isn't. +struct StreamNumberProvider { + static DiffResult compare(uint16_t L, uint16_t R) { + if (L == R) + return DiffResult::IDENTICAL; + bool LP = L != kInvalidStreamIndex; + bool RP = R != kInvalidStreamIndex; + if (LP != RP) + return DiffResult::DIFFERENT; + return DiffResult::EQUIVALENT; + } + + static std::string format(uint16_t SN, bool Right) { + if (SN == kInvalidStreamIndex) + return "(not present)"; + return formatv("{0}", SN).str(); + } +}; + +// Compares and formats two module indices. Modis are considered identical +// if they are identical, equivalent if they either both contain a value or +// both don't contain a value, and different if one contains a value and the +// other doesn't. +struct ModiProvider { + DiffResult compare(Optional<uint32_t> L, Optional<uint32_t> R) { + if (L == R) + return DiffResult::IDENTICAL; + if (L.hasValue() != R.hasValue()) + return DiffResult::DIFFERENT; + return DiffResult::EQUIVALENT; + } + + std::string format(Optional<uint32_t> Modi, bool Right) { + if (!Modi.hasValue()) + return "(not present)"; + return formatv("{0}", *Modi).str(); + } +}; + +// Compares and formats two paths embedded in the PDB, ignoring the beginning +// of the path if the user specified it as a "root path" on the command line. +struct BinaryPathProvider { + explicit BinaryPathProvider(uint32_t MaxLen) : MaxLen(MaxLen) {} + + DiffResult compare(StringRef L, StringRef R) { + if (L == R) + return DiffResult::IDENTICAL; + + SmallString<64> LN = removeRoot(L, false); + SmallString<64> RN = removeRoot(R, true); + + return (LN.equals_lower(RN)) ? DiffResult::EQUIVALENT + : DiffResult::DIFFERENT; + } + + std::string format(StringRef S, bool Right) { + if (S.empty()) + return "(empty)"; + + SmallString<64> Native = removeRoot(S, Right); + return truncateStringFront(Native.str(), MaxLen); + } + + SmallString<64> removeRoot(StringRef Path, bool IsRight) const { + SmallString<64> Native(Path); + auto &RootOpt = IsRight ? opts::diff::RightRoot : opts::diff::LeftRoot; + SmallString<64> Root(static_cast<std::string>(RootOpt)); + // pdb paths always use windows syntax, convert slashes to backslashes. + sys::path::native(Root, sys::path::Style::windows); + if (sys::path::has_stem(Root, sys::path::Style::windows)) + sys::path::append(Root, sys::path::Style::windows, + sys::path::get_separator(sys::path::Style::windows)); + + sys::path::replace_path_prefix(Native, Root, "", sys::path::Style::windows); + return Native; + } + uint32_t MaxLen; +}; + +// Compare and format two stream purposes. For general streams, this just +// compares the description. For module streams it uses the path comparison +// algorithm taking into consideration the binary root, described above. +// Formatting stream purposes just prints the stream purpose, except for +// module streams and named streams, where it prefixes the name / module +// with an identifier. Example: +// +// Named Stream "\names" +// Module Stream "foo.obj" +// +// If a named stream is too long to fit in a column, it is truncated at the +// end, and if a module is too long to fit in a column, it is truncated at the +// beginning. Example: +// +// Named Stream "\Really Long Str..." +// Module Stream "...puts\foo.obj" +// +struct StreamPurposeProvider { + explicit StreamPurposeProvider(uint32_t MaxLen) : MaxLen(MaxLen) {} + + DiffResult compare(const std::pair<StreamPurpose, std::string> &L, + const std::pair<StreamPurpose, std::string> &R) { + if (L.first != R.first) + return DiffResult::DIFFERENT; + if (L.first == StreamPurpose::ModuleStream) { + BinaryPathProvider PathProvider(MaxLen); + return PathProvider.compare(L.second, R.second); + } + return (L.second == R.second) ? DiffResult::IDENTICAL + : DiffResult::DIFFERENT; + } + + std::string format(const std::pair<StreamPurpose, std::string> &P, + bool Right) { + if (P.first == StreamPurpose::Other) + return truncateStringBack(P.second, MaxLen); + if (P.first == StreamPurpose::NamedStream) + return truncateQuotedNameBack("Named Stream", P.second, MaxLen); + + assert(P.first == StreamPurpose::ModuleStream); + uint32_t ExtraChars = strlen("Module \"\""); + BinaryPathProvider PathProvider(MaxLen - ExtraChars); + std::string Result = PathProvider.format(P.second, Right); + return formatv("Module \"{0}\"", Result); + } + + uint32_t MaxLen; +}; +} // namespace + namespace llvm { template <> struct format_provider<PdbRaw_FeatureSig> { static void format(const PdbRaw_FeatureSig &Sig, raw_ostream &Stream, @@ -49,47 +189,6 @@ template <> struct format_provider<PdbRaw_FeatureSig> { 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) {} @@ -136,300 +235,363 @@ Error DiffStyle::dump() { 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 (!Diffs) - outs() << "MSF Super Block: No differences detected...\n"; + DiffPrinter D(2, "MSF Super Block", 16, 20, opts::diff::PrintResultColumn, + opts::diff::PrintValueColumns, outs()); + D.printExplicit("File", DiffResult::UNSPECIFIED, + truncateStringFront(File1.getFilePath(), 18), + truncateStringFront(File2.getFilePath(), 18)); + D.print("Block Size", File1.getBlockSize(), File2.getBlockSize()); + D.print("Block Count", File1.getBlockCount(), File2.getBlockCount()); + D.print("Unknown 1", File1.getUnknown1(), File2.getUnknown1()); + D.print("Directory Size", File1.getNumDirectoryBytes(), + File2.getNumDirectoryBytes()); return Error::success(); } Error DiffStyle::diffStreamDirectory() { - SmallVector<std::string, 32> P; - SmallVector<std::string, 32> Q; + DiffPrinter D(2, "Stream Directory", 30, 20, opts::diff::PrintResultColumn, + opts::diff::PrintValueColumns, outs()); + D.printExplicit("File", DiffResult::UNSPECIFIED, + truncateStringFront(File1.getFilePath(), 18), + truncateStringFront(File2.getFilePath(), 18)); + + SmallVector<std::pair<StreamPurpose, std::string>, 32> P; + SmallVector<std::pair<StreamPurpose, std::string>, 32> Q; discoverStreamPurposes(File1, P); discoverStreamPurposes(File2, Q); - outs() << "Stream Directory: Searching for differences...\n"; - - bool HasDifferences = false; + D.print("Stream Count", File1.getNumStreams(), File2.getNumStreams()); 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()); + // Scan all streams in the left hand side, looking for ones that are also + // in the right. Each time we find one, remove it. When we're done, Q + // should contain all the streams that are in the right but not in the left. + StreamPurposeProvider StreamProvider(28); + for (const auto &P : PI) { + typedef decltype(PI) ContainerType; + typedef typename ContainerType::value_type value_type; + + auto Iter = llvm::find_if(QI, [P, &StreamProvider](const value_type &V) { + DiffResult Result = StreamProvider.compare(P.value(), V.value()); + return Result == DiffResult::EQUIVALENT || + Result == DiffResult::IDENTICAL; + }); + + if (Iter == QI.end()) { + D.printExplicit(StreamProvider.format(P.value(), false), + DiffResult::DIFFERENT, P.index(), "(not present)"); + continue; } - } - 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()); - } + D.print<EquivalentDiffProvider>(StreamProvider.format(P.value(), false), + P.index(), Iter->index()); + QI.erase(Iter); } - 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"; + + for (const auto &Q : QI) { + D.printExplicit(StreamProvider.format(Q.value(), true), + DiffResult::DIFFERENT, "(not present)", Q.index()); } - if (!HasDifferences) - outs() << "Stream Directory: No differences detected!\n"; return Error::success(); } Error DiffStyle::diffStringTable() { + DiffPrinter D(2, "String Table", 30, 20, opts::diff::PrintResultColumn, + opts::diff::PrintValueColumns, outs()); + D.printExplicit("File", DiffResult::UNSPECIFIED, + truncateStringFront(File1.getFilePath(), 18), + truncateStringFront(File2.getFilePath(), 18)); + 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()); - } - } + std::string Count1 = Has1 ? llvm::utostr(ExpectedST1->getNameCount()) + : "(string table not present)"; + std::string Count2 = Has2 ? llvm::utostr(ExpectedST2->getNameCount()) + : "(string table not present)"; + D.print("Number of Strings", Count1, Count2); + + if (!Has1 || !Has2) { 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()); + D.print("Hash Version", ST1.getHashVersion(), ST2.getHashVersion()); + D.print("Byte Size", ST1.getByteSize(), ST2.getByteSize()); + D.print("Signature", ST1.getSignature(), ST2.getSignature()); // Both have a valid string table, dive in and compare individual strings. auto IdList1 = ST1.name_ids(); auto IdList2 = ST2.name_ids(); - std::vector<StringRef> Strings1, Strings2; - Strings1.reserve(IdList1.size()); - Strings2.reserve(IdList2.size()); + StringSet<> LS; + StringSet<> RS; + uint32_t Empty1 = 0; + uint32_t Empty2 = 0; for (auto ID : IdList1) { auto S = ST1.getStringForID(ID); if (!S) return S.takeError(); - Strings1.push_back(*S); + if (S->empty()) + ++Empty1; + else + LS.insert(*S); } for (auto ID : IdList2) { auto S = ST2.getStringForID(ID); if (!S) return S.takeError(); - Strings2.push_back(*S); + if (S->empty()) + ++Empty2; + else + RS.insert(*S); + } + D.print("Empty Strings", Empty1, Empty2); + + for (const auto &S : LS) { + auto R = RS.find(S.getKey()); + std::string Truncated = truncateStringMiddle(S.getKey(), 28); + uint32_t I = cantFail(ST1.getIDForString(S.getKey())); + if (R == RS.end()) { + D.printExplicit(Truncated, DiffResult::DIFFERENT, I, "(not present)"); + continue; + } + + uint32_t J = cantFail(ST2.getIDForString(R->getKey())); + D.print<EquivalentDiffProvider>(Truncated, I, J); + RS.erase(R); } - 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()); + for (const auto &S : RS) { + auto L = LS.find(S.getKey()); + std::string Truncated = truncateStringMiddle(S.getKey(), 28); + uint32_t J = cantFail(ST2.getIDForString(S.getKey())); + if (L == LS.end()) { + D.printExplicit(Truncated, DiffResult::DIFFERENT, "(not present)", J); + continue; + } + + uint32_t I = cantFail(ST1.getIDForString(L->getKey())); + D.print<EquivalentDiffProvider>(Truncated, I, J); } - if (!HasDiff) - outs() << "String Table: No differences detected!\n"; return Error::success(); } Error DiffStyle::diffFreePageMap() { return Error::success(); } Error DiffStyle::diffInfoStream() { + DiffPrinter D(2, "PDB Stream", 22, 40, opts::diff::PrintResultColumn, + opts::diff::PrintValueColumns, outs()); + D.printExplicit("File", DiffResult::UNSPECIFIED, + truncateStringFront(File1.getFilePath(), 38), + truncateStringFront(File2.getFilePath(), 38)); + 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()); + std::string L = Has1 ? "(present)" : "(not present)"; + std::string R = Has2 ? "(present)" : "(not present)"; + D.print("Stream", L, R); + + consumeError(ExpectedInfo1.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; + D.print("Stream Size", IS1.getStreamSize(), IS2.getStreamSize()); + D.print("Age", IS1.getAge(), IS2.getAge()); + D.print("Guid", IS1.getGuid(), IS2.getGuid()); + D.print("Signature", IS1.getSignature(), IS2.getSignature()); + D.print("Version", IS1.getVersion(), IS2.getVersion()); + D.diffUnorderedArray("Feature", IS1.getFeatureSignatures(), + IS2.getFeatureSignatures()); + D.print("Named Stream Size", IS1.getNamedStreamMapByteSize(), + IS2.getNamedStreamMapByteSize()); + StringMap<uint32_t> NSL = IS1.getNamedStreams().getStringMap(); + StringMap<uint32_t> NSR = IS2.getNamedStreams().getStringMap(); + D.diffUnorderedMap<EquivalentDiffProvider>("Named Stream", NSL, NSR); + return Error::success(); +} + +static std::vector<std::pair<uint32_t, DbiModuleDescriptor>> +getModuleDescriptors(const DbiModuleList &ML) { + std::vector<std::pair<uint32_t, DbiModuleDescriptor>> List; + List.reserve(ML.getModuleCount()); + for (uint32_t I = 0; I < ML.getModuleCount(); ++I) + List.emplace_back(I, ML.getModuleDescriptor(I)); + return List; +} + +static void +diffOneModule(DiffPrinter &D, + const std::pair<uint32_t, DbiModuleDescriptor> Item, + std::vector<std::pair<uint32_t, DbiModuleDescriptor>> &Other, + bool ItemIsRight) { + StreamPurposeProvider HeaderProvider(70); + std::pair<StreamPurpose, std::string> Header; + Header.first = StreamPurpose::ModuleStream; + Header.second = Item.second.getModuleName(); + D.printFullRow(HeaderProvider.format(Header, ItemIsRight)); + + const auto *L = &Item; + + BinaryPathProvider PathProvider(28); + auto Iter = llvm::find_if( + Other, [&PathProvider, ItemIsRight, + L](const std::pair<uint32_t, DbiModuleDescriptor> &Other) { + const auto *Left = L; + const auto *Right = &Other; + if (ItemIsRight) + std::swap(Left, Right); + DiffResult Result = PathProvider.compare(Left->second.getModuleName(), + Right->second.getModuleName()); + return Result == DiffResult::EQUIVALENT || + Result == DiffResult::IDENTICAL; + }); + if (Iter == Other.end()) { + // We didn't find this module at all on the other side. Just print one row + // and continue. + D.print<ModiProvider>("- Modi", Item.first, None); + return; } - 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(); + // We did find this module. Go through and compare each field. + const auto *R = &*Iter; + if (ItemIsRight) + std::swap(L, R); + + D.print<ModiProvider>("- Modi", L->first, R->first); + D.print<BinaryPathProvider>("- Obj File Name", L->second.getObjFileName(), + R->second.getObjFileName(), PathProvider); + D.print<StreamNumberProvider>("- Debug Stream", + L->second.getModuleStreamIndex(), + R->second.getModuleStreamIndex()); + D.print("- C11 Byte Size", L->second.getC11LineInfoByteSize(), + R->second.getC11LineInfoByteSize()); + D.print("- C13 Byte Size", L->second.getC13LineInfoByteSize(), + R->second.getC13LineInfoByteSize()); + D.print("- # of files", L->second.getNumberOfFiles(), + R->second.getNumberOfFiles()); + D.print("- Pdb File Path Index", L->second.getPdbFilePathNameIndex(), + R->second.getPdbFilePathNameIndex()); + D.print("- Source File Name Index", L->second.getSourceFileNameIndex(), + R->second.getSourceFileNameIndex()); + D.print("- Symbol Byte Size", L->second.getSymbolDebugInfoByteSize(), + R->second.getSymbolDebugInfoByteSize()); + Other.erase(Iter); } -Error DiffStyle::diffDbiStream() { return Error::success(); } +Error DiffStyle::diffDbiStream() { + DiffPrinter D(2, "DBI Stream", 40, 30, opts::diff::PrintResultColumn, + opts::diff::PrintValueColumns, outs()); + D.printExplicit("File", DiffResult::UNSPECIFIED, + truncateStringFront(File1.getFilePath(), 28), + truncateStringFront(File2.getFilePath(), 28)); + + auto ExpectedDbi1 = File1.getPDBDbiStream(); + auto ExpectedDbi2 = File2.getPDBDbiStream(); + + bool Has1 = !!ExpectedDbi1; + bool Has2 = !!ExpectedDbi2; + if (!(Has1 && Has2)) { + std::string L = Has1 ? "(present)" : "(not present)"; + std::string R = Has2 ? "(present)" : "(not present)"; + D.print("Stream", L, R); + + consumeError(ExpectedDbi1.takeError()); + consumeError(ExpectedDbi2.takeError()); + return Error::success(); + } + + auto &DL = *ExpectedDbi1; + auto &DR = *ExpectedDbi2; + + D.print("Dbi Version", (uint32_t)DL.getDbiVersion(), + (uint32_t)DR.getDbiVersion()); + D.print("Age", DL.getAge(), DR.getAge()); + D.print("Machine", (uint16_t)DL.getMachineType(), + (uint16_t)DR.getMachineType()); + D.print("Flags", DL.getFlags(), DR.getFlags()); + D.print("Build Major", DL.getBuildMajorVersion(), DR.getBuildMajorVersion()); + D.print("Build Minor", DL.getBuildMinorVersion(), DR.getBuildMinorVersion()); + D.print("Build Number", DL.getBuildNumber(), DR.getBuildNumber()); + D.print("PDB DLL Version", DL.getPdbDllVersion(), DR.getPdbDllVersion()); + D.print("PDB DLL RBLD", DL.getPdbDllRbld(), DR.getPdbDllRbld()); + D.print<StreamNumberProvider>("DBG (FPO)", + DL.getDebugStreamIndex(DbgHeaderType::FPO), + DR.getDebugStreamIndex(DbgHeaderType::FPO)); + D.print<StreamNumberProvider>( + "DBG (Exception)", DL.getDebugStreamIndex(DbgHeaderType::Exception), + DR.getDebugStreamIndex(DbgHeaderType::Exception)); + D.print<StreamNumberProvider>("DBG (Fixup)", + DL.getDebugStreamIndex(DbgHeaderType::Fixup), + DR.getDebugStreamIndex(DbgHeaderType::Fixup)); + D.print<StreamNumberProvider>( + "DBG (OmapToSrc)", DL.getDebugStreamIndex(DbgHeaderType::OmapToSrc), + DR.getDebugStreamIndex(DbgHeaderType::OmapToSrc)); + D.print<StreamNumberProvider>( + "DBG (OmapFromSrc)", DL.getDebugStreamIndex(DbgHeaderType::OmapFromSrc), + DR.getDebugStreamIndex(DbgHeaderType::OmapFromSrc)); + D.print<StreamNumberProvider>( + "DBG (SectionHdr)", DL.getDebugStreamIndex(DbgHeaderType::SectionHdr), + DR.getDebugStreamIndex(DbgHeaderType::SectionHdr)); + D.print<StreamNumberProvider>( + "DBG (TokenRidMap)", DL.getDebugStreamIndex(DbgHeaderType::TokenRidMap), + DR.getDebugStreamIndex(DbgHeaderType::TokenRidMap)); + D.print<StreamNumberProvider>("DBG (Xdata)", + DL.getDebugStreamIndex(DbgHeaderType::Xdata), + DR.getDebugStreamIndex(DbgHeaderType::Xdata)); + D.print<StreamNumberProvider>("DBG (Pdata)", + DL.getDebugStreamIndex(DbgHeaderType::Pdata), + DR.getDebugStreamIndex(DbgHeaderType::Pdata)); + D.print<StreamNumberProvider>("DBG (NewFPO)", + DL.getDebugStreamIndex(DbgHeaderType::NewFPO), + DR.getDebugStreamIndex(DbgHeaderType::NewFPO)); + D.print<StreamNumberProvider>( + "DBG (SectionHdrOrig)", + DL.getDebugStreamIndex(DbgHeaderType::SectionHdrOrig), + DR.getDebugStreamIndex(DbgHeaderType::SectionHdrOrig)); + D.print<StreamNumberProvider>("Globals Stream", + DL.getGlobalSymbolStreamIndex(), + DR.getGlobalSymbolStreamIndex()); + D.print<StreamNumberProvider>("Publics Stream", + DL.getPublicSymbolStreamIndex(), + DR.getPublicSymbolStreamIndex()); + D.print<StreamNumberProvider>("Symbol Records", DL.getSymRecordStreamIndex(), + DR.getSymRecordStreamIndex()); + D.print("Has CTypes", DL.hasCTypes(), DR.hasCTypes()); + D.print("Is Incrementally Linked", DL.isIncrementallyLinked(), + DR.isIncrementallyLinked()); + D.print("Is Stripped", DL.isStripped(), DR.isStripped()); + const DbiModuleList &ML = DL.modules(); + const DbiModuleList &MR = DR.modules(); + D.print("Module Count", ML.getModuleCount(), MR.getModuleCount()); + D.print("Source File Count", ML.getSourceFileCount(), + MR.getSourceFileCount()); + auto MDL = getModuleDescriptors(ML); + auto MDR = getModuleDescriptors(MR); + // Scan all module descriptors from the left, and look for corresponding + // module descriptors on the right. + for (const auto &L : MDL) + diffOneModule(D, L, MDR, false); + + for (const auto &R : MDR) + diffOneModule(D, R, MDL, true); + + return Error::success(); +} Error DiffStyle::diffSectionContribs() { return Error::success(); } diff --git a/tools/llvm-pdbutil/DiffPrinter.cpp b/tools/llvm-pdbutil/DiffPrinter.cpp new file mode 100644 index 0000000000000..dd61cc1825936 --- /dev/null +++ b/tools/llvm-pdbutil/DiffPrinter.cpp @@ -0,0 +1,147 @@ + +#include "DiffPrinter.h" + +#include "llvm/Support/FormatAdapters.h" + +using namespace llvm; +using namespace llvm::pdb; + +namespace { +struct Colorize { + Colorize(raw_ostream &OS, DiffResult Result) : OS(OS) { + if (!OS.has_colors()) + return; + switch (Result) { + case DiffResult::IDENTICAL: + OS.changeColor(raw_ostream::Colors::GREEN, false); + break; + case DiffResult::EQUIVALENT: + OS.changeColor(raw_ostream::Colors::YELLOW, true); + break; + default: + OS.changeColor(raw_ostream::Colors::RED, false); + break; + } + } + + ~Colorize() { + if (OS.has_colors()) + OS.resetColor(); + } + + raw_ostream &OS; +}; +} + +DiffPrinter::DiffPrinter(uint32_t Indent, StringRef Header, + uint32_t PropertyWidth, uint32_t FieldWidth, + bool Result, bool Fields, raw_ostream &Stream) + : PrintResult(Result), PrintValues(Fields), Indent(Indent), + PropertyWidth(PropertyWidth), FieldWidth(FieldWidth), OS(Stream) { + printHeaderRow(); + printFullRow(Header); +} + +DiffPrinter::~DiffPrinter() {} + +uint32_t DiffPrinter::tableWidth() const { + // `|` + uint32_t W = 1; + + // `<width>|` + W += PropertyWidth + 1; + + if (PrintResult) { + // ` I |` + W += 4; + } + + if (PrintValues) { + // `<width>|<width>|` + W += 2 * (FieldWidth + 1); + } + return W; +} + +void DiffPrinter::printFullRow(StringRef Text) { + newLine(); + printValue(Text, DiffResult::UNSPECIFIED, AlignStyle::Center, + tableWidth() - 2, true); + printSeparatorRow(); +} + +void DiffPrinter::printSeparatorRow() { + newLine(); + OS << formatv("{0}", fmt_repeat('-', PropertyWidth)); + if (PrintResult) { + OS << '+'; + OS << formatv("{0}", fmt_repeat('-', 3)); + } + if (PrintValues) { + OS << '+'; + OS << formatv("{0}", fmt_repeat('-', FieldWidth)); + OS << '+'; + OS << formatv("{0}", fmt_repeat('-', FieldWidth)); + } + OS << '|'; +} + +void DiffPrinter::printHeaderRow() { + newLine('-'); + OS << formatv("{0}", fmt_repeat('-', tableWidth() - 1)); +} + +void DiffPrinter::newLine(char InitialChar) { + OS << "\n"; + OS.indent(Indent) << InitialChar; +} + +void DiffPrinter::printExplicit(StringRef Property, DiffResult C, + StringRef Left, StringRef Right) { + newLine(); + printValue(Property, DiffResult::UNSPECIFIED, AlignStyle::Right, + PropertyWidth, true); + printResult(C); + printValue(Left, C, AlignStyle::Center, FieldWidth, false); + printValue(Right, C, AlignStyle::Center, FieldWidth, false); + printSeparatorRow(); +} + +void DiffPrinter::printResult(DiffResult Result) { + if (!PrintResult) + return; + switch (Result) { + case DiffResult::DIFFERENT: + printValue("D", Result, AlignStyle::Center, 3, true); + break; + case DiffResult::EQUIVALENT: + printValue("E", Result, AlignStyle::Center, 3, true); + break; + case DiffResult::IDENTICAL: + printValue("I", Result, AlignStyle::Center, 3, true); + break; + case DiffResult::UNSPECIFIED: + printValue(" ", Result, AlignStyle::Center, 3, true); + break; + } +} + +void DiffPrinter::printValue(StringRef Value, DiffResult C, AlignStyle Style, + uint32_t Width, bool Force) { + if (!Force && !PrintValues) + return; + + if (Style == AlignStyle::Right) + --Width; + + std::string FormattedItem = + formatv("{0}", fmt_align(Value, Style, Width)).str(); + if (C != DiffResult::UNSPECIFIED) { + Colorize Color(OS, C); + OS << FormattedItem; + } else + OS << FormattedItem; + if (Style == AlignStyle::Right) + OS << ' '; + OS << '|'; +} diff --git a/tools/llvm-pdbutil/DiffPrinter.h b/tools/llvm-pdbutil/DiffPrinter.h new file mode 100644 index 0000000000000..475747d8dc11d --- /dev/null +++ b/tools/llvm-pdbutil/DiffPrinter.h @@ -0,0 +1,172 @@ +//===- DiffPrinter.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_DIFFPRINTER_H +#define LLVM_TOOLS_LLVMPDBDUMP_DIFFPRINTER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" + +#include <list> +#include <unordered_set> + +namespace std { +template <> struct hash<llvm::pdb::PdbRaw_FeatureSig> { + typedef llvm::pdb::PdbRaw_FeatureSig argument_type; + typedef std::size_t result_type; + result_type operator()(argument_type Item) const { + return std::hash<uint32_t>{}(uint32_t(Item)); + } +}; +} // namespace std + +namespace llvm { +namespace pdb { + +class PDBFile; + +enum class DiffResult { UNSPECIFIED, IDENTICAL, EQUIVALENT, DIFFERENT }; + +struct IdenticalDiffProvider { + template <typename T, typename U> + DiffResult compare(const T &Left, const U &Right) { + return (Left == Right) ? DiffResult::IDENTICAL : DiffResult::DIFFERENT; + } + + template <typename T> std::string format(const T &Item, bool Right) { + return formatv("{0}", Item).str(); + } +}; + +struct EquivalentDiffProvider { + template <typename T, typename U> + DiffResult compare(const T &Left, const U &Right) { + return (Left == Right) ? DiffResult::IDENTICAL : DiffResult::EQUIVALENT; + } + + template <typename T> std::string format(const T &Item, bool Right) { + return formatv("{0}", Item).str(); + } +}; + +class DiffPrinter { +public: + DiffPrinter(uint32_t Indent, StringRef Header, uint32_t PropertyWidth, + uint32_t FieldWidth, bool Result, bool Values, + raw_ostream &Stream); + ~DiffPrinter(); + + template <typename T, typename U> struct Identical {}; + + template <typename Provider = IdenticalDiffProvider, typename T, typename U> + void print(StringRef Property, const T &Left, const U &Right, + Provider P = Provider()) { + std::string L = P.format(Left, false); + std::string R = P.format(Right, true); + + DiffResult Result = P.compare(Left, Right); + printExplicit(Property, Result, L, R); + } + + void printExplicit(StringRef Property, DiffResult C, StringRef Left, + StringRef Right); + + template <typename T, typename U> + void printExplicit(StringRef Property, DiffResult C, const T &Left, + const U &Right) { + std::string L = formatv("{0}", Left).str(); + std::string R = formatv("{0}", Right).str(); + printExplicit(Property, C, StringRef(L), StringRef(R)); + } + + template <typename T, typename U> + void diffUnorderedArray(StringRef Property, ArrayRef<T> Left, + ArrayRef<U> Right) { + std::unordered_set<T> LS(Left.begin(), Left.end()); + std::unordered_set<U> RS(Right.begin(), Right.end()); + std::string Count1 = formatv("{0} element(s)", Left.size()); + std::string Count2 = formatv("{0} element(s)", Right.size()); + print(std::string(Property) + "s (set)", Count1, Count2); + for (const auto &L : LS) { + auto Iter = RS.find(L); + std::string Text = formatv("{0}", L).str(); + if (Iter == RS.end()) { + print(Property, Text, "(not present)"); + continue; + } + print(Property, Text, Text); + RS.erase(Iter); + } + for (const auto &R : RS) { + auto Iter = LS.find(R); + std::string Text = formatv("{0}", R).str(); + if (Iter == LS.end()) { + print(Property, "(not present)", Text); + continue; + } + print(Property, Text, Text); + } + } + + template <typename ValueProvider = IdenticalDiffProvider, typename T, + typename U> + void diffUnorderedMap(StringRef Property, const StringMap<T> &Left, + const StringMap<U> &Right, + ValueProvider P = ValueProvider()) { + StringMap<U> RightCopy(Right); + + std::string Count1 = formatv("{0} element(s)", Left.size()); + std::string Count2 = formatv("{0} element(s)", Right.size()); + print(std::string(Property) + "s (map)", Count1, Count2); + + for (const auto &L : Left) { + auto Iter = RightCopy.find(L.getKey()); + if (Iter == RightCopy.end()) { + printExplicit(L.getKey(), DiffResult::DIFFERENT, L.getValue(), + "(not present)"); + continue; + } + + print(L.getKey(), L.getValue(), Iter->getValue(), P); + RightCopy.erase(Iter); + } + + for (const auto &R : RightCopy) { + printExplicit(R.getKey(), DiffResult::DIFFERENT, "(not present)", + R.getValue()); + } + } + + void printFullRow(StringRef Text); + +private: + uint32_t tableWidth() const; + + void printHeaderRow(); + void printSeparatorRow(); + void newLine(char InitialChar = '|'); + void printValue(StringRef Value, DiffResult C, AlignStyle Style, + uint32_t Width, bool Force); + void printResult(DiffResult Result); + + bool PrintResult; + bool PrintValues; + uint32_t Indent; + uint32_t PropertyWidth; + uint32_t FieldWidth; + raw_ostream &OS; +}; +} // namespace pdb +} // namespace llvm + +#endif diff --git a/tools/llvm-pdbutil/DumpOutputStyle.cpp b/tools/llvm-pdbutil/DumpOutputStyle.cpp index a1f919b4dd065..0642d841fd9f2 100644 --- a/tools/llvm-pdbutil/DumpOutputStyle.cpp +++ b/tools/llvm-pdbutil/DumpOutputStyle.cpp @@ -418,6 +418,13 @@ Error DumpOutputStyle::dumpModules() { P.formatLine(" debug stream: {0}, # files: {1}, has ec info: {2}", Modi.getModuleStreamIndex(), Modi.getNumberOfFiles(), Modi.hasECInfo()); + StringRef PdbFilePath = + Err(Stream.getECName(Modi.getPdbFilePathNameIndex())); + StringRef SrcFilePath = + Err(Stream.getECName(Modi.getSourceFileNameIndex())); + P.formatLine(" pdb file ni: {0} `{1}`, src file ni: {2} `{3}`", + Modi.getPdbFilePathNameIndex(), PdbFilePath, + Modi.getSourceFileNameIndex(), SrcFilePath); } return Error::success(); } diff --git a/tools/llvm-pdbutil/FormatUtil.cpp b/tools/llvm-pdbutil/FormatUtil.cpp index 1bbe2724f0ab9..02030272dd4da 100644 --- a/tools/llvm-pdbutil/FormatUtil.cpp +++ b/tools/llvm-pdbutil/FormatUtil.cpp @@ -16,6 +16,58 @@ using namespace llvm; using namespace llvm::pdb; +std::string llvm::pdb::truncateStringBack(StringRef S, uint32_t MaxLen) { + if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3) + return S; + + assert(MaxLen >= 3); + uint32_t FinalLen = std::min<size_t>(S.size(), MaxLen - 3); + S = S.take_front(FinalLen); + return std::string(S) + std::string("..."); +} + +std::string llvm::pdb::truncateStringMiddle(StringRef S, uint32_t MaxLen) { + if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3) + return S; + + assert(MaxLen >= 3); + uint32_t FinalLen = std::min<size_t>(S.size(), MaxLen - 3); + StringRef Front = S.take_front(FinalLen / 2); + StringRef Back = S.take_back(Front.size()); + return std::string(Front) + std::string("...") + std::string(Back); +} + +std::string llvm::pdb::truncateStringFront(StringRef S, uint32_t MaxLen) { + if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3) + return S; + + assert(MaxLen >= 3); + S = S.take_back(MaxLen - 3); + return std::string("...") + std::string(S); +} + +std::string llvm::pdb::truncateQuotedNameFront(StringRef Label, StringRef Name, + uint32_t MaxLen) { + uint32_t RequiredExtraChars = Label.size() + 1 + 2; + if (MaxLen == 0 || RequiredExtraChars + Name.size() <= MaxLen) + return formatv("{0} \"{1}\"", Label, Name).str(); + + assert(MaxLen >= RequiredExtraChars); + std::string TN = truncateStringFront(Name, MaxLen - RequiredExtraChars); + return formatv("{0} \"{1}\"", Label, TN).str(); +} + +std::string llvm::pdb::truncateQuotedNameBack(StringRef Label, StringRef Name, + uint32_t MaxLen) { + uint32_t RequiredExtraChars = Label.size() + 1 + 2; + if (MaxLen == 0 || RequiredExtraChars + Name.size() <= MaxLen) + return formatv("{0} \"{1}\"", Label, Name).str(); + + assert(MaxLen >= RequiredExtraChars); + std::string TN = truncateStringBack(Name, MaxLen - RequiredExtraChars); + return formatv("{0} \"{1}\"", Label, TN).str(); +} + std::string llvm::pdb::typesetItemList(ArrayRef<std::string> Opts, uint32_t IndentLevel, uint32_t GroupSize, StringRef Sep) { diff --git a/tools/llvm-pdbutil/FormatUtil.h b/tools/llvm-pdbutil/FormatUtil.h index 3db2dbacc57b6..df32ed9360fba 100644 --- a/tools/llvm-pdbutil/FormatUtil.h +++ b/tools/llvm-pdbutil/FormatUtil.h @@ -22,6 +22,14 @@ namespace llvm { namespace pdb { +std::string truncateStringBack(StringRef S, uint32_t MaxLen); +std::string truncateStringMiddle(StringRef S, uint32_t MaxLen); +std::string truncateStringFront(StringRef S, uint32_t MaxLen); +std::string truncateQuotedNameFront(StringRef Label, StringRef Name, + uint32_t MaxLen); +std::string truncateQuotedNameBack(StringRef Label, StringRef Name, + uint32_t MaxLen); + #define PUSH_MASKED_FLAG(Enum, Mask, TheOpt, Value, Text) \ if (Enum::TheOpt == (Value & Mask)) \ Opts.push_back(Text); @@ -33,7 +41,7 @@ namespace pdb { case Enum::X: \ return Ret; -template <typename T> static std::string formatUnknownEnum(T Value) { +template <typename T> std::string formatUnknownEnum(T Value) { return formatv("unknown ({0})", static_cast<typename std::underlying_type<T>::type>(Value)) .str(); diff --git a/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/tools/llvm-pdbutil/MinimalTypeDumper.cpp index 1af53e35ed111..9621320ea99ad 100644 --- a/tools/llvm-pdbutil/MinimalTypeDumper.cpp +++ b/tools/llvm-pdbutil/MinimalTypeDumper.cpp @@ -299,7 +299,7 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, ClassRecord &Class) { - P.formatLine("class name: `{0}`", Class.Name); + P.format(" `{0}`", Class.Name); if (Class.hasUniqueName()) P.formatLine("unique name: `{0}`", Class.UniqueName); P.formatLine("vtable: {0}, base list: {1}, field list: {2}", @@ -311,7 +311,7 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, UnionRecord &Union) { - P.formatLine("class name: `{0}`", Union.Name); + P.format(" `{0}`", Union.Name); if (Union.hasUniqueName()) P.formatLine("unique name: `{0}`", Union.UniqueName); P.formatLine("field list: {0}", Union.FieldList); @@ -321,7 +321,7 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, } Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) { - P.formatLine("name: `{0}`", Enum.Name); + P.format(" `{0}`", Enum.Name); if (Enum.hasUniqueName()) P.formatLine("unique name: `{0}`", Enum.UniqueName); P.formatLine("field list: {0}, underlying type: {1}", Enum.FieldList, diff --git a/tools/llvm-pdbutil/StreamUtil.cpp b/tools/llvm-pdbutil/StreamUtil.cpp index 81aa256b5002d..4d352004dec30 100644 --- a/tools/llvm-pdbutil/StreamUtil.cpp +++ b/tools/llvm-pdbutil/StreamUtil.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "StreamUtil.h" +#include "FormatUtil.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMapInfo.h" @@ -18,11 +19,12 @@ #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) { +using namespace llvm; +using namespace llvm::pdb; +void llvm::pdb::discoverStreamPurposes( + PDBFile &File, + SmallVectorImpl<std::pair<StreamPurpose, 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(); @@ -52,74 +54,72 @@ void discoverStreamPurposes(PDBFile &File, Purposes.resize(StreamCount); for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { - std::string Value; + std::pair<StreamPurpose, std::string> Value; if (StreamIdx == OldMSFDirectory) - Value = "Old MSF Directory"; + Value = std::make_pair(StreamPurpose::Other, "Old MSF Directory"); else if (StreamIdx == StreamPDB) - Value = "PDB Stream"; + Value = std::make_pair(StreamPurpose::Other, "PDB Stream"); else if (StreamIdx == StreamDBI) - Value = "DBI Stream"; + Value = std::make_pair(StreamPurpose::Other, "DBI Stream"); else if (StreamIdx == StreamTPI) - Value = "TPI Stream"; + Value = std::make_pair(StreamPurpose::Other, "TPI Stream"); else if (StreamIdx == StreamIPI) - Value = "IPI Stream"; + Value = std::make_pair(StreamPurpose::Other, "IPI Stream"); else if (Dbi && StreamIdx == Dbi->getGlobalSymbolStreamIndex()) - Value = "Global Symbol Hash"; + Value = std::make_pair(StreamPurpose::Other, "Global Symbol Hash"); else if (Dbi && StreamIdx == Dbi->getPublicSymbolStreamIndex()) - Value = "Public Symbol Hash"; + Value = std::make_pair(StreamPurpose::Other, "Public Symbol Hash"); else if (Dbi && StreamIdx == Dbi->getSymRecordStreamIndex()) - Value = "Public Symbol Records"; + Value = std::make_pair(StreamPurpose::Other, "Public Symbol Records"); else if (Tpi && StreamIdx == Tpi->getTypeHashStreamIndex()) - Value = "TPI Hash"; + Value = std::make_pair(StreamPurpose::Other, "TPI Hash"); else if (Tpi && StreamIdx == Tpi->getTypeHashStreamAuxIndex()) - Value = "TPI Aux Hash"; + Value = std::make_pair(StreamPurpose::Other, "TPI Aux Hash"); else if (Ipi && StreamIdx == Ipi->getTypeHashStreamIndex()) - Value = "IPI Hash"; + Value = std::make_pair(StreamPurpose::Other, "IPI Hash"); else if (Ipi && StreamIdx == Ipi->getTypeHashStreamAuxIndex()) - Value = "IPI Aux Hash"; + Value = std::make_pair(StreamPurpose::Other, "IPI Aux Hash"); else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Exception)) - Value = "Exception Data"; + Value = std::make_pair(StreamPurpose::Other, "Exception Data"); else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Fixup)) - Value = "Fixup Data"; + Value = std::make_pair(StreamPurpose::Other, "Fixup Data"); else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::FPO)) - Value = "FPO Data"; + Value = std::make_pair(StreamPurpose::Other, "FPO Data"); else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::NewFPO)) - Value = "New FPO Data"; + Value = std::make_pair(StreamPurpose::Other, "New FPO Data"); else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapFromSrc)) - Value = "Omap From Source Data"; + Value = std::make_pair(StreamPurpose::Other, "Omap From Source Data"); else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapToSrc)) - Value = "Omap To Source Data"; + Value = std::make_pair(StreamPurpose::Other, "Omap To Source Data"); else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Pdata)) - Value = "Pdata"; + Value = std::make_pair(StreamPurpose::Other, "Pdata"); else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdr)) - Value = "Section Header Data"; + Value = std::make_pair(StreamPurpose::Other, "Section Header Data"); else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdrOrig)) - Value = "Section Header Original Data"; + Value = + std::make_pair(StreamPurpose::Other, "Section Header Original Data"); else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::TokenRidMap)) - Value = "Token Rid Data"; + Value = std::make_pair(StreamPurpose::Other, "Token Rid Data"); else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Xdata)) - Value = "Xdata"; + Value = std::make_pair(StreamPurpose::Other, "Xdata"); else { auto ModIter = ModStreams.find(StreamIdx); auto NSIter = NamedStreams.find(StreamIdx); if (ModIter != ModStreams.end()) { - Value = "Module \""; - Value += ModIter->second.getModuleName(); - Value += "\""; + Value = std::make_pair(StreamPurpose::ModuleStream, + ModIter->second.getModuleName()); } else if (NSIter != NamedStreams.end()) { - Value = "Named Stream \""; - Value += NSIter->second; - Value += "\""; + Value = std::make_pair(StreamPurpose::NamedStream, NSIter->second); } else { - Value = "???"; + Value = std::make_pair(StreamPurpose::Other, "???"); } } Purposes[StreamIdx] = Value; @@ -135,5 +135,18 @@ void discoverStreamPurposes(PDBFile &File, if (!Info) consumeError(Info.takeError()); } -} + +void llvm::pdb::discoverStreamPurposes(PDBFile &File, + SmallVectorImpl<std::string> &Purposes) { + SmallVector<std::pair<StreamPurpose, std::string>, 24> SP; + discoverStreamPurposes(File, SP); + Purposes.reserve(SP.size()); + for (const auto &P : SP) { + if (P.first == StreamPurpose::NamedStream) + Purposes.push_back(formatv("Named Stream \"{0}\"", P.second)); + else if (P.first == StreamPurpose::ModuleStream) + Purposes.push_back(formatv("Module \"{0}\"", P.second)); + else + Purposes.push_back(P.second); + } } diff --git a/tools/llvm-pdbutil/StreamUtil.h b/tools/llvm-pdbutil/StreamUtil.h index b5c0beba44fed..f49c0a0eceb66 100644 --- a/tools/llvm-pdbutil/StreamUtil.h +++ b/tools/llvm-pdbutil/StreamUtil.h @@ -17,8 +17,13 @@ namespace llvm { namespace pdb { class PDBFile; +enum class StreamPurpose { NamedStream, ModuleStream, Other }; + void discoverStreamPurposes(PDBFile &File, SmallVectorImpl<std::string> &Purposes); +void discoverStreamPurposes( + PDBFile &File, + SmallVectorImpl<std::pair<StreamPurpose, std::string>> &Purposes); } } diff --git a/tools/llvm-pdbutil/llvm-pdbutil.cpp b/tools/llvm-pdbutil/llvm-pdbutil.cpp index ad11ad4980008..6aa08ff3cd872 100644 --- a/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ b/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -284,9 +284,32 @@ cl::opt<bool> NoEnumDefs("no-enum-definitions", } namespace diff { -cl::list<std::string> InputFilenames(cl::Positional, - cl::desc("<first> <second>"), - cl::OneOrMore, cl::sub(DiffSubcommand)); +cl::opt<bool> PrintValueColumns( + "values", cl::init(true), + cl::desc("Print one column for each PDB with the field value"), + cl::Optional, cl::sub(DiffSubcommand)); +cl::opt<bool> + PrintResultColumn("result", cl::init(false), + cl::desc("Print a column with the result status"), + cl::Optional, cl::sub(DiffSubcommand)); + +cl::opt<std::string> LeftRoot( + "left-bin-root", cl::Optional, + cl::desc("Treats the specified path as the root of the tree containing " + "binaries referenced by the left PDB. The root is stripped from " + "embedded paths when doing equality comparisons."), + cl::sub(DiffSubcommand)); +cl::opt<std::string> RightRoot( + "right-bin-root", cl::Optional, + cl::desc("Treats the specified path as the root of the tree containing " + "binaries referenced by the right PDB. The root is stripped from " + "embedded paths when doing equality comparisons"), + cl::sub(DiffSubcommand)); + +cl::opt<std::string> Left(cl::Positional, cl::desc("<left>"), + cl::sub(DiffSubcommand)); +cl::opt<std::string> Right(cl::Positional, cl::desc("<right>"), + cl::sub(DiffSubcommand)); } cl::OptionCategory FileOptions("Module & File Options"); @@ -399,7 +422,7 @@ cl::opt<bool> DumpTypeExtras("type-extras", cl::cat(TypeOptions), cl::sub(DumpSubcommand)); cl::list<uint32_t> DumpTypeIndex( - "type-index", cl::ZeroOrMore, + "type-index", cl::ZeroOrMore, cl::CommaSeparated, cl::desc("only dump types with the specified hexadecimal type index"), cl::cat(TypeOptions), cl::sub(DumpSubcommand)); @@ -415,7 +438,7 @@ cl::opt<bool> DumpIdExtras("id-extras", cl::desc("dump id hashes and index offsets"), cl::cat(TypeOptions), cl::sub(DumpSubcommand)); cl::list<uint32_t> DumpIdIndex( - "id-index", cl::ZeroOrMore, + "id-index", cl::ZeroOrMore, cl::CommaSeparated, cl::desc("only dump ids with the specified hexadecimal type index"), cl::cat(TypeOptions), cl::sub(DumpSubcommand)); @@ -1079,6 +1102,11 @@ int main(int argc_, const char *argv_[]) { if (opts::pdb2yaml::DumpModules) opts::pdb2yaml::DbiStream = true; } + if (opts::DiffSubcommand) { + if (!opts::diff::PrintResultColumn && !opts::diff::PrintValueColumns) { + llvm::errs() << "WARNING: No diff columns specified\n"; + } + } llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded); @@ -1137,11 +1165,7 @@ int main(int argc_, const char *argv_[]) { std::for_each(opts::bytes::InputFilenames.begin(), opts::bytes::InputFilenames.end(), dumpBytes); } 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]); + diff(opts::diff::Left, opts::diff::Right); } else if (opts::MergeSubcommand) { if (opts::merge::InputFilenames.size() < 2) { errs() << "merge subcommand requires at least 2 input files.\n"; diff --git a/tools/llvm-pdbutil/llvm-pdbutil.h b/tools/llvm-pdbutil/llvm-pdbutil.h index 9ee5866bbeffc..4e92e639a1278 100644 --- a/tools/llvm-pdbutil/llvm-pdbutil.h +++ b/tools/llvm-pdbutil/llvm-pdbutil.h @@ -168,6 +168,13 @@ extern llvm::cl::opt<bool> DumpModuleFiles; extern llvm::cl::list<ModuleSubsection> DumpModuleSubsections; extern llvm::cl::opt<bool> DumpModuleSyms; } // namespace pdb2yaml + +namespace diff { +extern llvm::cl::opt<bool> PrintValueColumns; +extern llvm::cl::opt<bool> PrintResultColumn; +extern llvm::cl::opt<std::string> LeftRoot; +extern llvm::cl::opt<std::string> RightRoot; +} // namespace diff } #endif diff --git a/tools/llvm-profdata/llvm-profdata.cpp b/tools/llvm-profdata/llvm-profdata.cpp index e9bc2de82bdf0..eee242107dabe 100644 --- a/tools/llvm-profdata/llvm-profdata.cpp +++ b/tools/llvm-profdata/llvm-profdata.cpp @@ -159,14 +159,20 @@ static void loadInput(const WeightedFile &Input, WriterContext *WC) { for (auto &I : *Reader) { const StringRef FuncName = I.Name; - if (Error E = WC->Writer.addRecord(std::move(I), Input.Weight)) { + bool Reported = false; + WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) { + if (Reported) { + consumeError(std::move(E)); + return; + } + Reported = true; // Only show hint the first time an error occurs. instrprof_error IPE = InstrProfError::take(std::move(E)); std::unique_lock<std::mutex> ErrGuard{WC->ErrLock}; bool firstTime = WC->WriterErrorCodes.insert(IPE).second; handleMergeWriterError(make_error<InstrProfError>(IPE), Input.Filename, FuncName, firstTime); - } + }); } if (Reader->hasError()) WC->Err = Reader->getError(); @@ -174,8 +180,15 @@ static void loadInput(const WeightedFile &Input, WriterContext *WC) { /// Merge the \p Src writer context into \p Dst. static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { - if (Error E = Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer))) + bool Reported = false; + Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) { + if (Reported) { + consumeError(std::move(E)); + return; + } + Reported = true; Dst->Err = std::move(E); + }); } static void mergeInstrProfile(const WeightedFileVector &Inputs, @@ -499,8 +512,8 @@ static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK, } static int showInstrProfile(const std::string &Filename, bool ShowCounts, - bool ShowIndirectCallTargets, bool ShowMemOPSizes, - bool ShowDetailedSummary, + uint32_t TopN, bool ShowIndirectCallTargets, + bool ShowMemOPSizes, bool ShowDetailedSummary, std::vector<uint32_t> DetailedSummaryCutoffs, bool ShowAllFunctions, const std::string &ShowFunction, bool TextFormat, @@ -519,6 +532,17 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, size_t ShownFunctions = 0; int NumVPKind = IPVK_Last - IPVK_First + 1; std::vector<ValueSitesStats> VPStats(NumVPKind); + + auto MinCmp = [](const std::pair<std::string, uint64_t> &v1, + const std::pair<std::string, uint64_t> &v2) { + return v1.second > v2.second; + }; + + std::priority_queue<std::pair<std::string, uint64_t>, + std::vector<std::pair<std::string, uint64_t>>, + decltype(MinCmp)> + HottestFuncs(MinCmp); + for (const auto &Func : *Reader) { bool Show = ShowAllFunctions || (!ShowFunction.empty() && @@ -528,13 +552,28 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, if (doTextFormatDump) { InstrProfSymtab &Symtab = Reader->getSymtab(); - InstrProfWriter::writeRecordInText(Func, Symtab, OS); + InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab, + OS); continue; } assert(Func.Counts.size() > 0 && "function missing entry counter"); Builder.addRecord(Func); + if (TopN) { + uint64_t FuncMax = 0; + for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) + FuncMax = std::max(FuncMax, Func.Counts[I]); + + if (HottestFuncs.size() == TopN) { + if (HottestFuncs.top().second < FuncMax) { + HottestFuncs.pop(); + HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); + } + } else + HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); + } + if (Show) { if (!ShownFunctions) @@ -592,6 +631,18 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n"; OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n"; + if (TopN) { + std::vector<std::pair<std::string, uint64_t>> SortedHottestFuncs; + while (!HottestFuncs.empty()) { + SortedHottestFuncs.emplace_back(HottestFuncs.top()); + HottestFuncs.pop(); + } + OS << "Top " << TopN + << " functions with the largest internal block counts: \n"; + for (auto &hotfunc : llvm::reverse(SortedHottestFuncs)) + OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n"; + } + if (ShownFunctions && ShowIndirectCallTargets) { OS << "Statistics for indirect call sites profile:\n"; showValueSitesStats(OS, IPVK_IndirectCallTarget, @@ -675,6 +726,9 @@ static int show_main(int argc, const char *argv[]) { cl::desc("Profile kind:"), cl::init(instr), cl::values(clEnumVal(instr, "Instrumentation profile (default)"), clEnumVal(sample, "Sample profile"))); + cl::opt<uint32_t> TopNFunctions( + "topn", cl::init(0), + cl::desc("Show the list of functions with the largest internal counts")); cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); @@ -692,10 +746,10 @@ static int show_main(int argc, const char *argv[]) { std::vector<uint32_t> Cutoffs(DetailedSummaryCutoffs.begin(), DetailedSummaryCutoffs.end()); if (ProfileKind == instr) - return showInstrProfile(Filename, ShowCounts, ShowIndirectCallTargets, - ShowMemOPSizes, ShowDetailedSummary, - DetailedSummaryCutoffs, ShowAllFunctions, - ShowFunction, TextFormat, OS); + return showInstrProfile(Filename, ShowCounts, TopNFunctions, + ShowIndirectCallTargets, ShowMemOPSizes, + ShowDetailedSummary, DetailedSummaryCutoffs, + ShowAllFunctions, ShowFunction, TextFormat, OS); else return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, ShowFunction, OS); diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp index e5ff3e4186dec..9fb3267e2f9d3 100644 --- a/tools/llvm-readobj/COFFDumper.cpp +++ b/tools/llvm-readobj/COFFDumper.cpp @@ -1637,7 +1637,11 @@ static StringRef getBaseRelocTypeName(uint8_t Type) { case COFF::IMAGE_REL_BASED_HIGHADJ: return "HIGHADJ"; case COFF::IMAGE_REL_BASED_ARM_MOV32T: return "ARM_MOV32(T)"; case COFF::IMAGE_REL_BASED_DIR64: return "DIR64"; - default: return "unknown (" + llvm::utostr(Type) + ")"; + default: { + static std::string Result; + Result = "unknown (" + llvm::utostr(Type) + ")"; + return Result; + } } } diff --git a/tools/llvm-readobj/WasmDumper.cpp b/tools/llvm-readobj/WasmDumper.cpp index 14603f8a2b094..266226d59ee87 100644 --- a/tools/llvm-readobj/WasmDumper.cpp +++ b/tools/llvm-readobj/WasmDumper.cpp @@ -153,6 +153,12 @@ void WasmDumper::printSections() { switch (WasmSec.Type) { case wasm::WASM_SEC_CUSTOM: W.printString("Name", WasmSec.Name); + if (WasmSec.Name == "linking") { + const wasm::WasmLinkingData &LinkingData = Obj->linkingData(); + W.printNumber("DataSize", LinkingData.DataSize); + if (LinkingData.DataAlignment) + W.printNumber("DataAlignment", LinkingData.DataAlignment); + } break; case wasm::WASM_SEC_MEMORY: ListScope Group(W, "Memories"); diff --git a/tools/llvm-shlib/CMakeLists.txt b/tools/llvm-shlib/CMakeLists.txt index 3ebede00cc434..907345a94023b 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) OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "DragonFly")) # FIXME: It should be "GNU ld for elf" +if(("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") OR (MINGW) OR (HAIKU) OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "DragonFly")) # FIXME: It should be "GNU ld for elf" configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/simple_version_script.map.in ${LLVM_LIBRARY_DIR}/tools/llvm-shlib/simple_version_script.map) diff --git a/tools/llvm-stress/llvm-stress.cpp b/tools/llvm-stress/llvm-stress.cpp index 3cf8b37bc2e29..3945da7020b0b 100644 --- a/tools/llvm-stress/llvm-stress.cpp +++ b/tools/llvm-stress/llvm-stress.cpp @@ -385,10 +385,10 @@ struct ConstModifier: public Modifier { if (Ty->isVectorTy()) { switch (getRandom() % 2) { - case 0: if (Ty->getScalarType()->isIntegerTy()) + case 0: if (Ty->isIntOrIntVectorTy()) return PT->push_back(ConstantVector::getAllOnesValue(Ty)); break; - case 1: if (Ty->getScalarType()->isIntegerTy()) + case 1: if (Ty->isIntOrIntVectorTy()) return PT->push_back(ConstantVector::getNullValue(Ty)); } } @@ -531,8 +531,7 @@ struct CastModifier: public Modifier { } // Both types are integers: - if (VTy->getScalarType()->isIntegerTy() && - DestTy->getScalarType()->isIntegerTy()) { + if (VTy->isIntOrIntVectorTy() && DestTy->isIntOrIntVectorTy()) { if (VSize > DestSize) { return PT->push_back( new TruncInst(V, DestTy, "Tr", BB->getTerminator())); @@ -546,8 +545,7 @@ struct CastModifier: public Modifier { } // Fp to int. - if (VTy->getScalarType()->isFloatingPointTy() && - DestTy->getScalarType()->isIntegerTy()) { + if (VTy->isFPOrFPVectorTy() && DestTy->isIntOrIntVectorTy()) { if (getRandom() & 1) return PT->push_back( new FPToSIInst(V, DestTy, "FC", BB->getTerminator())); @@ -555,8 +553,7 @@ struct CastModifier: public Modifier { } // Int to fp. - if (VTy->getScalarType()->isIntegerTy() && - DestTy->getScalarType()->isFloatingPointTy()) { + if (VTy->isIntOrIntVectorTy() && DestTy->isFPOrFPVectorTy()) { if (getRandom() & 1) return PT->push_back( new SIToFPInst(V, DestTy, "FC", BB->getTerminator())); @@ -565,8 +562,7 @@ struct CastModifier: public Modifier { } // Both floats. - if (VTy->getScalarType()->isFloatingPointTy() && - DestTy->getScalarType()->isFloatingPointTy()) { + if (VTy->isFPOrFPVectorTy() && DestTy->isFPOrFPVectorTy()) { if (VSize > DestSize) { return PT->push_back( new FPTruncInst(V, DestTy, "Tr", BB->getTerminator())); diff --git a/tools/obj2yaml/wasm2yaml.cpp b/tools/obj2yaml/wasm2yaml.cpp index 1df6afcf3c46d..a1da4b6a748c7 100644 --- a/tools/obj2yaml/wasm2yaml.cpp +++ b/tools/obj2yaml/wasm2yaml.cpp @@ -236,9 +236,10 @@ ErrorOr<WasmYAML::Object *> WasmDumper::dump() { 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); + Seg.SectionOffset = Segment.SectionOffset; + Seg.MemoryIndex = Segment.Data.MemoryIndex; + Seg.Offset = Segment.Data.Offset; + Seg.Content = yaml::BinaryRef(Segment.Data.Content); DataSec->Segments.push_back(Seg); } S = std::move(DataSec); diff --git a/tools/opt-viewer/CMakeLists.txt b/tools/opt-viewer/CMakeLists.txt new file mode 100644 index 0000000000000..19b6069330820 --- /dev/null +++ b/tools/opt-viewer/CMakeLists.txt @@ -0,0 +1,13 @@ +set (files + "opt-diff.py" + "opt-stats.py" + "opt-viewer.py" + "optpmap.py" + "optrecord.py" + "style.css") + +foreach (file ${files}) + install(PROGRAMS ${file} + DESTINATION share/opt-viewer + COMPONENT opt-viewer) +endforeach (file) diff --git a/tools/opt-viewer/opt-diff.py b/tools/opt-viewer/opt-diff.py new file mode 100755 index 0000000000000..9e921f8488d36 --- /dev/null +++ b/tools/opt-viewer/opt-diff.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python2.7 + +from __future__ import print_function + +desc = '''Generate the difference of two YAML files into a new YAML file (works on +pair of directories too). A new attribute 'Added' is set to True or False +depending whether the entry is added or removed from the first input to the +next. + +The tools requires PyYAML.''' + +import yaml +# Try to use the C parser. +try: + from yaml import CLoader as Loader +except ImportError: + from yaml import Loader + +import optrecord +import argparse +from collections import defaultdict +from multiprocessing import cpu_count, Pool +import os, os.path +import fnmatch + +def find_files(dir_or_file): + if os.path.isfile(dir_or_file): + return [dir_or_file] + + all = [] + for dir, subdirs, files in os.walk(dir_or_file): + for file in files: + if fnmatch.fnmatch(file, "*.opt.yaml"): + all.append( os.path.join(dir, file)) + return all + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=desc) + parser.add_argument('yaml_dir_or_file_1') + parser.add_argument('yaml_dir_or_file_2') + parser.add_argument( + '--jobs', + '-j', + default=cpu_count(), + type=int, + help='Max job count (defaults to %(default)s, the current CPU count)') + parser.add_argument( + '--no-progress-indicator', + '-n', + action='store_true', + default=False, + help='Do not display any indicator of how many YAML files were read.') + parser.add_argument('--output', '-o', default='diff.opt.yaml') + args = parser.parse_args() + + files1 = find_files(args.yaml_dir_or_file_1) + files2 = find_files(args.yaml_dir_or_file_2) + + print_progress = not args.no_progress_indicator + all_remarks1, _, _ = optrecord.gather_results(files1, args.jobs, print_progress) + all_remarks2, _, _ = optrecord.gather_results(files2, args.jobs, print_progress) + + added = set(all_remarks2.values()) - set(all_remarks1.values()) + removed = set(all_remarks1.values()) - set(all_remarks2.values()) + + for r in added: + r.Added = True + for r in removed: + r.Added = False + with open(args.output, 'w') as stream: + yaml.dump_all(added | removed, stream) diff --git a/tools/opt-viewer/opt-stats.py b/tools/opt-viewer/opt-stats.py new file mode 100755 index 0000000000000..a7e598fdfd026 --- /dev/null +++ b/tools/opt-viewer/opt-stats.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python2.7 + +from __future__ import print_function + +desc = '''Generate statistics about optimization records from the YAML files +generated with -fsave-optimization-record and -fdiagnostics-show-hotness. + +The tools requires PyYAML and Pygments Python packages.''' + +import optrecord +import argparse +import operator +from collections import defaultdict +from multiprocessing import cpu_count, Pool + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=desc) + parser.add_argument('yaml_files', nargs='+') + parser.add_argument( + '--jobs', + '-j', + default=cpu_count(), + type=int, + help='Max job count (defaults to %(default)s, the current CPU count)') + parser.add_argument( + '--no-progress-indicator', + '-n', + action='store_true', + default=False, + help='Do not display any indicator of how many YAML files were read.') + args = parser.parse_args() + + print_progress = not args.no_progress_indicator + all_remarks, file_remarks, _ = optrecord.gather_results( + args.yaml_files, args.jobs, print_progress) + if print_progress: + print('\n') + + bypass = defaultdict(int) + byname = defaultdict(int) + for r in optrecord.itervalues(all_remarks): + bypass[r.Pass] += 1 + byname[r.Pass + "/" + r.Name] += 1 + + total = len(all_remarks) + print("{:24s} {:10d}\n".format("Total number of remarks", total)) + + print("Top 10 remarks by pass:") + for (passname, count) in sorted(bypass.items(), key=operator.itemgetter(1), + reverse=True)[:10]: + print(" {:30s} {:2.0f}%". format(passname, count * 100. / total)) + + print("\nTop 10 remarks:") + for (name, count) in sorted(byname.items(), key=operator.itemgetter(1), + reverse=True)[:10]: + print(" {:30s} {:2.0f}%". format(name, count * 100. / total)) diff --git a/tools/opt-viewer/opt-viewer.py b/tools/opt-viewer/opt-viewer.py new file mode 100755 index 0000000000000..e6dd6a0286fe7 --- /dev/null +++ b/tools/opt-viewer/opt-viewer.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python2.7 + +from __future__ import print_function + +import argparse +import cgi +import errno +import functools +from multiprocessing import cpu_count +import os.path +import re +import shutil + +from pygments import highlight +from pygments.lexers.c_cpp import CppLexer +from pygments.formatters import HtmlFormatter + +import optpmap +import optrecord + + +desc = '''Generate HTML output to visualize optimization records from the YAML files +generated with -fsave-optimization-record and -fdiagnostics-show-hotness. + +The tools requires PyYAML and Pygments Python packages.''' + + +# This allows passing the global context to the child processes. +class Context: + def __init__(self, caller_loc = dict()): + # Map function names to their source location for function where inlining happened + self.caller_loc = caller_loc + +context = Context() + +class SourceFileRenderer: + def __init__(self, source_dir, output_dir, filename): + existing_filename = None + if os.path.exists(filename): + existing_filename = filename + else: + fn = os.path.join(source_dir, filename) + if os.path.exists(fn): + existing_filename = fn + + self.stream = open(os.path.join(output_dir, optrecord.html_file_name(filename)), 'w') + if existing_filename: + self.source_stream = open(existing_filename) + else: + self.source_stream = None + print(''' +<html> +<h1>Unable to locate file {}</h1> +</html> + '''.format(filename), file=self.stream) + + self.html_formatter = HtmlFormatter(encoding='utf-8') + self.cpp_lexer = CppLexer(stripnl=False) + + def render_source_lines(self, stream, line_remarks): + file_text = stream.read() + html_highlighted = highlight(file_text, self.cpp_lexer, self.html_formatter) + + # Take off the header and footer, these must be + # reapplied line-wise, within the page structure + html_highlighted = html_highlighted.replace('<div class="highlight"><pre>', '') + html_highlighted = html_highlighted.replace('</pre></div>', '') + + for (linenum, html_line) in enumerate(html_highlighted.split('\n'), start=1): + print(''' +<tr> +<td><a name=\"L{linenum}\">{linenum}</a></td> +<td></td> +<td></td> +<td><div class="highlight"><pre>{html_line}</pre></div></td> +</tr>'''.format(**locals()), file=self.stream) + + for remark in line_remarks.get(linenum, []): + self.render_inline_remarks(remark, html_line) + + def render_inline_remarks(self, r, line): + inlining_context = r.DemangledFunctionName + dl = context.caller_loc.get(r.Function) + if dl: + link = optrecord.make_link(dl['File'], dl['Line'] - 2) + inlining_context = "<a href={link}>{r.DemangledFunctionName}</a>".format(**locals()) + + # Column is the number of characters *including* tabs, keep those and + # replace everything else with spaces. + indent = line[:max(r.Column, 1) - 1] + indent = re.sub('\S', ' ', indent) + + print(''' +<tr> +<td></td> +<td>{r.RelativeHotness}</td> +<td class=\"column-entry-{r.color}\">{r.PassWithDiffPrefix}</td> +<td><pre style="display:inline">{indent}</pre><span class=\"column-entry-yellow\"> {r.message} </span></td> +<td class=\"column-entry-yellow\">{inlining_context}</td> +</tr>'''.format(**locals()), file=self.stream) + + def render(self, line_remarks): + if not self.source_stream: + return + + print(''' +<html> +<head> +<link rel='stylesheet' type='text/css' href='style.css'> +</head> +<body> +<div class="centered"> +<table> +<tr> +<td>Line</td> +<td>Hotness</td> +<td>Optimization</td> +<td>Source</td> +<td>Inline Context</td> +</tr>''', file=self.stream) + self.render_source_lines(self.source_stream, line_remarks) + + print(''' +</table> +</body> +</html>''', file=self.stream) + + +class IndexRenderer: + def __init__(self, output_dir): + self.stream = open(os.path.join(output_dir, 'index.html'), 'w') + + def render_entry(self, r, odd): + escaped_name = cgi.escape(r.DemangledFunctionName) + print(''' +<tr> +<td class=\"column-entry-{odd}\"><a href={r.Link}>{r.DebugLocString}</a></td> +<td class=\"column-entry-{odd}\">{r.RelativeHotness}</td> +<td class=\"column-entry-{odd}\">{escaped_name}</td> +<td class=\"column-entry-{r.color}\">{r.PassWithDiffPrefix}</td> +</tr>'''.format(**locals()), file=self.stream) + + def render(self, all_remarks): + print(''' +<html> +<head> +<link rel='stylesheet' type='text/css' href='style.css'> +</head> +<body> +<div class="centered"> +<table> +<tr> +<td>Source Location</td> +<td>Hotness</td> +<td>Function</td> +<td>Pass</td> +</tr>''', file=self.stream) + for i, remark in enumerate(all_remarks): + self.render_entry(remark, i % 2) + print(''' +</table> +</body> +</html>''', file=self.stream) + + +def _render_file(source_dir, output_dir, ctx, entry): + global context + context = ctx + filename, remarks = entry + SourceFileRenderer(source_dir, output_dir, filename).render(remarks) + + +def map_remarks(all_remarks): + # Set up a map between function names and their source location for + # function where inlining happened + for remark in optrecord.itervalues(all_remarks): + if isinstance(remark, optrecord.Passed) and remark.Pass == "inline" and remark.Name == "Inlined": + for arg in remark.Args: + caller = arg.get('Caller') + if caller: + try: + context.caller_loc[caller] = arg['DebugLoc'] + except KeyError: + pass + + +def generate_report(all_remarks, + file_remarks, + source_dir, + output_dir, + should_display_hotness, + num_jobs, + should_print_progress): + try: + os.makedirs(output_dir) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(output_dir): + pass + else: + raise + + _render_file_bound = functools.partial(_render_file, source_dir, output_dir, context) + if should_print_progress: + print('Rendering HTML files...') + optpmap.pmap(_render_file_bound, + file_remarks.items(), + num_jobs, + should_print_progress) + + if should_display_hotness: + sorted_remarks = sorted(optrecord.itervalues(all_remarks), key=lambda r: (r.Hotness, r.File, r.Line, r.Column, r.PassWithDiffPrefix, r.yaml_tag, r.Function), reverse=True) + else: + sorted_remarks = sorted(optrecord.itervalues(all_remarks), key=lambda r: (r.File, r.Line, r.Column, r.PassWithDiffPrefix, r.yaml_tag, r.Function)) + IndexRenderer(args.output_dir).render(sorted_remarks) + + shutil.copy(os.path.join(os.path.dirname(os.path.realpath(__file__)), + "style.css"), output_dir) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=desc) + parser.add_argument('yaml_files', nargs='+') + parser.add_argument( + '--output-dir', + '-o', + default='html', + help='Path to a directory where generated HTML files will be output. ' + 'If the directory does not already exist, it will be created. ' + '"%(default)s" by default.') + parser.add_argument( + '--jobs', + '-j', + default=cpu_count(), + type=int, + help='Max job count (defaults to %(default)s, the current CPU count)') + parser.add_argument( + '-source-dir', + '-s', + default='', + help='set source directory') + parser.add_argument( + '--no-progress-indicator', + '-n', + action='store_true', + default=False, + help='Do not display any indicator of how many YAML files were read ' + 'or rendered into HTML.') + args = parser.parse_args() + + print_progress = not args.no_progress_indicator + all_remarks, file_remarks, should_display_hotness = \ + optrecord.gather_results(args.yaml_files, args.jobs, print_progress) + + map_remarks(all_remarks) + + generate_report(all_remarks, + file_remarks, + args.source_dir, + args.output_dir, + should_display_hotness, + args.jobs, + print_progress) diff --git a/tools/opt-viewer/optpmap.py b/tools/opt-viewer/optpmap.py new file mode 100644 index 0000000000000..01e848e03976d --- /dev/null +++ b/tools/opt-viewer/optpmap.py @@ -0,0 +1,53 @@ +import sys +import multiprocessing + + +_current = None +_total = None + + +def _init(current, total): + global _current + global _total + _current = current + _total = total + + +def _wrapped_func(func_and_args): + func, argument, should_print_progress = func_and_args + + if should_print_progress: + with _current.get_lock(): + _current.value += 1 + sys.stdout.write('\r\t{} of {}'.format(_current.value, _total.value)) + + return func(argument) + + +def pmap(func, iterable, processes, should_print_progress, *args, **kwargs): + """ + A parallel map function that reports on its progress. + + Applies `func` to every item of `iterable` and return a list of the + results. If `processes` is greater than one, a process pool is used to run + the functions in parallel. `should_print_progress` is a boolean value that + indicates whether a string 'N of M' should be printed to indicate how many + of the functions have finished being run. + """ + global _current + global _total + _current = multiprocessing.Value('i', 0) + _total = multiprocessing.Value('i', len(iterable)) + + func_and_args = [(func, arg, should_print_progress,) for arg in iterable] + if processes <= 1: + result = map(_wrapped_func, func_and_args, *args, **kwargs) + else: + pool = multiprocessing.Pool(initializer=_init, + initargs=(_current, _total,), + processes=processes) + result = pool.map(_wrapped_func, func_and_args, *args, **kwargs) + + if should_print_progress: + sys.stdout.write('\r') + return result diff --git a/tools/opt-viewer/optrecord.py b/tools/opt-viewer/optrecord.py new file mode 100644 index 0000000000000..61ed9626cffad --- /dev/null +++ b/tools/opt-viewer/optrecord.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python2.7 + +from __future__ import print_function + +import yaml +# Try to use the C parser. +try: + from yaml import CLoader as Loader +except ImportError: + print("For faster parsing, you may want to install libYAML for PyYAML") + from yaml import Loader + +import cgi +from collections import defaultdict +import functools +from multiprocessing import Lock +import subprocess + +import optpmap + + +p = subprocess.Popen(['c++filt', '-n'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) +p_lock = Lock() + + +try: + dict.iteritems +except AttributeError: + # Python 3 + def itervalues(d): + return iter(d.values()) + def iteritems(d): + return iter(d.items()) +else: + # Python 2 + def itervalues(d): + return d.itervalues() + def iteritems(d): + return d.iteritems() + + +def demangle(name): + with p_lock: + p.stdin.write((name + '\n').encode('utf-8')) + p.stdin.flush() + return p.stdout.readline().rstrip().decode('utf-8') + + +def html_file_name(filename): + return filename.replace('/', '_') + ".html" + + +def make_link(File, Line): + return "\"{}#L{}\"".format(html_file_name(File), Line) + + +class Remark(yaml.YAMLObject): + # Work-around for http://pyyaml.org/ticket/154. + yaml_loader = Loader + + def initmissing(self): + if not hasattr(self, 'Hotness'): + self.Hotness = 0 + if not hasattr(self, 'Args'): + self.Args = [] + + @property + def File(self): + return self.DebugLoc['File'] + + @property + def Line(self): + return int(self.DebugLoc['Line']) + + @property + def Column(self): + return self.DebugLoc['Column'] + + @property + def DebugLocString(self): + return "{}:{}:{}".format(self.File, self.Line, self.Column) + + @property + def DemangledFunctionName(self): + return demangle(self.Function) + + @property + def Link(self): + return make_link(self.File, self.Line) + + def getArgString(self, mapping): + mapping = mapping.copy() + dl = mapping.get('DebugLoc') + if dl: + del mapping['DebugLoc'] + + assert(len(mapping) == 1) + (key, value) = mapping.items()[0] + + if key == 'Caller' or key == 'Callee': + value = cgi.escape(demangle(value)) + + if dl and key != 'Caller': + return "<a href={}>{}</a>".format( + make_link(dl['File'], dl['Line']), value) + else: + return value + + def getDiffPrefix(self): + if hasattr(self, 'Added'): + if self.Added: + return '+' + else: + return '-' + return '' + + @property + def PassWithDiffPrefix(self): + return self.getDiffPrefix() + self.Pass + + @property + def message(self): + # Args is a list of mappings (dictionaries) + values = [self.getArgString(mapping) for mapping in self.Args] + return "".join(values) + + @property + def RelativeHotness(self): + if self.max_hotness: + return "{}%".format(int(round(self.Hotness * 100 / self.max_hotness))) + else: + return '' + + @property + def key(self): + k = (self.__class__, self.PassWithDiffPrefix, self.Name, self.File, self.Line, self.Column, self.Function) + for arg in self.Args: + for (key, value) in iteritems(arg): + if type(value) is dict: + value = tuple(value.items()) + k += (key, value) + return k + + def __hash__(self): + return hash(self.key) + + def __eq__(self, other): + return self.key == other.key + + def __repr__(self): + return str(self.key) + + +class Analysis(Remark): + yaml_tag = '!Analysis' + + @property + def color(self): + return "white" + + +class AnalysisFPCommute(Analysis): + yaml_tag = '!AnalysisFPCommute' + + +class AnalysisAliasing(Analysis): + yaml_tag = '!AnalysisAliasing' + + +class Passed(Remark): + yaml_tag = '!Passed' + + @property + def color(self): + return "green" + + +class Missed(Remark): + yaml_tag = '!Missed' + + @property + def color(self): + return "red" + + +def get_remarks(input_file): + max_hotness = 0 + all_remarks = dict() + file_remarks = defaultdict(functools.partial(defaultdict, list)) + + with open(input_file) as f: + docs = yaml.load_all(f, Loader=Loader) + for remark in docs: + remark.initmissing() + # Avoid remarks withoug debug location or if they are duplicated + if not hasattr(remark, 'DebugLoc') or remark.key in all_remarks: + continue + all_remarks[remark.key] = remark + + file_remarks[remark.File][remark.Line].append(remark) + + # If we're reading a back a diff yaml file, max_hotness is already + # captured which may actually be less than the max hotness found + # in the file. + if hasattr(remark, 'max_hotness'): + max_hotness = remark.max_hotness + max_hotness = max(max_hotness, remark.Hotness) + + return max_hotness, all_remarks, file_remarks + + +def gather_results(filenames, num_jobs, should_print_progress): + if should_print_progress: + print('Reading YAML files...') + remarks = optpmap.pmap( + get_remarks, filenames, num_jobs, should_print_progress) + max_hotness = max(entry[0] for entry in remarks) + + def merge_file_remarks(file_remarks_job, all_remarks, merged): + for filename, d in iteritems(file_remarks_job): + for line, remarks in iteritems(d): + for remark in remarks: + # Bring max_hotness into the remarks so that + # RelativeHotness does not depend on an external global. + remark.max_hotness = max_hotness + if remark.key not in all_remarks: + merged[filename][line].append(remark) + + all_remarks = dict() + file_remarks = defaultdict(functools.partial(defaultdict, list)) + for _, all_remarks_job, file_remarks_job in remarks: + merge_file_remarks(file_remarks_job, all_remarks, file_remarks) + all_remarks.update(all_remarks_job) + + return all_remarks, file_remarks, max_hotness != 0 diff --git a/tools/opt-viewer/style.css b/tools/opt-viewer/style.css new file mode 100644 index 0000000000000..595c3e46847dd --- /dev/null +++ b/tools/opt-viewer/style.css @@ -0,0 +1,198 @@ +.red { + background-color: #ffd0d0; +} +.cyan { + background-color: cyan; +} +body { + font-family: -apple-system, sans-serif; +} +pre { + margin-top: 0px !important; + margin-bottom: 0px !important; +} +.source-name-title { + padding: 5px 10px; + border-bottom: 1px solid #dbdbdb; + background-color: #eee; + line-height: 35px; +} +.centered { + display: table; + margin-left: left; + margin-right: auto; + border: 1px solid #dbdbdb; + border-radius: 3px; +} +.expansion-view { + background-color: rgba(0, 0, 0, 0); + margin-left: 0px; + margin-top: 5px; + margin-right: 5px; + margin-bottom: 5px; + border: 1px solid #dbdbdb; + border-radius: 3px; +} +table { + border-collapse: collapse; +} +.light-row { + background: #ffffff; + border: 1px solid #dbdbdb; +} +.column-entry { + text-align: right; +} +.column-entry-left { + text-align: left; +} +.column-entry-white { + text-align: right; + background-color: #ffffff; +} +.column-entry-red { + text-align: right; + background-color: #ffd0d0; +} +.column-entry-green { + text-align: right; + background-color: #d0ffd0; +} +.column-entry-yellow { + text-align: left; + background-color: #ffe1a6; +} +.column-entry-0 { + background-color: #ffffff; +} +.column-entry-1 { + background-color: #eeeeee; +} +.line-number { + text-align: right; + color: #aaa; +} +.covered-line { + text-align: right; + color: #0080ff; +} +.uncovered-line { + text-align: right; + color: #ff3300; +} +.tooltip { + position: relative; + display: inline; + background-color: #b3e6ff; + text-decoration: none; +} +.tooltip span.tooltip-content { + position: absolute; + width: 100px; + margin-left: -50px; + color: #FFFFFF; + background: #000000; + height: 30px; + line-height: 30px; + text-align: center; + visibility: hidden; + border-radius: 6px; +} +.tooltip span.tooltip-content:after { + content: ''; + position: absolute; + top: 100%; + left: 50%; + margin-left: -8px; + width: 0; height: 0; + border-top: 8px solid #000000; + border-right: 8px solid transparent; + border-left: 8px solid transparent; +} +:hover.tooltip span.tooltip-content { + visibility: visible; + opacity: 0.8; + bottom: 30px; + left: 50%; + z-index: 999; +} +th, td { + vertical-align: top; + padding: 2px 5px; + border-collapse: collapse; + border-right: solid 1px #eee; + border-left: solid 1px #eee; +} +td:first-child { + border-left: none; +} +td:last-child { + border-right: none; +} + +/* Generated with pygmentize -S colorful -f html >> style.css */ + +.hll { background-color: #ffffcc } +.c { color: #888888 } /* Comment */ +.err { color: #FF0000; background-color: #FFAAAA } /* Error */ +.k { color: #008800; font-weight: bold } /* Keyword */ +.o { color: #333333 } /* Operator */ +.ch { color: #888888 } /* Comment.Hashbang */ +.cm { color: #888888 } /* Comment.Multiline */ +.cp { color: #557799 } /* Comment.Preproc */ +.cpf { color: #888888 } /* Comment.PreprocFile */ +.c1 { color: #888888 } /* Comment.Single */ +.cs { color: #cc0000; font-weight: bold } /* Comment.Special */ +.gd { color: #A00000 } /* Generic.Deleted */ +.ge { font-style: italic } /* Generic.Emph */ +.gr { color: #FF0000 } /* Generic.Error */ +.gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.gi { color: #00A000 } /* Generic.Inserted */ +.go { color: #888888 } /* Generic.Output */ +.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ +.gs { font-weight: bold } /* Generic.Strong */ +.gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.gt { color: #0044DD } /* Generic.Traceback */ +.kc { color: #008800; font-weight: bold } /* Keyword.Constant */ +.kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ +.kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ +.kp { color: #003388; font-weight: bold } /* Keyword.Pseudo */ +.kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ +.kt { color: #333399; font-weight: bold } /* Keyword.Type */ +.m { color: #6600EE; font-weight: bold } /* Literal.Number */ +.s { background-color: #fff0f0 } /* Literal.String */ +.na { color: #0000CC } /* Name.Attribute */ +.nb { color: #007020 } /* Name.Builtin */ +.nc { color: #BB0066; font-weight: bold } /* Name.Class */ +.no { color: #003366; font-weight: bold } /* Name.Constant */ +.nd { color: #555555; font-weight: bold } /* Name.Decorator */ +.ni { color: #880000; font-weight: bold } /* Name.Entity */ +.ne { color: #FF0000; font-weight: bold } /* Name.Exception */ +.nf { color: #0066BB; font-weight: bold } /* Name.Function */ +.nl { color: #997700; font-weight: bold } /* Name.Label */ +.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ +.nt { color: #007700 } /* Name.Tag */ +.nv { color: #996633 } /* Name.Variable */ +.ow { color: #000000; font-weight: bold } /* Operator.Word */ +.w { color: #bbbbbb } /* Text.Whitespace */ +.mb { color: #6600EE; font-weight: bold } /* Literal.Number.Bin */ +.mf { color: #6600EE; font-weight: bold } /* Literal.Number.Float */ +.mh { color: #005588; font-weight: bold } /* Literal.Number.Hex */ +.mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ +.mo { color: #4400EE; font-weight: bold } /* Literal.Number.Oct */ +.sb { background-color: #fff0f0 } /* Literal.String.Backtick */ +.sc { color: #0044DD } /* Literal.String.Char */ +.sd { color: #DD4422 } /* Literal.String.Doc */ +.s2 { background-color: #fff0f0 } /* Literal.String.Double */ +.se { color: #666666; font-weight: bold; background-color: #fff0f0 } /* Literal.String.Escape */ +.sh { background-color: #fff0f0 } /* Literal.String.Heredoc */ +.si { background-color: #eeeeee } /* Literal.String.Interpol */ +.sx { color: #DD2200; background-color: #fff0f0 } /* Literal.String.Other */ +.sr { color: #000000; background-color: #fff0ff } /* Literal.String.Regex */ +.s1 { background-color: #fff0f0 } /* Literal.String.Single */ +.ss { color: #AA6600 } /* Literal.String.Symbol */ +.bp { color: #007020 } /* Name.Builtin.Pseudo */ +.vc { color: #336699 } /* Name.Variable.Class */ +.vg { color: #dd7700; font-weight: bold } /* Name.Variable.Global */ +.vi { color: #3333BB } /* Name.Variable.Instance */ +.il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ diff --git a/tools/opt/NewPMDriver.cpp b/tools/opt/NewPMDriver.cpp index 58e9caeff0fb1..94242d795aaeb 100644 --- a/tools/opt/NewPMDriver.cpp +++ b/tools/opt/NewPMDriver.cpp @@ -48,6 +48,102 @@ static cl::opt<std::string> "pipeline for handling managed aliasing queries"), cl::Hidden); +/// {{@ These options accept textual pipeline descriptions which will be +/// inserted into default pipelines at the respective extension points +static cl::opt<std::string> PeepholeEPPipeline( + "passes-ep-peephole", + cl::desc("A textual description of the function pass pipeline inserted at " + "the Peephole extension points into default pipelines"), + cl::Hidden); +static cl::opt<std::string> LateLoopOptimizationsEPPipeline( + "passes-ep-late-loop-optimizations", + cl::desc( + "A textual description of the loop pass pipeline inserted at " + "the LateLoopOptimizations extension point into default pipelines"), + cl::Hidden); +static cl::opt<std::string> LoopOptimizerEndEPPipeline( + "passes-ep-loop-optimizer-end", + cl::desc("A textual description of the loop pass pipeline inserted at " + "the LoopOptimizerEnd extension point into default pipelines"), + cl::Hidden); +static cl::opt<std::string> ScalarOptimizerLateEPPipeline( + "passes-ep-scalar-optimizer-late", + cl::desc("A textual description of the function pass pipeline inserted at " + "the ScalarOptimizerLate extension point into default pipelines"), + cl::Hidden); +static cl::opt<std::string> CGSCCOptimizerLateEPPipeline( + "passes-ep-cgscc-optimizer-late", + cl::desc("A textual description of the cgscc pass pipeline inserted at " + "the CGSCCOptimizerLate extension point into default pipelines"), + cl::Hidden); +static cl::opt<std::string> VectorizerStartEPPipeline( + "passes-ep-vectorizer-start", + cl::desc("A textual description of the function pass pipeline inserted at " + "the VectorizerStart extension point into default pipelines"), + cl::Hidden); +/// @}} + +template <typename PassManagerT> +bool tryParsePipelineText(PassBuilder &PB, StringRef PipelineText) { + if (PipelineText.empty()) + return false; + + // Verify the pipeline is parseable: + PassManagerT PM; + if (PB.parsePassPipeline(PM, PipelineText)) + return true; + + errs() << "Could not parse pipeline '" << PipelineText + << "'. I'm going to igore it.\n"; + return false; +} + +/// If one of the EPPipeline command line options was given, register callbacks +/// for parsing and inserting the given pipeline +static void registerEPCallbacks(PassBuilder &PB, bool VerifyEachPass, + bool DebugLogging) { + if (tryParsePipelineText<FunctionPassManager>(PB, PeepholeEPPipeline)) + PB.registerPeepholeEPCallback([&PB, VerifyEachPass, DebugLogging]( + FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) { + PB.parsePassPipeline(PM, PeepholeEPPipeline, VerifyEachPass, + DebugLogging); + }); + if (tryParsePipelineText<LoopPassManager>(PB, + LateLoopOptimizationsEPPipeline)) + PB.registerLateLoopOptimizationsEPCallback( + [&PB, VerifyEachPass, DebugLogging]( + LoopPassManager &PM, PassBuilder::OptimizationLevel Level) { + PB.parsePassPipeline(PM, LateLoopOptimizationsEPPipeline, + VerifyEachPass, DebugLogging); + }); + if (tryParsePipelineText<LoopPassManager>(PB, LoopOptimizerEndEPPipeline)) + PB.registerLoopOptimizerEndEPCallback([&PB, VerifyEachPass, DebugLogging]( + LoopPassManager &PM, PassBuilder::OptimizationLevel Level) { + PB.parsePassPipeline(PM, LoopOptimizerEndEPPipeline, VerifyEachPass, + DebugLogging); + }); + if (tryParsePipelineText<FunctionPassManager>(PB, + ScalarOptimizerLateEPPipeline)) + PB.registerScalarOptimizerLateEPCallback( + [&PB, VerifyEachPass, DebugLogging]( + FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) { + PB.parsePassPipeline(PM, ScalarOptimizerLateEPPipeline, + VerifyEachPass, DebugLogging); + }); + if (tryParsePipelineText<CGSCCPassManager>(PB, CGSCCOptimizerLateEPPipeline)) + PB.registerCGSCCOptimizerLateEPCallback([&PB, VerifyEachPass, DebugLogging]( + CGSCCPassManager &PM, PassBuilder::OptimizationLevel Level) { + PB.parsePassPipeline(PM, CGSCCOptimizerLateEPPipeline, VerifyEachPass, + DebugLogging); + }); + if (tryParsePipelineText<FunctionPassManager>(PB, VectorizerStartEPPipeline)) + PB.registerVectorizerStartEPCallback([&PB, VerifyEachPass, DebugLogging]( + FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) { + PB.parsePassPipeline(PM, VectorizerStartEPPipeline, VerifyEachPass, + DebugLogging); + }); +} + bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, tool_output_file *Out, tool_output_file *ThinLTOLinkOut, @@ -56,7 +152,9 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, bool ShouldPreserveAssemblyUseListOrder, bool ShouldPreserveBitcodeUseListOrder, bool EmitSummaryIndex, bool EmitModuleHash) { + bool VerifyEachPass = VK == VK_VerifyEachPass; PassBuilder PB(TM); + registerEPCallbacks(PB, VerifyEachPass, DebugPM); // Specially handle the alias analysis manager so that we can register // a custom pipeline of AA passes with it. @@ -85,8 +183,7 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, if (VK > VK_NoVerifier) MPM.addPass(VerifierPass()); - if (!PB.parsePassPipeline(MPM, PassPipeline, VK == VK_VerifyEachPass, - DebugPM)) { + if (!PB.parsePassPipeline(MPM, PassPipeline, VerifyEachPass, DebugPM)) { errs() << Arg0 << ": unable to parse pass pipeline description.\n"; return false; } diff --git a/tools/sanstats/sanstats.cpp b/tools/sanstats/sanstats.cpp index b2216eab119e1..4463c0f0e48c4 100644 --- a/tools/sanstats/sanstats.cpp +++ b/tools/sanstats/sanstats.cpp @@ -76,8 +76,11 @@ const char *ReadModule(char SizeofPtr, const char *Begin, const char *End) { if (Begin == End) return nullptr; + // As the instrumentation tracks the return address and not + // the address of the call to `__sanitizer_stats_report` we + // remove one from the address to get the correct DI. if (Expected<DILineInfo> LineInfo = - Symbolizer.symbolizeCode(Filename, Addr)) { + Symbolizer.symbolizeCode(Filename, Addr - 1)) { llvm::outs() << LineInfo->FileName << ':' << LineInfo->Line << ' ' << LineInfo->FunctionName << ' '; } else { diff --git a/tools/yaml2obj/yaml2wasm.cpp b/tools/yaml2obj/yaml2wasm.cpp index 110700d40c328..059ec5f9edcda 100644 --- a/tools/yaml2obj/yaml2wasm.cpp +++ b/tools/yaml2obj/yaml2wasm.cpp @@ -338,7 +338,7 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::DataSection &Section) { encodeULEB128(Section.Segments.size(), OS); for (auto &Segment : Section.Segments) { - encodeULEB128(Segment.Index, OS); + encodeULEB128(Segment.MemoryIndex, OS); writeInitExpr(Segment.Offset, OS); encodeULEB128(Segment.Content.binary_size(), OS); Segment.Content.writeAsBinary(OS); |