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
218 lines
6.8 KiB
C++
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");
|