1 //===- Debugify.cpp - Attach synthetic debug info to everything -----------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 /// \file This pass attaches synthetic debug info to everything. It can be used
10 /// to create targeted tests for debug info preservation.
12 //===----------------------------------------------------------------------===//
15 #include "llvm/ADT/BitVector.h"
16 #include "llvm/ADT/StringExtras.h"
17 #include "llvm/IR/BasicBlock.h"
18 #include "llvm/IR/Constants.h"
19 #include "llvm/IR/DIBuilder.h"
20 #include "llvm/IR/DebugInfo.h"
21 #include "llvm/IR/Function.h"
22 #include "llvm/IR/GlobalVariable.h"
23 #include "llvm/IR/InstIterator.h"
24 #include "llvm/IR/Instruction.h"
25 #include "llvm/IR/Instructions.h"
26 #include "llvm/IR/IntrinsicInst.h"
27 #include "llvm/IR/Module.h"
28 #include "llvm/IR/Type.h"
29 #include "llvm/Pass.h"
30 #include "llvm/Support/raw_ostream.h"
31 #include "llvm/Transforms/IPO.h"
37 cl::opt
<bool> Quiet("debugify-quiet",
38 cl::desc("Suppress verbose debugify output"));
40 raw_ostream
&dbg() { return Quiet
? nulls() : errs(); }
42 uint64_t getAllocSizeInBits(Module
&M
, Type
*Ty
) {
43 return Ty
->isSized() ? M
.getDataLayout().getTypeAllocSizeInBits(Ty
) : 0;
46 bool isFunctionSkipped(Function
&F
) {
47 return F
.isDeclaration() || !F
.hasExactDefinition();
50 /// Find the basic block's terminating instruction.
52 /// Special care is needed to handle musttail and deopt calls, as these behave
53 /// like (but are in fact not) terminators.
54 Instruction
*findTerminatingInstruction(BasicBlock
&BB
) {
55 if (auto *I
= BB
.getTerminatingMustTailCall())
57 if (auto *I
= BB
.getTerminatingDeoptimizeCall())
59 return BB
.getTerminator();
62 bool applyDebugifyMetadata(Module
&M
,
63 iterator_range
<Module::iterator
> Functions
,
65 // Skip modules with debug info.
66 if (M
.getNamedMetadata("llvm.dbg.cu")) {
67 dbg() << Banner
<< "Skipping module with debug info\n";
72 LLVMContext
&Ctx
= M
.getContext();
74 // Get a DIType which corresponds to Ty.
75 DenseMap
<uint64_t, DIType
*> TypeCache
;
76 auto getCachedDIType
= [&](Type
*Ty
) -> DIType
* {
77 uint64_t Size
= getAllocSizeInBits(M
, Ty
);
78 DIType
*&DTy
= TypeCache
[Size
];
80 std::string Name
= "ty" + utostr(Size
);
81 DTy
= DIB
.createBasicType(Name
, Size
, dwarf::DW_ATE_unsigned
);
86 unsigned NextLine
= 1;
88 auto File
= DIB
.createFile(M
.getName(), "/");
89 auto CU
= DIB
.createCompileUnit(dwarf::DW_LANG_C
, File
, "debugify",
90 /*isOptimized=*/true, "", 0);
92 // Visit each instruction.
93 for (Function
&F
: Functions
) {
94 if (isFunctionSkipped(F
))
97 auto SPType
= DIB
.createSubroutineType(DIB
.getOrCreateTypeArray(None
));
98 DISubprogram::DISPFlags SPFlags
=
99 DISubprogram::SPFlagDefinition
| DISubprogram::SPFlagOptimized
;
100 if (F
.hasPrivateLinkage() || F
.hasInternalLinkage())
101 SPFlags
|= DISubprogram::SPFlagLocalToUnit
;
102 auto SP
= DIB
.createFunction(CU
, F
.getName(), F
.getName(), File
, NextLine
,
103 SPType
, NextLine
, DINode::FlagZero
, SPFlags
);
105 for (BasicBlock
&BB
: F
) {
106 // Attach debug locations.
107 for (Instruction
&I
: BB
)
108 I
.setDebugLoc(DILocation::get(Ctx
, NextLine
++, 1, SP
));
110 // Inserting debug values into EH pads can break IR invariants.
114 // Find the terminating instruction, after which no debug values are
116 Instruction
*LastInst
= findTerminatingInstruction(BB
);
117 assert(LastInst
&& "Expected basic block with a terminator");
119 // Maintain an insertion point which can't be invalidated when updates
121 BasicBlock::iterator InsertPt
= BB
.getFirstInsertionPt();
122 assert(InsertPt
!= BB
.end() && "Expected to find an insertion point");
123 Instruction
*InsertBefore
= &*InsertPt
;
125 // Attach debug values.
126 for (Instruction
*I
= &*BB
.begin(); I
!= LastInst
; I
= I
->getNextNode()) {
127 // Skip void-valued instructions.
128 if (I
->getType()->isVoidTy())
131 // Phis and EH pads must be grouped at the beginning of the block.
132 // Only advance the insertion point when we finish visiting these.
133 if (!isa
<PHINode
>(I
) && !I
->isEHPad())
134 InsertBefore
= I
->getNextNode();
136 std::string Name
= utostr(NextVar
++);
137 const DILocation
*Loc
= I
->getDebugLoc().get();
138 auto LocalVar
= DIB
.createAutoVariable(SP
, Name
, File
, Loc
->getLine(),
139 getCachedDIType(I
->getType()),
140 /*AlwaysPreserve=*/true);
141 DIB
.insertDbgValueIntrinsic(I
, LocalVar
, DIB
.createExpression(), Loc
,
145 DIB
.finalizeSubprogram(SP
);
149 // Track the number of distinct lines and variables.
150 NamedMDNode
*NMD
= M
.getOrInsertNamedMetadata("llvm.debugify");
151 auto *IntTy
= Type::getInt32Ty(Ctx
);
152 auto addDebugifyOperand
= [&](unsigned N
) {
153 NMD
->addOperand(MDNode::get(
154 Ctx
, ValueAsMetadata::getConstant(ConstantInt::get(IntTy
, N
))));
156 addDebugifyOperand(NextLine
- 1); // Original number of lines.
157 addDebugifyOperand(NextVar
- 1); // Original number of variables.
158 assert(NMD
->getNumOperands() == 2 &&
159 "llvm.debugify should have exactly 2 operands!");
161 // Claim that this synthetic debug info is valid.
162 StringRef DIVersionKey
= "Debug Info Version";
163 if (!M
.getModuleFlag(DIVersionKey
))
164 M
.addModuleFlag(Module::Warning
, DIVersionKey
, DEBUG_METADATA_VERSION
);
169 /// Return true if a mis-sized diagnostic is issued for \p DVI.
170 bool diagnoseMisSizedDbgValue(Module
&M
, DbgValueInst
*DVI
) {
171 // The size of a dbg.value's value operand should match the size of the
172 // variable it corresponds to.
174 // TODO: This, along with a check for non-null value operands, should be
175 // promoted to verifier failures.
176 Value
*V
= DVI
->getValue();
180 // For now, don't try to interpret anything more complicated than an empty
181 // DIExpression. Eventually we should try to handle OP_deref and fragments.
182 if (DVI
->getExpression()->getNumElements())
185 Type
*Ty
= V
->getType();
186 uint64_t ValueOperandSize
= getAllocSizeInBits(M
, Ty
);
187 Optional
<uint64_t> DbgVarSize
= DVI
->getFragmentSizeInBits();
188 if (!ValueOperandSize
|| !DbgVarSize
)
191 bool HasBadSize
= false;
192 if (Ty
->isIntegerTy()) {
193 auto Signedness
= DVI
->getVariable()->getSignedness();
194 if (Signedness
&& *Signedness
== DIBasicType::Signedness::Signed
)
195 HasBadSize
= ValueOperandSize
< *DbgVarSize
;
197 HasBadSize
= ValueOperandSize
!= *DbgVarSize
;
201 dbg() << "ERROR: dbg.value operand has size " << ValueOperandSize
202 << ", but its variable has size " << *DbgVarSize
<< ": ";
209 bool checkDebugifyMetadata(Module
&M
,
210 iterator_range
<Module::iterator
> Functions
,
211 StringRef NameOfWrappedPass
, StringRef Banner
,
212 bool Strip
, DebugifyStatsMap
*StatsMap
) {
213 // Skip modules without debugify metadata.
214 NamedMDNode
*NMD
= M
.getNamedMetadata("llvm.debugify");
216 dbg() << Banner
<< "Skipping module without debugify metadata\n";
220 auto getDebugifyOperand
= [&](unsigned Idx
) -> unsigned {
221 return mdconst::extract
<ConstantInt
>(NMD
->getOperand(Idx
)->getOperand(0))
224 assert(NMD
->getNumOperands() == 2 &&
225 "llvm.debugify should have exactly 2 operands!");
226 unsigned OriginalNumLines
= getDebugifyOperand(0);
227 unsigned OriginalNumVars
= getDebugifyOperand(1);
228 bool HasErrors
= false;
230 // Track debug info loss statistics if able.
231 DebugifyStatistics
*Stats
= nullptr;
232 if (StatsMap
&& !NameOfWrappedPass
.empty())
233 Stats
= &StatsMap
->operator[](NameOfWrappedPass
);
235 BitVector MissingLines
{OriginalNumLines
, true};
236 BitVector MissingVars
{OriginalNumVars
, true};
237 for (Function
&F
: Functions
) {
238 if (isFunctionSkipped(F
))
241 // Find missing lines.
242 for (Instruction
&I
: instructions(F
)) {
243 if (isa
<DbgValueInst
>(&I
))
246 auto DL
= I
.getDebugLoc();
247 if (DL
&& DL
.getLine() != 0) {
248 MissingLines
.reset(DL
.getLine() - 1);
253 dbg() << "ERROR: Instruction with empty DebugLoc in function ";
254 dbg() << F
.getName() << " --";
261 // Find missing variables and mis-sized debug values.
262 for (Instruction
&I
: instructions(F
)) {
263 auto *DVI
= dyn_cast
<DbgValueInst
>(&I
);
268 (void)to_integer(DVI
->getVariable()->getName(), Var
, 10);
269 assert(Var
<= OriginalNumVars
&& "Unexpected name for DILocalVariable");
270 bool HasBadSize
= diagnoseMisSizedDbgValue(M
, DVI
);
272 MissingVars
.reset(Var
- 1);
273 HasErrors
|= HasBadSize
;
277 // Print the results.
278 for (unsigned Idx
: MissingLines
.set_bits())
279 dbg() << "WARNING: Missing line " << Idx
+ 1 << "\n";
281 for (unsigned Idx
: MissingVars
.set_bits())
282 dbg() << "WARNING: Missing variable " << Idx
+ 1 << "\n";
284 // Update DI loss statistics.
286 Stats
->NumDbgLocsExpected
+= OriginalNumLines
;
287 Stats
->NumDbgLocsMissing
+= MissingLines
.count();
288 Stats
->NumDbgValuesExpected
+= OriginalNumVars
;
289 Stats
->NumDbgValuesMissing
+= MissingVars
.count();
293 if (!NameOfWrappedPass
.empty())
294 dbg() << " [" << NameOfWrappedPass
<< "]";
295 dbg() << ": " << (HasErrors
? "FAIL" : "PASS") << '\n';
297 // Strip the Debugify Metadata if required.
300 M
.eraseNamedMetadata(NMD
);
307 /// ModulePass for attaching synthetic debug info to everything, used with the
308 /// legacy module pass manager.
309 struct DebugifyModulePass
: public ModulePass
{
310 bool runOnModule(Module
&M
) override
{
311 return applyDebugifyMetadata(M
, M
.functions(), "ModuleDebugify: ");
314 DebugifyModulePass() : ModulePass(ID
) {}
316 void getAnalysisUsage(AnalysisUsage
&AU
) const override
{
317 AU
.setPreservesAll();
320 static char ID
; // Pass identification.
323 /// FunctionPass for attaching synthetic debug info to instructions within a
324 /// single function, used with the legacy module pass manager.
325 struct DebugifyFunctionPass
: public FunctionPass
{
326 bool runOnFunction(Function
&F
) override
{
327 Module
&M
= *F
.getParent();
328 auto FuncIt
= F
.getIterator();
329 return applyDebugifyMetadata(M
, make_range(FuncIt
, std::next(FuncIt
)),
330 "FunctionDebugify: ");
333 DebugifyFunctionPass() : FunctionPass(ID
) {}
335 void getAnalysisUsage(AnalysisUsage
&AU
) const override
{
336 AU
.setPreservesAll();
339 static char ID
; // Pass identification.
342 /// ModulePass for checking debug info inserted by -debugify, used with the
343 /// legacy module pass manager.
344 struct CheckDebugifyModulePass
: public ModulePass
{
345 bool runOnModule(Module
&M
) override
{
346 return checkDebugifyMetadata(M
, M
.functions(), NameOfWrappedPass
,
347 "CheckModuleDebugify", Strip
, StatsMap
);
350 CheckDebugifyModulePass(bool Strip
= false, StringRef NameOfWrappedPass
= "",
351 DebugifyStatsMap
*StatsMap
= nullptr)
352 : ModulePass(ID
), Strip(Strip
), NameOfWrappedPass(NameOfWrappedPass
),
353 StatsMap(StatsMap
) {}
355 void getAnalysisUsage(AnalysisUsage
&AU
) const override
{
356 AU
.setPreservesAll();
359 static char ID
; // Pass identification.
363 StringRef NameOfWrappedPass
;
364 DebugifyStatsMap
*StatsMap
;
367 /// FunctionPass for checking debug info inserted by -debugify-function, used
368 /// with the legacy module pass manager.
369 struct CheckDebugifyFunctionPass
: public FunctionPass
{
370 bool runOnFunction(Function
&F
) override
{
371 Module
&M
= *F
.getParent();
372 auto FuncIt
= F
.getIterator();
373 return checkDebugifyMetadata(M
, make_range(FuncIt
, std::next(FuncIt
)),
374 NameOfWrappedPass
, "CheckFunctionDebugify",
378 CheckDebugifyFunctionPass(bool Strip
= false,
379 StringRef NameOfWrappedPass
= "",
380 DebugifyStatsMap
*StatsMap
= nullptr)
381 : FunctionPass(ID
), Strip(Strip
), NameOfWrappedPass(NameOfWrappedPass
),
382 StatsMap(StatsMap
) {}
384 void getAnalysisUsage(AnalysisUsage
&AU
) const override
{
385 AU
.setPreservesAll();
388 static char ID
; // Pass identification.
392 StringRef NameOfWrappedPass
;
393 DebugifyStatsMap
*StatsMap
;
396 } // end anonymous namespace
398 void exportDebugifyStats(llvm::StringRef Path
, const DebugifyStatsMap
&Map
) {
400 raw_fd_ostream OS
{Path
, EC
};
402 errs() << "Could not open file: " << EC
.message() << ", " << Path
<< '\n';
406 OS
<< "Pass Name" << ',' << "# of missing debug values" << ','
407 << "# of missing locations" << ',' << "Missing/Expected value ratio" << ','
408 << "Missing/Expected location ratio" << '\n';
409 for (const auto &Entry
: Map
) {
410 StringRef Pass
= Entry
.first
;
411 DebugifyStatistics Stats
= Entry
.second
;
413 OS
<< Pass
<< ',' << Stats
.NumDbgValuesMissing
<< ','
414 << Stats
.NumDbgLocsMissing
<< ',' << Stats
.getMissingValueRatio() << ','
415 << Stats
.getEmptyLocationRatio() << '\n';
419 ModulePass
*createDebugifyModulePass() { return new DebugifyModulePass(); }
421 FunctionPass
*createDebugifyFunctionPass() {
422 return new DebugifyFunctionPass();
425 PreservedAnalyses
NewPMDebugifyPass::run(Module
&M
, ModuleAnalysisManager
&) {
426 applyDebugifyMetadata(M
, M
.functions(), "ModuleDebugify: ");
427 return PreservedAnalyses::all();
430 ModulePass
*createCheckDebugifyModulePass(bool Strip
,
431 StringRef NameOfWrappedPass
,
432 DebugifyStatsMap
*StatsMap
) {
433 return new CheckDebugifyModulePass(Strip
, NameOfWrappedPass
, StatsMap
);
436 FunctionPass
*createCheckDebugifyFunctionPass(bool Strip
,
437 StringRef NameOfWrappedPass
,
438 DebugifyStatsMap
*StatsMap
) {
439 return new CheckDebugifyFunctionPass(Strip
, NameOfWrappedPass
, StatsMap
);
442 PreservedAnalyses
NewPMCheckDebugifyPass::run(Module
&M
,
443 ModuleAnalysisManager
&) {
444 checkDebugifyMetadata(M
, M
.functions(), "", "CheckModuleDebugify", false,
446 return PreservedAnalyses::all();
449 char DebugifyModulePass::ID
= 0;
450 static RegisterPass
<DebugifyModulePass
> DM("debugify",
451 "Attach debug info to everything");
453 char CheckDebugifyModulePass::ID
= 0;
454 static RegisterPass
<CheckDebugifyModulePass
>
455 CDM("check-debugify", "Check debug info from -debugify");
457 char DebugifyFunctionPass::ID
= 0;
458 static RegisterPass
<DebugifyFunctionPass
> DF("debugify-function",
459 "Attach debug info to a function");
461 char CheckDebugifyFunctionPass::ID
= 0;
462 static RegisterPass
<CheckDebugifyFunctionPass
>
463 CDF("check-debugify-function", "Check debug info from -debugify-function");