Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

//===- 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 = M.getDataLayout().getTypeAllocSizeInBits(Ty);
    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

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");