llvm-project/llvm/tools/opt/Debugify.cpp
Vedant Kumar 92f7a6200c [Debugify] Add a mode to opt to enable faster testing
Opt's "-enable-debugify" mode adds an instance of Debugify at the
beginning of the pass pipeline, and an instance of CheckDebugify at the
end.

You can enable this mode with lit using: -Dopt="opt -enable-debugify".
Note that running test suites in this mode will result in many failures
due to strict FileCheck commands, etc.

It can be more useful to look for assertion failures which arise only
when Debugify is enabled, e.g to prove that we have (or do not have)
test coverage for some code path with debug info present.

Differential Revision: https://reviews.llvm.org/D41793

llvm-svn: 323256
2018-01-23 20:43:50 +00:00

218 lines
6.8 KiB
C++

//===- Debugify.cpp - 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 This pass attaches synthetic debug info to everything. It can be used
/// to create targeted tests for debug info preservation.
///
//===----------------------------------------------------------------------===//
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/IPO.h"
using namespace llvm;
namespace {
bool applyDebugifyMetadata(Module &M) {
// Skip modules with debug info.
if (M.getNamedMetadata("llvm.dbg.cu")) {
errs() << "Debugify: Skipping module with debug info\n";
return false;
}
DIBuilder DIB(M);
LLVMContext &Ctx = M.getContext();
// Get a DIType which corresponds to Ty.
DenseMap<uint64_t, DIType *> TypeCache;
auto getCachedDIType = [&](Type *Ty) -> DIType * {
uint64_t Size =
Ty->isSized() ? M.getDataLayout().getTypeAllocSizeInBits(Ty) : 0;
DIType *&DTy = TypeCache[Size];
if (!DTy) {
std::string Name = "ty" + utostr(Size);
DTy = DIB.createBasicType(Name, Size, dwarf::DW_ATE_unsigned);
}
return DTy;
};
unsigned NextLine = 1;
unsigned NextVar = 1;
auto File = DIB.createFile(M.getName(), "/");
auto CU =
DIB.createCompileUnit(dwarf::DW_LANG_C, DIB.createFile(M.getName(), "/"),
"debugify", /*isOptimized=*/true, "", 0);
// Visit each instruction.
for (Function &F : M) {
if (F.isDeclaration())
continue;
auto SPType = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None));
bool IsLocalToUnit = F.hasPrivateLinkage() || F.hasInternalLinkage();
auto SP =
DIB.createFunction(CU, F.getName(), F.getName(), File, NextLine, SPType,
IsLocalToUnit, F.hasExactDefinition(), NextLine,
DINode::FlagZero, /*isOptimized=*/true);
F.setSubprogram(SP);
for (BasicBlock &BB : F) {
// Attach debug locations.
for (Instruction &I : BB)
I.setDebugLoc(DILocation::get(Ctx, NextLine++, 1, SP));
// Attach debug values.
for (Instruction &I : BB) {
// Skip void-valued instructions.
if (I.getType()->isVoidTy())
continue;
// Skip the terminator instruction and any just-inserted intrinsics.
if (isa<TerminatorInst>(&I) || isa<DbgValueInst>(&I))
break;
std::string Name = utostr(NextVar++);
const DILocation *Loc = I.getDebugLoc().get();
auto LocalVar = DIB.createAutoVariable(SP, Name, File, Loc->getLine(),
getCachedDIType(I.getType()),
/*AlwaysPreserve=*/true);
DIB.insertDbgValueIntrinsic(&I, LocalVar, DIB.createExpression(), Loc,
BB.getTerminator());
}
}
DIB.finalizeSubprogram(SP);
}
DIB.finalize();
// Track the number of distinct lines and variables.
NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.debugify");
auto *IntTy = Type::getInt32Ty(Ctx);
auto addDebugifyOperand = [&](unsigned N) {
NMD->addOperand(MDNode::get(
Ctx, ValueAsMetadata::getConstant(ConstantInt::get(IntTy, N))));
};
addDebugifyOperand(NextLine - 1); // Original number of lines.
addDebugifyOperand(NextVar - 1); // Original number of variables.
return true;
}
void checkDebugifyMetadata(Module &M) {
// Skip modules without debugify metadata.
NamedMDNode *NMD = M.getNamedMetadata("llvm.debugify");
if (!NMD)
return;
auto getDebugifyOperand = [&](unsigned Idx) -> unsigned {
return mdconst::extract<ConstantInt>(NMD->getOperand(Idx)->getOperand(0))
->getZExtValue();
};
unsigned OriginalNumLines = getDebugifyOperand(0);
unsigned OriginalNumVars = getDebugifyOperand(1);
bool HasErrors = false;
// Find missing lines.
BitVector MissingLines{OriginalNumLines, true};
for (Function &F : M) {
for (Instruction &I : instructions(F)) {
if (isa<DbgValueInst>(&I))
continue;
auto DL = I.getDebugLoc();
if (DL) {
MissingLines.reset(DL.getLine() - 1);
continue;
}
outs() << "ERROR: Instruction with empty DebugLoc -- ";
I.print(outs());
outs() << "\n";
HasErrors = true;
}
}
for (unsigned Idx : MissingLines.set_bits())
outs() << "WARNING: Missing line " << Idx + 1 << "\n";
// Find missing variables.
BitVector MissingVars{OriginalNumVars, true};
for (Function &F : M) {
for (Instruction &I : instructions(F)) {
auto *DVI = dyn_cast<DbgValueInst>(&I);
if (!DVI)
continue;
unsigned Var = ~0U;
(void)to_integer(DVI->getVariable()->getName(), Var, 10);
assert(Var <= OriginalNumVars && "Unexpected name for DILocalVariable");
MissingVars.reset(Var - 1);
}
}
for (unsigned Idx : MissingVars.set_bits())
outs() << "ERROR: Missing variable " << Idx + 1 << "\n";
HasErrors |= MissingVars.count() > 0;
outs() << "CheckDebugify: " << (HasErrors ? "FAIL" : "PASS") << "\n";
}
/// Attach synthetic debug info to everything.
struct DebugifyPass : public ModulePass {
bool runOnModule(Module &M) override { return applyDebugifyMetadata(M); }
DebugifyPass() : ModulePass(ID) {}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
}
static char ID; // Pass identification.
};
/// Check debug info inserted by -debugify for completeness.
struct CheckDebugifyPass : public ModulePass {
bool runOnModule(Module &M) override {
checkDebugifyMetadata(M);
return false;
}
CheckDebugifyPass() : ModulePass(ID) {}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
}
static char ID; // Pass identification.
};
} // end anonymous namespace
ModulePass *createDebugifyPass() { return new DebugifyPass(); }
ModulePass *createCheckDebugifyPass() { return new CheckDebugifyPass(); }
char DebugifyPass::ID = 0;
static RegisterPass<DebugifyPass> X("debugify",
"Attach debug info to everything");
char CheckDebugifyPass::ID = 0;
static RegisterPass<CheckDebugifyPass> Y("check-debugify",
"Check debug info from -debugify");