[llvm] [llvm-dwarfdump][LineCov 1/3] Add variable coverage metrics (PR #169646)

Stephen Tozer via llvm-commits llvm-commits at lists.llvm.org
Mon Dec 1 06:29:32 PST 2025


================
@@ -0,0 +1,244 @@
+//===-- Coverage.cpp - Debug info coverage metrics ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-dwarfdump.h"
+#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/DebugInfo/DIContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"
+#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/DebugProgramInstruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SourceMgr.h"
+#include <set>
+
+using namespace llvm;
+using namespace llvm::dwarf;
+using namespace llvm::object;
+
+typedef std::pair<std::string, std::string> StringPair;
+
+static std::optional<std::set<std::pair<uint16_t, uint32_t>>>
+computeVariableCoverage(DWARFContext &DICtx, DWARFDie DIE,
+                        const DWARFDebugLine::LineTable *const LineTable) {
+  auto addLines = [](const DWARFDebugLine::LineTable *LineTable,
+                     std::set<std::pair<uint16_t, uint32_t>> &Lines,
+                     DWARFAddressRange Range,
+                     std::map<std::string, uint16_t, std::less<>> &FileNames) {
+    std::vector<uint32_t> Rows;
+    if (LineTable->lookupAddressRange({Range.LowPC, Range.SectionIndex},
+                                      Range.HighPC - Range.LowPC, Rows)) {
+      for (const auto &RowI : Rows) {
+        const auto Row = LineTable->Rows[RowI];
+        if (Row.Address.Address < Range.LowPC)
+          continue;
+        const auto FileIndex = Row.File;
+
+        if (!any_of(FileNames,
+                    [FileIndex](auto &FN) { return FN.second == FileIndex; })) {
+          std::string Name;
+          LineTable->getFileNameByIndex(
+              FileIndex, "",
+              DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath, Name);
+          FileNames.emplace(Name, FileIndex);
+        }
+
+        const auto Line = Row.Line;
+        if (Line) // ignore zero lines
+          Lines.insert({FileIndex, Line});
+      }
+    }
+  };
+
+  std::map<std::string, uint16_t, std::less<>> FileNames;
+
+  auto Locations = DIE.getLocations(DW_AT_location);
+  std::optional<std::set<std::pair<uint16_t, uint32_t>>> Lines;
+  if (Locations) {
+    for (const auto &L : Locations.get()) {
+      if (L.Range) {
+        if (!Lines)
+          Lines = {{}};
+        addLines(LineTable, *Lines, L.Range.value(), FileNames);
+      }
+    }
+  } else {
+    consumeError(Locations.takeError());
+  }
+
+  auto Ranges = DIE.getParent().getAddressRanges();
+  std::optional<std::set<std::pair<uint16_t, uint32_t>>> ParentLines;
+  if (Ranges) {
+    ParentLines = {{}};
+    for (const auto &R : Ranges.get())
+      addLines(LineTable, *ParentLines, R, FileNames);
+  } else {
+    consumeError(Ranges.takeError());
+  }
+
+  if (!Lines && ParentLines) {
+    Lines = ParentLines;
+  } else if (ParentLines) {
+    std::set<std::pair<uint16_t, uint32_t>> Intersection;
+    std::set_intersection(Lines->begin(), Lines->end(), ParentLines->begin(),
+                          ParentLines->end(),
+                          std::inserter(Intersection, Intersection.begin()));
+    Lines = Intersection;
+  }
+
+  return Lines;
+}
+
+static const SmallVector<DWARFDie> getParentSubroutines(DWARFDie DIE) {
+  SmallVector<DWARFDie> Parents;
+  DWARFDie Parent = DIE;
+  do
+    if (Parent.getTag() == DW_TAG_subprogram ||
+        Parent.getTag() == DW_TAG_inlined_subroutine)
+      Parents.push_back(Parent);
+  while ((Parent = Parent.getParent()));
+  return Parents;
+}
+
+struct VarKey {
+  const char *const SubprogramName;
+  const char *const Name;
+  std::string DeclFile;
+  uint64_t DeclLine;
+
+  bool operator==(const VarKey &Other) const {
+    return DeclLine == Other.DeclLine &&
+           !strcmp(SubprogramName, Other.SubprogramName) &&
+           !strcmp(Name, Other.Name) && !DeclFile.compare(Other.DeclFile);
+  }
+
+  bool operator<(const VarKey &Other) const {
+    int A = strcmp(SubprogramName, Other.SubprogramName);
+    if (A)
+      return A < 0;
+    int B = strcmp(Name, Other.Name);
+    if (B)
+      return B < 0;
+    int C = DeclFile.compare(Other.DeclFile);
+    if (C)
+      return C < 0;
+    return DeclLine < Other.DeclLine;
+  }
+};
+
+struct VarCoverage {
+  SmallVector<DWARFDie> Parents;
+  size_t Cov;
+  size_t Instances;
+};
+
+typedef std::multimap<VarKey, VarCoverage, std::less<>> VarMap;
+
+static std::optional<const VarKey> getVarKey(DWARFDie Die, DWARFDie Parent) {
+  const auto *const DieName = Die.getName(DINameKind::LinkageName);
+  const auto DieFile =
+      Die.getDeclFile(DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath);
+  const auto *const ParentName = Parent.getName(DINameKind::LinkageName);
+  if (!DieName || !ParentName)
+    return std::nullopt;
+  return VarKey{ParentName, DieName, DieFile, Die.getDeclLine()};
+}
+
+static void displayParents(SmallVector<DWARFDie> Parents, raw_ostream &OS) {
+  bool First = true;
+  for (const auto Parent : Parents) {
+    if (auto FormValue = Parent.find(DW_AT_call_file)) {
+      if (auto OptString = FormValue->getAsFile(
+              DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath)) {
+        if (First)
+          First = false;
+        else
+          OS << ", ";
+        OS << *OptString << ":" << toUnsigned(Parent.find(DW_AT_call_line), 0);
+      }
+    }
+  }
+}
+
+static void displayVariableCoverage(const VarKey &Key, const VarCoverage &Var,
+                                    bool CombineInstances, raw_ostream &OS) {
+  WithColor(OS, HighlightColor::String) << Key.SubprogramName;
+  OS << "\t";
+  if (CombineInstances)
+    OS << Var.Instances;
+  else if (Var.Parents.size())
+    displayParents(Var.Parents, OS);
+  OS << "\t";
+  WithColor(OS, HighlightColor::String) << Key.Name;
+  OS << "\t" << Key.DeclFile << ":" << Key.DeclLine;
+  OS << "\t" << format("%.3g", ((float)Var.Cov / Var.Instances));
+  OS << "\n";
+}
+
+bool dwarfdump::showVariableCoverage(ObjectFile &Obj, DWARFContext &DICtx,
+                                     bool CombineInstances, raw_ostream &OS) {
+  VarMap Vars;
+
+  WithColor(errs(), HighlightColor::Remark) << "Processing DIEs...\n";
----------------
SLTozer wrote:

Is this still used, or is it a "dev" print?

https://github.com/llvm/llvm-project/pull/169646


More information about the llvm-commits mailing list