[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

llvm-svn: 337787
This commit is contained in:
Vedant Kumar 2018-07-24 00:41:29 +00:00
parent ca407c4336
commit d6ff43cc71
4 changed files with 128 additions and 16 deletions

View File

@ -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
}

View File

@ -209,7 +209,7 @@ bool diagnoseMisSizedDbgValue(Module &M, DbgValueInst *DVI) {
bool checkDebugifyMetadata(Module &M,
iterator_range<Module::iterator> 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();
}

View File

@ -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<NewPMDebugifyPass> {
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<llvm::StringRef, DebugifyStatistics>;
/// 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<NewPMCheckDebugifyPass> {

View File

@ -217,6 +217,11 @@ static cl::opt<bool> DebugifyEach(
cl::desc(
"Start each pass with debugify and end it with check-debugify"));
static cl::opt<std::string>
DebugifyExport("debugify-export",
cl::desc("Export per-pass debugify statistics to this file"),
cl::value_desc("filename"), cl::init(""));
static cl::opt<bool>
PrintBreakpoints("print-breakpoints-for-testing",
cl::desc("Print select breakpoints location for testing"));
@ -267,34 +272,45 @@ static cl::opt<std::string>
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();