Index: test/DebugInfo/debugify-export.ll =================================================================== --- /dev/null +++ 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 +} Index: tools/opt/Debugify.h =================================================================== --- /dev/null +++ tools/opt/Debugify.h @@ -0,0 +1,75 @@ +//===- Debugify.h - Attach synthetic debug info to everything -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file Interface to the `debugify` synthetic debug info testing utility. +/// +//===----------------------------------------------------------------------===// + +#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(); + +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 = "", + DebugifyStatsMap *StatsMap = nullptr); + +llvm::FunctionPass * +createCheckDebugifyFunctionPass(bool Strip = false, + llvm::StringRef NameOfWrappedPass = "", + DebugifyStatsMap *StatsMap = nullptr); + +struct NewPMCheckDebugifyPass + : public llvm::PassInfoMixin { + llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM); +}; + +#endif // LLVM_TOOLS_OPT_DEBUGIFY_H Index: tools/opt/Debugify.cpp =================================================================== --- tools/opt/Debugify.cpp +++ tools/opt/Debugify.cpp @@ -12,7 +12,7 @@ /// //===----------------------------------------------------------------------===// -#include "PassPrinters.h" +#include "Debugify.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/IR/BasicBlock.h" @@ -209,7 +209,7 @@ 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 @@ 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 @@ 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 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 @@ private: bool Strip; StringRef NameOfWrappedPass; + DebugifyStatsMap *StatsMap; }; /// FunctionPass for checking debug info inserted by -debugify-function, used @@ -356,12 +372,14 @@ 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 @@ 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() << 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 @@ } 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(); } Index: tools/opt/NewPMDriver.cpp =================================================================== --- tools/opt/NewPMDriver.cpp +++ tools/opt/NewPMDriver.cpp @@ -13,6 +13,7 @@ /// //===----------------------------------------------------------------------===// +#include "Debugify.h" #include "NewPMDriver.h" #include "PassPrinters.h" #include "llvm/ADT/StringRef.h" Index: tools/opt/PassPrinters.h =================================================================== --- tools/opt/PassPrinters.h +++ tools/opt/PassPrinters.h @@ -49,24 +49,4 @@ } // end namespace llvm -llvm::ModulePass *createDebugifyModulePass(); -llvm::FunctionPass *createDebugifyFunctionPass(); - -struct NewPMDebugifyPass : public llvm::PassInfoMixin { - llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM); -}; - -llvm::ModulePass * -createCheckDebugifyModulePass(bool Strip = false, - llvm::StringRef NameOfWrappedPass = ""); - -llvm::FunctionPass * -createCheckDebugifyFunctionPass(bool Strip = false, - llvm::StringRef NameOfWrappedPass = ""); - -struct NewPMCheckDebugifyPass - : public llvm::PassInfoMixin { - llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM); -}; - #endif // LLVM_TOOLS_OPT_PASSPRINTERS_H Index: tools/opt/opt.cpp =================================================================== --- tools/opt/opt.cpp +++ tools/opt/opt.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "BreakpointPrinter.h" +#include "Debugify.h" #include "NewPMDriver.h" #include "PassPrinters.h" #include "llvm/ADT/Triple.h" @@ -216,6 +217,11 @@ 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")); @@ -266,34 +272,45 @@ 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) { @@ -838,6 +855,9 @@ Out->os() << BOS->str(); } + if (DebugifyEach && !DebugifyExport.empty()) + exportDebugifyStats(DebugifyExport, Passes.getDebugifyStatsMap()); + // Declare success. if (!NoOutput || PrintBreakpoints) Out->keep();