From 499e3df2ce6d975ee601f2ea5786dda952d00e7f Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Tue, 24 Jul 2018 00:41:29 +0000 Subject: [PATCH] [Debugify] Export per-pass debug info loss statistics Add a -debugify-export option to opt. This exports per-pass `debugify` loss statistics to a file in CSV format. For some interesting numbers on debug value loss during an -O2 build of the sqlite3 amalgamation, see the review thread. Differential Revision: https://reviews.llvm.org/D49003 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@337787 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/DebugInfo/debugify-export.ll | 14 +++++++ tools/opt/Debugify.cpp | 67 +++++++++++++++++++++++++------ tools/opt/Debugify.h | 40 +++++++++++++++++- tools/opt/opt.cpp | 23 ++++++++++- 4 files changed, 128 insertions(+), 16 deletions(-) create mode 100644 test/DebugInfo/debugify-export.ll diff --git a/test/DebugInfo/debugify-export.ll b/test/DebugInfo/debugify-export.ll new file mode 100644 index 00000000000..77930355ae9 --- /dev/null +++ b/test/DebugInfo/debugify-export.ll @@ -0,0 +1,14 @@ +; RUN: opt < %s -debugify-each -debugify-quiet -debugify-export - -o /dev/null | FileCheck %s + +; CHECK: Pass Name +; CHECK-SAME: # of missing debug values +; CHECK-SAME: # of missing locations +; CHECK-SAME: Missing/Expected value ratio +; CHECK-SAME: Missing/Expected location ratio + +; CHECK: Module Verifier +; CHECK-SAME: 0,0,0.000000e+00,0.000000e+00 + +define void @foo() { + ret void +} diff --git a/tools/opt/Debugify.cpp b/tools/opt/Debugify.cpp index b56a94b6e5f..6c3cdc75e33 100644 --- a/tools/opt/Debugify.cpp +++ b/tools/opt/Debugify.cpp @@ -209,7 +209,7 @@ bool diagnoseMisSizedDbgValue(Module &M, DbgValueInst *DVI) { bool checkDebugifyMetadata(Module &M, iterator_range Functions, StringRef NameOfWrappedPass, StringRef Banner, - bool Strip) { + bool Strip, DebugifyStatsMap *StatsMap) { // Skip modules without debugify metadata. NamedMDNode *NMD = M.getNamedMetadata("llvm.debugify"); if (!NMD) { @@ -227,6 +227,11 @@ bool checkDebugifyMetadata(Module &M, unsigned OriginalNumVars = getDebugifyOperand(1); bool HasErrors = false; + // Track debug info loss statistics if able. + DebugifyStatistics *Stats = nullptr; + if (StatsMap && !NameOfWrappedPass.empty()) + Stats = &StatsMap->operator[](NameOfWrappedPass); + BitVector MissingLines{OriginalNumLines, true}; BitVector MissingVars{OriginalNumVars, true}; for (Function &F : Functions) { @@ -276,6 +281,14 @@ bool checkDebugifyMetadata(Module &M, for (unsigned Idx : MissingVars.set_bits()) dbg() << "WARNING: Missing variable " << Idx + 1 << "\n"; + // Update DI loss statistics. + if (Stats) { + Stats->NumDbgLocsExpected += OriginalNumLines; + Stats->NumDbgLocsMissing += MissingLines.count(); + Stats->NumDbgValuesExpected += OriginalNumVars; + Stats->NumDbgValuesMissing += MissingVars.count(); + } + dbg() << Banner; if (!NameOfWrappedPass.empty()) dbg() << " [" << NameOfWrappedPass << "]"; @@ -331,11 +344,13 @@ struct DebugifyFunctionPass : public FunctionPass { struct CheckDebugifyModulePass : public ModulePass { bool runOnModule(Module &M) override { return checkDebugifyMetadata(M, M.functions(), NameOfWrappedPass, - "CheckModuleDebugify", Strip); + "CheckModuleDebugify", Strip, StatsMap); } - CheckDebugifyModulePass(bool Strip = false, StringRef NameOfWrappedPass = "") - : ModulePass(ID), Strip(Strip), NameOfWrappedPass(NameOfWrappedPass) {} + CheckDebugifyModulePass(bool Strip = false, StringRef NameOfWrappedPass = "", + DebugifyStatsMap *StatsMap = nullptr) + : ModulePass(ID), Strip(Strip), NameOfWrappedPass(NameOfWrappedPass), + StatsMap(StatsMap) {} void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesAll(); @@ -346,6 +361,7 @@ struct CheckDebugifyModulePass : public ModulePass { private: bool Strip; StringRef NameOfWrappedPass; + DebugifyStatsMap *StatsMap; }; /// FunctionPass for checking debug info inserted by -debugify-function, used @@ -356,12 +372,14 @@ struct CheckDebugifyFunctionPass : public FunctionPass { auto FuncIt = F.getIterator(); return checkDebugifyMetadata(M, make_range(FuncIt, std::next(FuncIt)), NameOfWrappedPass, "CheckFunctionDebugify", - Strip); + Strip, StatsMap); } CheckDebugifyFunctionPass(bool Strip = false, - StringRef NameOfWrappedPass = "") - : FunctionPass(ID), Strip(Strip), NameOfWrappedPass(NameOfWrappedPass) {} + StringRef NameOfWrappedPass = "", + DebugifyStatsMap *StatsMap = nullptr) + : FunctionPass(ID), Strip(Strip), NameOfWrappedPass(NameOfWrappedPass), + StatsMap(StatsMap) {} void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesAll(); @@ -372,10 +390,32 @@ struct CheckDebugifyFunctionPass : public FunctionPass { private: bool Strip; StringRef NameOfWrappedPass; + DebugifyStatsMap *StatsMap; }; } // end anonymous namespace +void exportDebugifyStats(llvm::StringRef Path, const DebugifyStatsMap &Map) { + std::error_code EC; + raw_fd_ostream OS{Path, EC}; + if (EC) { + errs() << "Could not open file: " << EC.message() << ", " << Path << '\n'; + return; + } + + OS << "Pass Name" << ',' << "# of missing debug values" << ',' + << "# of missing locations" << ',' << "Missing/Expected value ratio" << ',' + << "Missing/Expected location ratio" << '\n'; + for (const auto &Entry : Map) { + StringRef Pass = Entry.first; + DebugifyStatistics Stats = Entry.second; + + OS << Pass << ',' << Stats.NumDbgValuesMissing << ',' + << Stats.NumDbgLocsMissing << ',' << Stats.getMissingValueRatio() << ',' + << Stats.getEmptyLocationRatio() << '\n'; + } +} + ModulePass *createDebugifyModulePass() { return new DebugifyModulePass(); } FunctionPass *createDebugifyFunctionPass() { @@ -388,18 +428,21 @@ PreservedAnalyses NewPMDebugifyPass::run(Module &M, ModuleAnalysisManager &) { } ModulePass *createCheckDebugifyModulePass(bool Strip, - StringRef NameOfWrappedPass) { - return new CheckDebugifyModulePass(Strip, NameOfWrappedPass); + StringRef NameOfWrappedPass, + DebugifyStatsMap *StatsMap) { + return new CheckDebugifyModulePass(Strip, NameOfWrappedPass, StatsMap); } FunctionPass *createCheckDebugifyFunctionPass(bool Strip, - StringRef NameOfWrappedPass) { - return new CheckDebugifyFunctionPass(Strip, NameOfWrappedPass); + StringRef NameOfWrappedPass, + DebugifyStatsMap *StatsMap) { + return new CheckDebugifyFunctionPass(Strip, NameOfWrappedPass, StatsMap); } PreservedAnalyses NewPMCheckDebugifyPass::run(Module &M, ModuleAnalysisManager &) { - checkDebugifyMetadata(M, M.functions(), "", "CheckModuleDebugify", false); + checkDebugifyMetadata(M, M.functions(), "", "CheckModuleDebugify", false, + nullptr); return PreservedAnalyses::all(); } diff --git a/tools/opt/Debugify.h b/tools/opt/Debugify.h index 14ea5d3c6eb..d1a60c73e72 100644 --- a/tools/opt/Debugify.h +++ b/tools/opt/Debugify.h @@ -14,7 +14,10 @@ #ifndef LLVM_TOOLS_OPT_DEBUGIFY_H #define LLVM_TOOLS_OPT_DEBUGIFY_H +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/MapVector.h" #include "llvm/IR/PassManager.h" +#include "llvm/Support/raw_ostream.h" llvm::ModulePass *createDebugifyModulePass(); llvm::FunctionPass *createDebugifyFunctionPass(); @@ -23,13 +26,46 @@ struct NewPMDebugifyPass : public llvm::PassInfoMixin { llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM); }; +/// Track how much `debugify` information has been lost. +struct DebugifyStatistics { + /// Number of missing dbg.values. + unsigned NumDbgValuesMissing = 0; + + /// Number of dbg.values expected. + unsigned NumDbgValuesExpected = 0; + + /// Number of instructions with empty debug locations. + unsigned NumDbgLocsMissing = 0; + + /// Number of instructions expected to have debug locations. + unsigned NumDbgLocsExpected = 0; + + /// Get the ratio of missing/expected dbg.values. + float getMissingValueRatio() const { + return float(NumDbgValuesMissing) / float(NumDbgLocsExpected); + } + + /// Get the ratio of missing/expected instructions with locations. + float getEmptyLocationRatio() const { + return float(NumDbgLocsMissing) / float(NumDbgLocsExpected); + } +}; + +/// Map pass names to a per-pass DebugifyStatistics instance. +using DebugifyStatsMap = llvm::MapVector; + +/// Export per-pass debugify statistics to the file specified by \p Path. +void exportDebugifyStats(llvm::StringRef Path, const DebugifyStatsMap &Map); + llvm::ModulePass * createCheckDebugifyModulePass(bool Strip = false, - llvm::StringRef NameOfWrappedPass = ""); + llvm::StringRef NameOfWrappedPass = "", + DebugifyStatsMap *StatsMap = nullptr); llvm::FunctionPass * createCheckDebugifyFunctionPass(bool Strip = false, - llvm::StringRef NameOfWrappedPass = ""); + llvm::StringRef NameOfWrappedPass = "", + DebugifyStatsMap *StatsMap = nullptr); struct NewPMCheckDebugifyPass : public llvm::PassInfoMixin { diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 98ad700409c..6e287b6c0ab 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -217,6 +217,11 @@ static cl::opt DebugifyEach( cl::desc( "Start each pass with debugify and end it with check-debugify")); +static cl::opt + DebugifyExport("debugify-export", + cl::desc("Export per-pass debugify statistics to this file"), + cl::value_desc("filename"), cl::init("")); + static cl::opt PrintBreakpoints("print-breakpoints-for-testing", cl::desc("Print select breakpoints location for testing")); @@ -267,34 +272,45 @@ static cl::opt cl::value_desc("filename")); class OptCustomPassManager : public legacy::PassManager { + DebugifyStatsMap DIStatsMap; + public: using super = legacy::PassManager; void add(Pass *P) override { + // Wrap each pass with (-check)-debugify passes if requested, making + // exceptions for passes which shouldn't see -debugify instrumentation. bool WrapWithDebugify = DebugifyEach && !P->getAsImmutablePass() && !isIRPrintingPass(P) && !isBitcodeWriterPass(P); if (!WrapWithDebugify) { super::add(P); return; } + + // Apply -debugify/-check-debugify before/after each pass and collect + // debug info loss statistics. PassKind Kind = P->getPassKind(); + StringRef Name = P->getPassName(); + // TODO: Implement Debugify for BasicBlockPass, LoopPass. switch (Kind) { case PT_Function: super::add(createDebugifyFunctionPass()); super::add(P); - super::add(createCheckDebugifyFunctionPass(true, P->getPassName())); + super::add(createCheckDebugifyFunctionPass(true, Name, &DIStatsMap)); break; case PT_Module: super::add(createDebugifyModulePass()); super::add(P); - super::add(createCheckDebugifyModulePass(true, P->getPassName())); + super::add(createCheckDebugifyModulePass(true, Name, &DIStatsMap)); break; default: super::add(P); break; } } + + const DebugifyStatsMap &getDebugifyStatsMap() const { return DIStatsMap; } }; static inline void addPass(legacy::PassManagerBase &PM, Pass *P) { @@ -839,6 +855,9 @@ int main(int argc, char **argv) { Out->os() << BOS->str(); } + if (DebugifyEach && !DebugifyExport.empty()) + exportDebugifyStats(DebugifyExport, Passes.getDebugifyStatsMap()); + // Declare success. if (!NoOutput || PrintBreakpoints) Out->keep();