[llvm-branch-commits] [llvm] obj2yaml: Introduce CovMap dump (PR #127432)

NAKAMURA Takumi via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Mar 6 19:44:48 PST 2025


https://github.com/chapuni updated https://github.com/llvm/llvm-project/pull/127432

>From 7e29d6ace39058b631dcfff5533d8aee055de6dd Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Mon, 3 Mar 2025 12:25:13 +0900
Subject: [PATCH] obj2yaml

---
 llvm/include/llvm/ObjectYAML/CovMap.h       |  47 ++-
 llvm/include/llvm/ProfileData/InstrProf.h   |   6 +
 llvm/lib/ObjectYAML/CovMap.cpp              | 377 +++++++++++++++++++-
 llvm/lib/ProfileData/InstrProf.cpp          |  23 +-
 llvm/test/tools/obj2yaml/ELF/covmap-be.yaml |   2 +
 llvm/test/tools/obj2yaml/ELF/covmap.yaml    |   2 +
 llvm/tools/obj2yaml/elf2yaml.cpp            |  59 ++-
 llvm/tools/obj2yaml/obj2yaml.cpp            |   2 +-
 llvm/tools/obj2yaml/obj2yaml.h              |   4 +
 9 files changed, 512 insertions(+), 10 deletions(-)

diff --git a/llvm/include/llvm/ObjectYAML/CovMap.h b/llvm/include/llvm/ObjectYAML/CovMap.h
index 3a0b86435d490..406204ee024fb 100644
--- a/llvm/include/llvm/ObjectYAML/CovMap.h
+++ b/llvm/include/llvm/ObjectYAML/CovMap.h
@@ -16,7 +16,7 @@
 //
 // - llvm::covmap
 //
-//   Provides YAML encoder for coverage map.
+//   Provides YAML encoder and decoder for coverage map.
 //
 //===----------------------------------------------------------------------===//
 
@@ -27,6 +27,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ObjectYAML/ELFYAML.h"
 #include "llvm/Support/Endian.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/YAMLTraits.h"
 #include <array>
 #include <cstdint>
@@ -41,6 +42,8 @@ class raw_ostream;
 
 namespace llvm::coverage::yaml {
 
+struct DecoderContext;
+
 /// Base Counter, corresponding to coverage::Counter.
 struct CounterTy {
   enum TagTy : uint8_t {
@@ -65,6 +68,12 @@ struct CounterTy {
 
   virtual void mapping(llvm::yaml::IO &IO);
 
+  /// Holds Val for extensions.
+  Error decodeOrTag(DecoderContext &Data);
+
+  /// Raise Error if Val isn't empty.
+  Error decode(DecoderContext &Data);
+
   void encode(raw_ostream &OS) const;
 };
 
@@ -85,6 +94,8 @@ struct DecisionTy {
 
   void mapping(llvm::yaml::IO &IO);
 
+  Error decode(DecoderContext &Data);
+
   void encode(raw_ostream &OS) const;
 };
 
@@ -118,6 +129,8 @@ struct RecTy : CounterTy {
 
   void mapping(llvm::yaml::IO &IO) override;
 
+  Error decode(DecoderContext &Data);
+
   void encode(uint64_t &StartLoc, raw_ostream &OS) const;
 };
 
@@ -142,6 +155,10 @@ struct CovFunTy {
 
   void mapping(llvm::yaml::IO &IO);
 
+  /// Depends on CovMap and SymTab(IPSK_names)
+  Expected<uint64_t> decode(const ArrayRef<uint8_t> Content, uint64_t Offset,
+                            endianness Endianness);
+
   void encode(raw_ostream &OS, endianness Endianness) const;
 };
 
@@ -170,6 +187,9 @@ struct CovMapTy {
   bool useWD() const { return (!Version || *Version >= 4); }
   StringRef getWD() const { return (WD ? *WD : StringRef()); }
 
+  Expected<uint64_t> decode(const ArrayRef<uint8_t> Content, uint64_t Offset,
+                            endianness Endianness);
+
   /// Generate Accumulated list with WD.
   /// Returns a single element {WD} if AccFiles is not given.
   std::vector<std::string>
@@ -236,6 +256,31 @@ LLVM_COVERAGE_YAML_ELEM_MAPPING(CovMapTy)
 
 namespace llvm::covmap {
 
+class Decoder {
+protected:
+  endianness Endianness;
+
+public:
+  Decoder(endianness Endianness) : Endianness(Endianness) {}
+  virtual ~Decoder() {}
+
+  /// Returns DecoderImpl.
+  static std::unique_ptr<Decoder> get(endianness Endianness,
+                                      bool CovMapEnabled);
+
+  /// Called from the Sections loop in advance of the final dump.
+  /// Decoder predecodes CovMap for Version info.
+  virtual Error acquire(unsigned AddressAlign, StringRef Name,
+                        ArrayRef<uint8_t> Content) = 0;
+
+  /// Make contents on ELFYAML object. CovMap is predecoded.
+  virtual Error make(ELFYAML::CovMapSectionBase *Base,
+                     ArrayRef<uint8_t> Content) = 0;
+
+  /// Suppress emission of CovMap unless enabled.
+  static bool enabled;
+};
+
 class Encoder {
 protected:
   endianness Endianness;
diff --git a/llvm/include/llvm/ProfileData/InstrProf.h b/llvm/include/llvm/ProfileData/InstrProf.h
index 7133c0c6a302c..e20424da3cac2 100644
--- a/llvm/include/llvm/ProfileData/InstrProf.h
+++ b/llvm/include/llvm/ProfileData/InstrProf.h
@@ -545,6 +545,12 @@ class InstrProfSymtab {
   /// This method is a wrapper to \c readAndDecodeStrings method.
   Error create(StringRef NameStrings);
 
+  // PrfNames is nested array.
+  using PrfNamesTy = SmallVector<std::string>;
+  using PrfNamesChunksTy = SmallVector<PrfNamesTy, 1>;
+
+  Expected<PrfNamesChunksTy> createAndGetList(ArrayRef<uint8_t> Content);
+
   /// Initialize symtab states with function names and vtable names. \c
   /// FuncNameStrings is a string composed of one or more encoded function name
   /// strings, and \c VTableNameStrings composes of one or more encoded vtable
diff --git a/llvm/lib/ObjectYAML/CovMap.cpp b/llvm/lib/ObjectYAML/CovMap.cpp
index 7662284caee76..dcf90f7b109cb 100644
--- a/llvm/lib/ObjectYAML/CovMap.cpp
+++ b/llvm/lib/ObjectYAML/CovMap.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// Implementations of CovMap and encoder.
+// Implementations of CovMap, encoder, decoder.
 //
 //===----------------------------------------------------------------------===//
 
@@ -15,9 +15,11 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ObjectYAML/ELFYAML.h"
+#include "llvm/ProfileData/Coverage/CoverageMappingReader.h"
 #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
 #include "llvm/ProfileData/InstrProf.h"
 #include "llvm/Support/Alignment.h"
+#include "llvm/Support/DataExtractor.h"
 #include "llvm/Support/Endian.h"
 #include "llvm/Support/LEB128.h"
 #include "llvm/Support/MD5.h"
@@ -37,6 +39,28 @@ using namespace llvm;
 using namespace llvm::coverage::yaml;
 using namespace llvm::covmap;
 
+bool Decoder::enabled;
+
+// DataExtractor w/ single Cursor
+struct coverage::yaml::DecoderContext : DataExtractor, DataExtractor::Cursor {
+  uint64_t LineStart = 0;
+
+  DecoderContext(const ArrayRef<uint8_t> Content, bool IsLE)
+      : DataExtractor(Content, IsLE, /*AddressSize=*/0),
+        DataExtractor::Cursor(0) {}
+
+  bool eof() { return DataExtractor::eof(*this); }
+  uint32_t getU32() { return DataExtractor::getU32(*this); }
+  uint64_t getU64() { return DataExtractor::getU64(*this); }
+  Expected<uint64_t> getULEB128() {
+    uint64_t Result = DataExtractor::getULEB128(*this);
+    if (!*this)
+      return takeError();
+    return Result;
+  }
+  StringRef getBytes(size_t sz) { return DataExtractor::getBytes(*this, sz); }
+};
+
 void CounterTy::encode(raw_ostream &OS) const {
   std::pair<unsigned, uint64_t> C;
   if (RefOpt)
@@ -55,11 +79,55 @@ void CounterTy::encode(raw_ostream &OS) const {
   encodeULEB128(C.first | (C.second << 2), OS);
 }
 
+Error CounterTy::decodeOrTag(DecoderContext &Data) {
+  auto COrErr = Data.getULEB128();
+  if (!COrErr)
+    return COrErr.takeError();
+  auto T = static_cast<TagTy>(*COrErr & 0x03);
+  auto V = (*COrErr >> 2);
+  if (T == Zero) {
+    if (V == 0)
+      Tag = Zero; // w/o Val
+    else
+      Val = V; // w/o Tag
+  } else {
+    Tag = T;
+    Val = V;
+  }
+
+  return Error::success();
+}
+
+Error CounterTy::decode(DecoderContext &Data) {
+  if (auto E = decodeOrTag(Data))
+    return E;
+  if (!this->Tag && this->Val)
+    return make_error<CoverageMapError>(
+        coveragemap_error::malformed,
+        "Counter::Zero shouldn't have the Val: 0x" +
+            Twine::utohexstr(*this->Val));
+  return Error::success();
+}
+
 void DecisionTy::encode(raw_ostream &OS) const {
   encodeULEB128(BIdx, OS);
   encodeULEB128(NC, OS);
 }
 
+Error DecisionTy::decode(DecoderContext &Data) {
+  auto BIdxOrErr = Data.getULEB128();
+  if (!BIdxOrErr)
+    return BIdxOrErr.takeError();
+  BIdx = *BIdxOrErr;
+
+  auto NCOrErr = Data.getULEB128();
+  if (!NCOrErr)
+    return NCOrErr.takeError();
+  NC = *NCOrErr;
+
+  return Error::success();
+}
+
 void RecTy::encode(uint64_t &StartLoc, raw_ostream &OS) const {
   if (Expansion) {
     encodeULEB128(4 + (*Expansion << 3), OS);
@@ -104,6 +172,106 @@ void RecTy::encode(uint64_t &StartLoc, raw_ostream &OS) const {
   }
 }
 
+Error RecTy::decode(DecoderContext &Data) {
+  auto getU16 = [&]() -> Expected<uint16_t> {
+    auto ValOrErr = Data.getULEB128();
+    if (!ValOrErr)
+      return ValOrErr.takeError();
+    if (*ValOrErr > 0x7FFF + 1)
+      return make_error<CoverageMapError>(coveragemap_error::malformed,
+                                          "MC/DC index is out of range: 0x" +
+                                              Twine::utohexstr(*ValOrErr));
+    return static_cast<uint16_t>(*ValOrErr);
+  };
+
+  auto decodeBranch = [&]() -> Error {
+    auto &B = BranchOpt.emplace();
+    if (auto E = B[0].decode(Data))
+      return E;
+    if (auto E = B[1].decode(Data))
+      return E;
+    return Error::success();
+  };
+
+  // Decode tagged CounterTy
+  if (auto E = CounterTy::decodeOrTag(Data))
+    return E;
+  if (!this->Val || this->Tag) {
+    // Compatible to CounterTy
+  } else if (*this->Val & 1u) {
+    Expansion = (*this->Val >> 1);
+    this->Val.reset();
+  } else {
+    auto Tag = *this->Val >> 1;
+    this->Val.reset();
+    switch (Tag) {
+    case Skip:
+      ExtTag = Skip; // w/o Val
+      break;
+    case Decision:
+      if (auto E = DecisionOpt.emplace().decode(Data))
+        return E;
+      ExtTag = Decision;
+      break;
+    case Branch:
+      if (auto E = decodeBranch())
+        return E;
+      ExtTag = Branch;
+      break;
+    case MCDCBranch: {
+      if (auto E = decodeBranch())
+        return E;
+      auto I0OrErr = getU16();
+      if (!I0OrErr)
+        return I0OrErr.takeError();
+      auto I1OrErr = getU16();
+      if (!I1OrErr)
+        return I1OrErr.takeError();
+      auto I2OrErr = getU16();
+      if (!I2OrErr)
+        return I2OrErr.takeError();
+      MCDC = {*I0OrErr, *I1OrErr, *I2OrErr};
+      ExtTag = MCDCBranch;
+      break;
+    }
+    default:
+      return make_error<CoverageMapError>(
+          coveragemap_error::malformed,
+          "Record doesn't have a valid Tag: 0x" + Twine::utohexstr(Tag));
+    }
+  }
+
+  // Decode Loc
+  auto LSDeltaOrErr = Data.getULEB128();
+  if (!LSDeltaOrErr)
+    return LSDeltaOrErr.takeError();
+  Data.LineStart += *LSDeltaOrErr;
+
+  auto CSOrErr = Data.getULEB128();
+  if (!CSOrErr)
+    return CSOrErr.takeError();
+
+  auto NLOrErr = Data.getULEB128();
+  if (!NLOrErr)
+    return NLOrErr.takeError();
+  auto LineEnd = Data.LineStart + *NLOrErr;
+
+  auto CEOrErr = Data.getULEB128();
+  if (!CEOrErr)
+    return CEOrErr.takeError();
+  auto ColumnEnd = *CEOrErr;
+
+  // Gap is set in ColumnEnd:31
+  if (ColumnEnd & (1u << 31))
+    isGap = true;
+  ColumnEnd &= ((1u << 31) - 1);
+
+  dLoc = {*LSDeltaOrErr, *CSOrErr, *NLOrErr, ColumnEnd};
+  Loc = {Data.LineStart, *CSOrErr, LineEnd, ColumnEnd};
+
+  return Error::success();
+}
+
 void CovFunTy::encode(raw_ostream &OS, endianness Endianness) const {
   // Encode Body in advance since DataSize should be known.
   std::string Body;
@@ -194,6 +362,72 @@ CovMapTy::encodeFilenames(const std::optional<ArrayRef<StringRef>> &AccFilesOpt,
   return {llvm::IndexedInstrProf::ComputeHash(FilenamesBlob), FilenamesBlob};
 }
 
+Expected<uint64_t> CovFunTy::decode(const ArrayRef<uint8_t> Content,
+                                    uint64_t Offset, endianness Endianness) {
+  DecoderContext Data(Content, (Endianness == endianness::little));
+  Data.seek(Offset);
+
+  uint32_t DataSize;
+  [[maybe_unused]] char CoverageMapping; // Ignored
+
+#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Initializer)                  \
+  if (sizeof(Type) == sizeof(uint64_t))                                        \
+    Name = Data.getU64();                                                      \
+  else if (sizeof(Type) == sizeof(uint32_t))                                   \
+    Name = Data.getU32();                                                      \
+  else                                                                         \
+    assert(sizeof(Type) == sizeof(CoverageMapping) && "Unknown type");
+
+#include "llvm/ProfileData/InstrProfData.inc"
+
+  if (!Data)
+    return Data.takeError();
+
+  [[maybe_unused]] auto ExpectedEndOffset = Data.tell() + DataSize;
+
+  // Decode body.
+  FileIDs.emplace();
+
+  auto NumFilesOrErr = Data.getULEB128();
+  if (!NumFilesOrErr)
+    return NumFilesOrErr.takeError();
+  for (unsigned I = 0, E = *NumFilesOrErr; I != E; ++I) {
+    if (auto IDOrErr = Data.getULEB128())
+      FileIDs->push_back(*IDOrErr);
+    else
+      return IDOrErr.takeError();
+  }
+
+  auto NumExprOrErr = Data.getULEB128();
+  if (!NumExprOrErr)
+    return NumExprOrErr.takeError();
+  Expressions.resize(*NumExprOrErr);
+  for (auto &[LHS, RHS] : Expressions) {
+    if (auto E = LHS.decode(Data))
+      return std::move(E);
+    if (auto E = RHS.decode(Data))
+      return std::move(E);
+  }
+
+  for (unsigned FileIdx = 0; FileIdx != *NumFilesOrErr; ++FileIdx) {
+    auto NumRegionsOrErr = Data.getULEB128();
+    if (!NumRegionsOrErr)
+      return NumRegionsOrErr.takeError();
+    auto &File = Files.emplace_back();
+
+    // Decode subarray.
+    Data.LineStart = 0;
+    for (unsigned I = 0; I != *NumRegionsOrErr; ++I) {
+      auto &Rec = File.Recs.emplace_back();
+      if (auto E = Rec.decode(Data))
+        return std::move(E);
+    }
+  }
+
+  assert(Data.tell() == ExpectedEndOffset);
+  return Data.tell();
+}
+
 void CovMapTy::encode(raw_ostream &OS, endianness Endianness) const {
   auto [FilenamesRef, FilenamesBlob] = encodeFilenames();
 
@@ -219,6 +453,36 @@ void CovMapTy::encode(raw_ostream &OS, endianness Endianness) const {
   OS << FilenamesBlob;
 }
 
+Expected<uint64_t> CovMapTy::decode(const ArrayRef<uint8_t> Content,
+                                    uint64_t Offset, endianness Endianness) {
+  DecoderContext Data(Content, (Endianness == endianness::little));
+  Data.seek(Offset);
+
+#define COVMAP_HEADER(Type, LLVMType, Name, Initializer)                       \
+  static_assert(sizeof(Type) == sizeof(uint32_t));                             \
+  Type Name = Data.getU32();
+#include "llvm/ProfileData/InstrProfData.inc"
+  if (!Data)
+    return Data.takeError();
+  assert(NRecords == 0);
+  // +1: uint32_t FilenamesSize;
+  assert(CoverageSize == 0);
+  this->Version = Version;
+
+  // Decode Body -- Filenames.
+  StringRef FnBlob = Data.getBytes(FilenamesSize);
+  if (!Data)
+    return Data.takeError();
+  this->FilenamesRef = MD5Hash(FnBlob);
+  this->Filenames.emplace();
+  if (auto E = RawCoverageFilenamesReader(FnBlob, *this->Filenames)
+                   .read(static_cast<CovMapVersion>(Version)))
+    return E;
+
+  Offset = Data.tell();
+  return Offset;
+}
+
 void CounterTy::mapping(llvm::yaml::IO &IO) {
   IO.mapOptional("Tag", Tag);
   IO.mapOptional("Val", Val);
@@ -289,8 +553,7 @@ void llvm::yaml::ScalarEnumerationTraits<RecTy::ExtTagTy>::enumeration(
 namespace {
 
 struct PrfNamesSection : ELFYAML::CovMapSectionBase {
-  using PrfNamesTy = SmallVector<std::string>;
-  SmallVector<PrfNamesTy, 1> PrfNames;
+  InstrProfSymtab::PrfNamesChunksTy PrfNames;
 
   PrfNamesSection() { Name = "__llvm_prf_names"; }
   static bool nameMatches(StringRef Name) { return Name == "__llvm_prf_names"; }
@@ -328,6 +591,26 @@ struct CovMapSection : ELFYAML::CovMapSectionBase {
     IO.mapOptional("CovMap", CovMaps);
   }
 
+  Error decode(ArrayRef<uint8_t> Blob, unsigned AddressAlign,
+               endianness Endianness) {
+    uint64_t Offset = 0;
+
+    while (true) {
+      Offset = llvm::alignTo(Offset, AddressAlign);
+      if (Offset >= Blob.size()) {
+        break;
+      }
+      auto &CovMap = CovMaps.emplace_back();
+      auto Result = CovMap.decode(Blob, Offset, Endianness);
+      if (!Result) {
+        return Result.takeError();
+      }
+      Offset = *Result;
+    }
+
+    return Error::success();
+  }
+
   Error encode(raw_ostream &OS, endianness Endianness) const override {
     auto BaseOffset = OS.tell();
     for (const auto &CovMap : CovMaps) {
@@ -354,6 +637,28 @@ struct CovFunSection : ELFYAML::CovMapSectionBase {
     IO.mapOptional("CovFun", CovFuns);
   }
 
+  static Expected<std::vector<CovFunTy>> decode(ArrayRef<uint8_t> CovFunA,
+                                                unsigned AddressAlign,
+                                                endianness Endianness) {
+    std::vector<CovFunTy> CovFuns;
+    uint64_t Offset = 0;
+
+    while (true) {
+      Offset = llvm::alignTo(Offset, AddressAlign);
+      if (Offset >= CovFunA.size())
+        break;
+
+      auto &CovFun = CovFuns.emplace_back();
+      auto Result = CovFun.decode(CovFunA, Offset, Endianness);
+      if (!Result)
+        return Result.takeError();
+
+      Offset = *Result;
+    }
+
+    return std::move(CovFuns);
+  }
+
   Error encode(raw_ostream &OS, endianness Endianness) const override {
     auto BaseOffset = OS.tell();
     for (auto [I, CovFun] : enumerate(CovFuns)) {
@@ -371,6 +676,7 @@ class CovMapFilenamesResolver {
 
 protected:
   DenseMap<uint64_t, struct CovMapTy *> CovMapByRef;
+  std::vector<CovMapTy> TempCovMaps; // For Decoder
 
 public:
   void collectCovMap(std::vector<CovMapTy> &CovMaps) {
@@ -378,6 +684,11 @@ class CovMapFilenamesResolver {
       CovMapByRef[CovMap.FilenamesRef] = &CovMap;
   }
 
+  void moveAndCollectCovMap(std::vector<CovMapTy> &&CovMaps) {
+    TempCovMaps = std::move(CovMaps);
+    collectCovMap(TempCovMaps);
+  }
+
   void collectCovFunFilenames(std::vector<CovFunTy> &CovFuns) {
     for (auto &CovFun : CovFuns) {
       auto &Filenames = FilenamesByCovMap[CovFun.FilenamesRef];
@@ -445,6 +756,59 @@ class CovMapFilenamesResolver {
   }
 };
 
+class DecoderImpl : public Decoder, CovMapFilenamesResolver {
+  std::unique_ptr<InstrProfSymtab> ProfileNames;
+
+public:
+  DecoderImpl(endianness Endianness, bool CovMapEnabled)
+      : Decoder(Endianness), ProfileNames(std::make_unique<InstrProfSymtab>()) {
+    enabled = CovMapEnabled;
+  }
+
+  Error acquire(unsigned AddressAlign, StringRef Name,
+                ArrayRef<uint8_t> Content) override {
+    // Don't register anything.
+    if (!enabled)
+      return Error::success();
+
+    if (CovMapSection::nameMatches(Name)) {
+      // Decode CovMaps in advance, since only CovMap knows its Version.
+      // CovMaps is restored (into CovMapSection) later.
+      auto TempCovMap = std::make_unique<CovMapSection>();
+      if (auto E = TempCovMap->decode(Content, AddressAlign, Endianness))
+        return E;
+      moveAndCollectCovMap(std::move(TempCovMap->CovMaps));
+    }
+
+    return Error::success();
+  }
+
+  Error make(ELFYAML::CovMapSectionBase *Base,
+             ArrayRef<uint8_t> Content) override {
+    if (auto *S = dyn_cast<CovMapSection>(Base)) {
+      // Store predecoded CovMaps.
+      S->CovMaps = std::move(TempCovMaps);
+      return Error::success();
+    } else if (auto *S = dyn_cast<PrfNamesSection>(Base)) {
+      // Decode PrfNames in advance since CovFun depends on it.
+      auto PrfNamesOrErr = ProfileNames->createAndGetList(Content);
+      if (!PrfNamesOrErr)
+        return PrfNamesOrErr.takeError();
+      S->PrfNames = std::move(*PrfNamesOrErr);
+      return Error::success();
+    } else if (auto *S = dyn_cast<CovFunSection>(Base)) {
+      auto CovFunsOrErr =
+          CovFunSection::decode(Content, S->AddressAlign, Endianness);
+      if (!CovFunsOrErr)
+        return CovFunsOrErr.takeError();
+      S->CovFuns = std::move(*CovFunsOrErr);
+      return Error::success();
+    }
+
+    llvm_unreachable("Unknown Section");
+  }
+};
+
 class EncoderImpl : public Encoder, CovMapFilenamesResolver {
 public:
   EncoderImpl(endianness Endianness) : Encoder(Endianness) {}
@@ -461,6 +825,11 @@ class EncoderImpl : public Encoder, CovMapFilenamesResolver {
 };
 } // namespace
 
+std::unique_ptr<Decoder> Decoder::get(endianness Endianness,
+                                      bool CovMapEnabled) {
+  return std::make_unique<DecoderImpl>(Endianness, CovMapEnabled);
+}
+
 std::unique_ptr<Encoder> Encoder::get(endianness Endianness) {
   return std::make_unique<EncoderImpl>(Endianness);
 }
@@ -482,4 +851,4 @@ covmap::make_unique(StringRef Name) {
   return nullptr;
 }
 
-LLVM_YAML_IS_SEQUENCE_VECTOR(PrfNamesSection::PrfNamesTy)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::InstrProfSymtab::PrfNamesTy)
diff --git a/llvm/lib/ProfileData/InstrProf.cpp b/llvm/lib/ProfileData/InstrProf.cpp
index 819ddd02a24ce..93714a5a05a0d 100644
--- a/llvm/lib/ProfileData/InstrProf.cpp
+++ b/llvm/lib/ProfileData/InstrProf.cpp
@@ -535,9 +535,9 @@ Error InstrProfSymtab::addVTableWithName(GlobalVariable &VTable,
 /// \c NameStrings is a string composed of one of more possibly encoded
 /// sub-strings. The substrings are separated by 0 or more zero bytes. This
 /// method decodes the string and calls `NameCallback` for each substring.
-static Error
-readAndDecodeStrings(StringRef NameStrings,
-                     std::function<Error(StringRef)> NameCallback) {
+static Error readAndDecodeStrings(
+    StringRef NameStrings, std::function<Error(StringRef)> NameCallback,
+    std::function<void(bool)> ChunkCallback = [](bool) {}) {
   const uint8_t *P = NameStrings.bytes_begin();
   const uint8_t *EndP = NameStrings.bytes_end();
   while (P < EndP) {
@@ -567,6 +567,7 @@ readAndDecodeStrings(StringRef NameStrings,
       P += UncompressedSize;
     }
     // Now parse the name strings.
+    ChunkCallback(IsCompressed);
     SmallVector<StringRef, 0> Names;
     NameStrings.split(Names, getInstrProfNameSeparator());
     for (StringRef &Name : Names)
@@ -585,6 +586,22 @@ Error InstrProfSymtab::create(StringRef NameStrings) {
       std::bind(&InstrProfSymtab::addFuncName, this, std::placeholders::_1));
 }
 
+Expected<InstrProfSymtab::PrfNamesChunksTy>
+InstrProfSymtab::createAndGetList(ArrayRef<uint8_t> Content) {
+  PrfNamesChunksTy Result;
+  PrfNamesTy *ArrayP = nullptr;
+  if (auto E = readAndDecodeStrings(
+          StringRef(reinterpret_cast<const char *>(Content.data()),
+                    Content.size()),
+          [&](StringRef Name) {
+            ArrayP->emplace_back(Name.str());
+            return addFuncName(Name);
+          },
+          [&](bool IsCompressed) { ArrayP = &Result.emplace_back(); }))
+    return E;
+  return Result;
+}
+
 Error InstrProfSymtab::create(StringRef FuncNameStrings,
                               StringRef VTableNameStrings) {
   if (Error E = readAndDecodeStrings(FuncNameStrings,
diff --git a/llvm/test/tools/obj2yaml/ELF/covmap-be.yaml b/llvm/test/tools/obj2yaml/ELF/covmap-be.yaml
index b97782153192b..8423f1ad5f765 100644
--- a/llvm/test/tools/obj2yaml/ELF/covmap-be.yaml
+++ b/llvm/test/tools/obj2yaml/ELF/covmap-be.yaml
@@ -1,6 +1,8 @@
 # RUN: yaml2obj %s -o %t.o
 # RUN: obj2yaml %t.o > %t.plain.yaml
+# RUN: obj2yaml --covmap-raw %t.o > %t.raw.yaml
 # RUN: yaml2obj %t.plain.yaml -o - | cmp %t.o -
+# RUN: yaml2obj %t.raw.yaml -o - | cmp %t.o -
 
 # FIXME: This is synthetically created. s/ELFDATA2LSB/ELF2DATAMSB/ s/EM_X86_64/EM_PPC64/
 --- !ELF
diff --git a/llvm/test/tools/obj2yaml/ELF/covmap.yaml b/llvm/test/tools/obj2yaml/ELF/covmap.yaml
index 9ec8987c6f93d..db30d373d5be1 100644
--- a/llvm/test/tools/obj2yaml/ELF/covmap.yaml
+++ b/llvm/test/tools/obj2yaml/ELF/covmap.yaml
@@ -1,6 +1,8 @@
 # RUN: yaml2obj %s -o %t.o
 # RUN: obj2yaml %t.o | tee %t.plain.yaml | FileCheck %s --check-prefixes=CHECK,PLAIN
+# RUN: obj2yaml --covmap-raw %t.o | tee %t.raw.yaml | FileCheck %s --check-prefixes=CHECK,COVMAP,RAWONLY,RAW,DLOC
 # RUN: yaml2obj %t.plain.yaml -o - | cmp %t.o -
+# RUN: yaml2obj %t.raw.yaml -o - | cmp %t.o -
 
 --- !ELF
 FileHeader:
diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp
index b1c8032ea2192..50fd92e24aa3d 100644
--- a/llvm/tools/obj2yaml/elf2yaml.cpp
+++ b/llvm/tools/obj2yaml/elf2yaml.cpp
@@ -11,8 +11,10 @@
 #include "llvm/ADT/Twine.h"
 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
 #include "llvm/Object/ELFObjectFile.h"
+#include "llvm/ObjectYAML/CovMap.h"
 #include "llvm/ObjectYAML/DWARFYAML.h"
 #include "llvm/ObjectYAML/ELFYAML.h"
+#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/DataExtractor.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -21,6 +23,10 @@
 
 using namespace llvm;
 
+static cl::opt<bool> CovMapRaw("covmap-raw",
+                               cl::desc("Dump raw YAML in Coverage Map."),
+                               cl::cat(Cat));
+
 namespace {
 
 template <class ELFT>
@@ -97,6 +103,8 @@ class ELFDumper {
   dumpStackSizesSection(const Elf_Shdr *Shdr);
   Expected<ELFYAML::BBAddrMapSection *>
   dumpBBAddrMapSection(const Elf_Shdr *Shdr);
+  Expected<ELFYAML::Section *> dumpCovMap(const Elf_Shdr *Shdr, StringRef Name,
+                                          covmap::Decoder *CovMapDecoder);
   Expected<ELFYAML::RawContentSection *>
   dumpPlaceholderSection(const Elf_Shdr *Shdr);
 
@@ -580,6 +588,30 @@ ELFDumper<ELFT>::dumpSections() {
     return Error::success();
   };
 
+  auto CovMapDecoder = covmap::Decoder::get(ELFT::Endianness, CovMapRaw);
+  if (covmap::Decoder::enabled) {
+    // Look up covmap-related sections in advance.
+    for (const auto &Sec : Sections) {
+      if (Sec.sh_type != ELF::SHT_PROGBITS)
+        continue;
+
+      auto NameOrErr = Obj.getSectionName(Sec);
+      if (!NameOrErr)
+        return NameOrErr.takeError();
+
+      if (!covmap::nameMatches(*NameOrErr))
+        continue;
+
+      auto ContentOrErr = Obj.getSectionContents(Sec);
+      if (!ContentOrErr)
+        return ContentOrErr.takeError();
+
+      if (auto E = CovMapDecoder->acquire(Sec.sh_addralign, *NameOrErr,
+                                          *ContentOrErr))
+        return std::move(E);
+    }
+  }
+
   auto GetDumper = [this](unsigned Type)
       -> std::function<Expected<ELFYAML::Chunk *>(const Elf_Shdr *)> {
     if (Obj.getHeader().e_machine == ELF::EM_ARM && Type == ELF::SHT_ARM_EXIDX)
@@ -661,6 +693,10 @@ ELFDumper<ELFT>::dumpSections() {
         if (Error E = Add(dumpStackSizesSection(&Sec)))
           return std::move(E);
         continue;
+      } else if (covmap::Decoder::enabled && covmap::nameMatches(*NameOrErr)) {
+        if (Error E = Add(dumpCovMap(&Sec, *NameOrErr, CovMapDecoder.get())))
+          return std::move(E);
+        continue;
       }
     }
 
@@ -1662,6 +1698,26 @@ ELFDumper<ELFT>::dumpMipsABIFlags(const Elf_Shdr *Shdr) {
   return S.release();
 }
 
+template <class ELFT>
+Expected<ELFYAML::Section *>
+ELFDumper<ELFT>::dumpCovMap(const Elf_Shdr *Shdr, StringRef Name,
+                            covmap::Decoder *CovMapDecoder) {
+  auto S = covmap::make_unique(Name);
+  assert(S);
+
+  if (Error E = dumpCommonSection(Shdr, *S))
+    return std::move(E);
+
+  auto ContentOrErr = Obj.getSectionContents(*Shdr);
+  if (!ContentOrErr)
+    return ContentOrErr.takeError();
+
+  if (auto E = CovMapDecoder->make(S.get(), *ContentOrErr))
+    return std::move(E);
+
+  return S.release();
+}
+
 template <class ELFT>
 static Error elf2yaml(raw_ostream &Out, const object::ELFFile<ELFT> &Obj,
                       std::unique_ptr<DWARFContext> DWARFCtx) {
@@ -1671,7 +1727,8 @@ static Error elf2yaml(raw_ostream &Out, const object::ELFFile<ELFT> &Obj,
     return YAMLOrErr.takeError();
 
   std::unique_ptr<ELFYAML::Object> YAML(YAMLOrErr.get());
-  yaml::Output Yout(Out);
+  yaml::Output Yout(Out, nullptr, /*WrapColumn*/
+                    (covmap::Decoder::enabled ? 160 : 70));
   Yout << *YAML;
 
   return Error::success();
diff --git a/llvm/tools/obj2yaml/obj2yaml.cpp b/llvm/tools/obj2yaml/obj2yaml.cpp
index 63c8cc55ee2d4..31018161af531 100644
--- a/llvm/tools/obj2yaml/obj2yaml.cpp
+++ b/llvm/tools/obj2yaml/obj2yaml.cpp
@@ -20,7 +20,7 @@
 using namespace llvm;
 using namespace llvm::object;
 
-static cl::OptionCategory Cat("obj2yaml Options");
+cl::OptionCategory Cat("obj2yaml Options");
 
 static cl::opt<std::string>
     InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-"));
diff --git a/llvm/tools/obj2yaml/obj2yaml.h b/llvm/tools/obj2yaml/obj2yaml.h
index 03d7db5317acd..ee34919962575 100644
--- a/llvm/tools/obj2yaml/obj2yaml.h
+++ b/llvm/tools/obj2yaml/obj2yaml.h
@@ -16,11 +16,15 @@
 #include "llvm/Object/Minidump.h"
 #include "llvm/Object/Wasm.h"
 #include "llvm/Object/XCOFFObjectFile.h"
+#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/MemoryBufferRef.h"
 #include "llvm/Support/raw_ostream.h"
 #include <system_error>
 
 enum RawSegments : unsigned { none = 0, data = 1, linkedit = 1 << 1 };
+
+extern llvm::cl::OptionCategory Cat;
+
 std::error_code coff2yaml(llvm::raw_ostream &Out,
                           const llvm::object::COFFObjectFile &Obj);
 llvm::Error elf2yaml(llvm::raw_ostream &Out,



More information about the llvm-branch-commits mailing list