mirror of
https://github.com/RPCS3/llvm.git
synced 2024-12-11 05:35:11 +00:00
[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
This commit is contained in:
parent
a4afd1f12d
commit
499e3df2ce
14
test/DebugInfo/debugify-export.ll
Normal file
14
test/DebugInfo/debugify-export.ll
Normal 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
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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> {
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user