diff --git a/llvm/include/llvm/ObjectYAML/CovMap.h b/llvm/include/llvm/ObjectYAML/CovMap.h index 913cb6060f7a8..644ea38a08364 100644 --- a/llvm/include/llvm/ObjectYAML/CovMap.h +++ b/llvm/include/llvm/ObjectYAML/CovMap.h @@ -16,16 +16,18 @@ // // - llvm::covmap // -// Provides YAML encoder for coverage map. +// Provides YAML encoder and decoder for coverage map. // //===----------------------------------------------------------------------===// #ifndef LLVM_OBJECTYAML_COVMAP_H #define LLVM_OBJECTYAML_COVMAP_H +#include "llvm/ADT/ArrayRef.h" #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 #include @@ -41,6 +43,8 @@ class raw_ostream; namespace llvm::coverage::yaml { +struct DecoderContext; + /// Base Counter, corresponding to coverage::Counter. struct CounterTy { enum TagTy : uint8_t { @@ -57,6 +61,14 @@ struct CounterTy { virtual void mapping(llvm::yaml::IO &IO); + uint64_t getExtTagVal() const { return (Tag == Zero && Val != 0 ? Val : 0); } + + /// 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; }; @@ -77,6 +89,8 @@ struct DecisionTy { void mapping(llvm::yaml::IO &IO); + Error decode(DecoderContext &Data); + void encode(raw_ostream &OS) const; }; @@ -110,6 +124,8 @@ struct RecTy : CounterTy { void mapping(llvm::yaml::IO &IO) override; + Error decode(DecoderContext &Data); + void encode(raw_ostream &OS) const; }; @@ -131,6 +147,10 @@ struct CovFunTy { void mapping(llvm::yaml::IO &IO); + /// Depends on CovMap and SymTab(IPSK_names) + Expected decode(const ArrayRef Content, uint64_t Offset, + endianness Endianness); + void encode(raw_ostream &OS, endianness Endianness) const; }; @@ -147,6 +167,9 @@ struct CovMapTy { void mapping(llvm::yaml::IO &IO); + Expected decode(const ArrayRef Content, uint64_t Offset, + endianness Endianness); + /// Encode Filenames. This is mostly used just to obtain FilenamesRef. std::pair encodeFilenames(bool Compress = false) const; @@ -201,6 +224,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 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 Content) = 0; + + /// Make contents on ELFYAML object. CovMap is predecoded. + virtual Error make(ELFYAML::CovMapSectionBase *Base, + ArrayRef Content) = 0; + + /// Suppress emission of CovMap unless enabled. + static bool enabled; +}; + /// Returns whether Name is interested. bool nameMatches(StringRef Name); 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; + using PrfNamesChunksTy = SmallVector; + + Expected createAndGetList(ArrayRef 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 3ad809f61bb0a..267ea0223659a 100644 --- a/llvm/lib/ObjectYAML/CovMap.cpp +++ b/llvm/lib/ObjectYAML/CovMap.cpp @@ -6,18 +6,22 @@ // //===----------------------------------------------------------------------===// // -// Implementations of CovMap and encoder. +// Implementations of CovMap, encoder, decoder. // //===----------------------------------------------------------------------===// #include "llvm/ObjectYAML/CovMap.h" +#include "llvm/ADT/ArrayRef.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" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" #include @@ -32,15 +36,70 @@ using namespace llvm; using namespace llvm::coverage::yaml; using namespace llvm::covmap; +bool Decoder::enabled; + +Decoder::~Decoder() {} + +// DataExtractor w/ single Cursor +struct coverage::yaml::DecoderContext : DataExtractor, DataExtractor::Cursor { + DecoderContext(const ArrayRef 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 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 { encodeULEB128(Tag | (Val << 2), OS); } +Error CounterTy::decodeOrTag(DecoderContext &Data) { + auto COrErr = Data.getULEB128(); + if (!COrErr) + return COrErr.takeError(); + Tag = static_cast(*COrErr & 0x03); + Val = (*COrErr >> 2); + return Error::success(); +} + +Error CounterTy::decode(DecoderContext &Data) { + if (auto E = decodeOrTag(Data)) + return E; + if (auto V = getExtTagVal()) + return make_error( + coveragemap_error::malformed, + "Counter::Zero shouldn't have the Val: 0x" + Twine::utohexstr(V)); + 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(raw_ostream &OS) const { if (Expansion) { encodeULEB128(4 + (*Expansion << 3), OS); @@ -77,6 +136,102 @@ void RecTy::encode(raw_ostream &OS) const { encodeULEB128(dLoc[3] | Gap, OS); } +Error RecTy::decode(DecoderContext &Data) { + auto getU16 = [&]() -> Expected { + auto ValOrErr = Data.getULEB128(); + if (!ValOrErr) + return ValOrErr.takeError(); + if (*ValOrErr > 0x7FFF + 1) + return make_error(coveragemap_error::malformed, + "MC/DC index is out of range: 0x" + + Twine::utohexstr(*ValOrErr)); + return static_cast(*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; + auto V = getExtTagVal(); + if (V == 0) { + // Compatible to CounterTy + } else if (V & 1u) { + Expansion = (V >> 1); + } else { + auto Tag = (V >> 1); + 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( + 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(); + + auto CSOrErr = Data.getULEB128(); + if (!CSOrErr) + return CSOrErr.takeError(); + + auto NLOrErr = Data.getULEB128(); + if (!NLOrErr) + return NLOrErr.takeError(); + + 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}; + + return Error::success(); +} + void CovFunTy::encode(raw_ostream &OS, endianness Endianness) const { // Encode Body in advance since DataSize should be known. std::string Body; @@ -125,6 +280,69 @@ CovMapTy::encodeFilenames(bool Compress) const { return {llvm::IndexedInstrProf::ComputeHash(FilenamesBlob), FilenamesBlob}; } +Expected CovFunTy::decode(const ArrayRef 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. + 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. + 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(); @@ -149,6 +367,35 @@ void CovMapTy::encode(raw_ostream &OS, endianness Endianness) const { OS << FilenamesBlob; } +Expected CovMapTy::decode(const ArrayRef 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)); \ + [[maybe_unused]] 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); + if (auto E = RawCoverageFilenamesReader(FnBlob, Filenames) + .read(static_cast(Version))) + return E; + + Offset = Data.tell(); + return Offset; +} + void CounterTy::mapping(llvm::yaml::IO &IO) { IO.mapRequired("Tag", Tag); IO.mapRequired("Val", Val); @@ -210,8 +457,7 @@ void llvm::yaml::ScalarEnumerationTraits::enumeration( namespace { struct PrfNamesSection : ELFYAML::CovMapSectionBase { - using PrfNamesTy = SmallVector; - SmallVector PrfNames; + InstrProfSymtab::PrfNamesChunksTy PrfNames; PrfNamesSection() { Name = "__llvm_prf_names"; } static bool nameMatches(StringRef Name) { return Name == "__llvm_prf_names"; } @@ -249,6 +495,26 @@ struct CovMapSection : ELFYAML::CovMapSectionBase { IO.mapOptional("CovMap", CovMaps); } + Error decode(ArrayRef 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) { @@ -275,6 +541,28 @@ struct CovFunSection : ELFYAML::CovMapSectionBase { IO.mapOptional("CovFun", CovFuns); } + static Expected> decode(ArrayRef CovFunA, + unsigned AddressAlign, + endianness Endianness) { + std::vector 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)) { @@ -285,8 +573,67 @@ struct CovFunSection : ELFYAML::CovMapSectionBase { return Error::success(); } }; + +class DecoderImpl : public Decoder { + std::unique_ptr ProfileNames; + std::vector TempCovMaps; + +public: + DecoderImpl(endianness Endianness, bool CovMapEnabled) + : Decoder(Endianness), ProfileNames(std::make_unique()) { + enabled = CovMapEnabled; + } + + Error acquire(unsigned AddressAlign, StringRef Name, + ArrayRef 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(); + if (auto E = TempCovMap->decode(Content, AddressAlign, Endianness)) + return E; + TempCovMaps = std::move(TempCovMap->CovMaps); + } + + return Error::success(); + } + + Error make(ELFYAML::CovMapSectionBase *Base, + ArrayRef Content) override { + if (auto *S = dyn_cast(Base)) { + // Store predecoded CovMaps. + S->CovMaps = std::move(TempCovMaps); + return Error::success(); + } else if (auto *S = dyn_cast(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(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"); + } +}; } // namespace +std::unique_ptr Decoder::get(endianness Endianness, + bool CovMapEnabled) { + return std::make_unique(Endianness, CovMapEnabled); +} + bool covmap::nameMatches(StringRef Name) { return (PrfNamesSection::nameMatches(Name) || CovMapSection::nameMatches(Name) || CovFunSection::nameMatches(Name)); @@ -304,4 +651,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 NameCallback) { +static Error readAndDecodeStrings( + StringRef NameStrings, std::function NameCallback, + std::function 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 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::createAndGetList(ArrayRef Content) { + PrfNamesChunksTy Result; + PrfNamesTy *ArrayP = nullptr; + if (auto E = readAndDecodeStrings( + StringRef(reinterpret_cast(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 465808442fb7c..4456bbbcb4a86 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 8edd884849cc5..09bcf86b44f47 100644 --- a/llvm/test/tools/obj2yaml/ELF/covmap.yaml +++ b/llvm/test/tools/obj2yaml/ELF/covmap.yaml @@ -1,6 +1,9 @@ # 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: sed -E '/^(#.*)?$/d' %s | diff %t.raw.yaml - # RUN: yaml2obj %t.plain.yaml -o - | cmp %t.o - +# RUN: yaml2obj %t.raw.yaml -o - | cmp %t.o - --- !ELF FileHeader: @@ -17,12 +20,18 @@ Sections: Flags: [ SHF_GNU_RETAIN ] AddressAlign: 0x8 # PLAIN: Content: +# COVMAP: CovFun: CovFun: +# RAW:- NameRef: 0xA72DB7A33048E6A3 - NameRef: 0xA72DB7A33048E6A3 +# COVMAP: FuncHash: 0xAFC772D567676299 FuncHash: 0xAFC772D567676299 FilenamesRef: 0x70DA2CAFD8CE198E +# RAW: FileIDs: [ 1, 1 ] FileIDs: [ 1, 1 ] +# COVMAP: Expressions: Expressions: +# RAW: - [ { Tag: Ref, Val: 0 }, { Tag: Ref, Val: 1 } ] - [ { Tag: Ref, Val: 0 }, { Tag: Ref, Val: 1 } ] - [ { Tag: Add, Val: 0 }, { Tag: Ref, Val: 2 } ] - [ { Tag: Add, Val: 0 }, { Tag: Ref, Val: 2 } ] @@ -31,31 +40,56 @@ Sections: - [ { Tag: Ref, Val: 0 }, { Tag: Ref, Val: 2 } ] - [ { Tag: Ref, Val: 0 }, { Tag: Ref, Val: 2 } ] - [ { Tag: Add, Val: 0 }, { Tag: Ref, Val: 5 } ] +# COVMAP: Files: Files: +# COVMAP: Regions: - Regions: +# RAWONLY-NOT: Loc: [ 3, 17, 14, 2 ] +# DLOC: dLoc: [ 3, 17, 11, 2 ] +# RAW: Tag: Ref, Val: 0 - { dLoc: [ 3, 17, 11, 2 ], Tag: Ref, Val: 0 } +# DLOC: dLoc: [ 3, 6, 5, 4 ] +# RAW: Tag: Add, Val: 0 - { dLoc: [ 3, 6, 5, 4 ], Tag: Add, Val: 0 } +# RAW: Tag: Zero, Val: 3, +# COVMAP: Expansion: 1 - { dLoc: [ 1, 9, 0, 13 ], Tag: Zero, Val: 3, Expansion: 1 } +# COVMAP: isGap: true +# RAW: Tag: Ref, Val: 2 - { dLoc: [ 0, 23, 1, 7 ], isGap: true, Tag: Ref, Val: 2 } - { dLoc: [ 1, 7, 0, 16 ], Tag: Ref, Val: 2 } +# COVMAP: isGap: true +# RAW: Tag: Sub, Val: 3 - { dLoc: [ 0, 17, 2, 5 ], isGap: true, Tag: Sub, Val: 3 } +# RAW: Tag: Zero, Val: 4, +# COVMAP: ExtTag: Skip - { dLoc: [ 1, 1, 0, 1 ], Tag: Zero, Val: 4, ExtTag: Skip } - { dLoc: [ 1, 5, 1, 4 ], Tag: Sub, Val: 3 } - { dLoc: [ 1, 12, 0, 17 ], Tag: Sub, Val: 3 } +# RAW: Tag: Zero, Val: 8, ExtTag: Branch, Branch: [ { Tag: Ref, Val: 1 }, { Tag: Sub, Val: 6 } ] } - { dLoc: [ 0, 12, 0, 17 ], Tag: Zero, Val: 8, ExtTag: Branch, Branch: [ { Tag: Ref, Val: 1 }, { Tag: Sub, Val: 6 } ] } - { dLoc: [ 0, 19, 1, 3 ], isGap: true, Tag: Sub, Val: 6 } - { dLoc: [ 1, 3, 0, 11 ], Tag: Sub, Val: 6 } +# DLOC: dLoc: [ 1, 3, 0, 12 ] +# COVMAP: Tag: Zero +# RAW: Val: 0 - { dLoc: [ 0, 12, 1, 3 ], isGap: true, Tag: Zero, Val: 0 } - { dLoc: [ 1, 3, 0, 12 ], Tag: Zero, Val: 0 } +# COVMAP: Regions: - Regions: - { dLoc: [ 1, 17, 0, 41 ], Tag: Add, Val: 0 } - { dLoc: [ 0, 18, 0, 32 ], Tag: Add, Val: 0 } +# RAW: Tag: Zero, Val: 10, ExtTag: Decision +# COVMAP: Decision: { BIdx: 5, NCond: 3 } } - { dLoc: [ 0, 18, 0, 40 ], Tag: Zero, Val: 10, ExtTag: Decision, Decision: { BIdx: 5, NCond: 3 } } - { dLoc: [ 0, 19, 0, 22 ], Tag: Add, Val: 0 } +# RAW: Tag: Zero, Val: 12, ExtTag: MCDCBranch, Branch: [ { Tag: Sub, Val: 7 }, { Tag: Ref, Val: 5 } ] +# COVMAP: MCDC: [ 1, 2, 3 ] - { dLoc: [ 0, 19, 0, 22 ], Tag: Zero, Val: 12, ExtTag: MCDCBranch, Branch: [ { Tag: Sub, Val: 7 }, { Tag: Ref, Val: 5 } ], MCDC: [ 1, 2, 3 ] } - { dLoc: [ 0, 26, 0, 31 ], Tag: Ref, Val: 5 } - { dLoc: [ 0, 26, 0, 31 ], Tag: Zero, Val: 12, ExtTag: MCDCBranch, Branch: [ { Tag: Zero, Val: 0 }, { Tag: Ref, Val: 6 } ], MCDC: [ 3, 2, 0 ] } - { dLoc: [ 0, 36, 0, 40 ], Tag: Ref, Val: 3 } +# DLOC: dLoc: [ 0, 36, 0, 40 ] - { dLoc: [ 0, 36, 0, 40 ], Tag: Zero, Val: 12, ExtTag: MCDCBranch, Branch: [ { Tag: Ref, Val: 4 }, { Tag: Zero, Val: 0 } ], MCDC: [ 2, 0, 0 ] } # CHECK: __llvm_covmap - Name: __llvm_covmap @@ -63,11 +97,17 @@ Sections: Flags: [ SHF_GNU_RETAIN ] AddressAlign: 0x8 # PLAIN: Content: +# COVMAP: CovMap: CovMap: +# COVMAP: - FilenamesRef: 0x70DA2CAFD8CE198E - FilenamesRef: 0x70DA2CAFD8CE198E +# COVMAP: Version: Version: 6 +# RAWONLY: Filenames: Filenames: +# RAWONLY: - '' - '' +# RAWONLY: - func.cpp - func.cpp # CHECK: __llvm_prf_names - Name: __llvm_prf_names @@ -78,7 +118,9 @@ Sections: # PLAIN-NOT: CovMap: # PLAIN-NOT: PrfNames: # PLAIN: Content: +# COVMAP: PrfNames PrfNames: +# COVMAP: - - _Z4funci - - _Z4funci - Type: SectionHeaderTable Sections: diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp index b1c8032ea2192..aba494c0f1173 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 CovMapRaw("covmap-raw", + cl::desc("Dump raw YAML in Coverage Map."), + cl::cat(Cat)); + namespace { template @@ -97,6 +103,8 @@ class ELFDumper { dumpStackSizesSection(const Elf_Shdr *Shdr); Expected dumpBBAddrMapSection(const Elf_Shdr *Shdr); + Expected dumpCovMap(const Elf_Shdr *Shdr, StringRef Name, + covmap::Decoder *CovMapDecoder); Expected dumpPlaceholderSection(const Elf_Shdr *Shdr); @@ -580,6 +588,30 @@ ELFDumper::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(const Elf_Shdr *)> { if (Obj.getHeader().e_machine == ELF::EM_ARM && Type == ELF::SHT_ARM_EXIDX) @@ -661,6 +693,10 @@ ELFDumper::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::dumpMipsABIFlags(const Elf_Shdr *Shdr) { return S.release(); } +template +Expected +ELFDumper::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 static Error elf2yaml(raw_ostream &Out, const object::ELFFile &Obj, std::unique_ptr DWARFCtx) { @@ -1671,7 +1727,8 @@ static Error elf2yaml(raw_ostream &Out, const object::ELFFile &Obj, return YAMLOrErr.takeError(); std::unique_ptr YAML(YAMLOrErr.get()); - yaml::Output Yout(Out); + yaml::Output Yout(Out, nullptr, /*WrapColumn*/ + (covmap::Decoder::enabled ? 200 : 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 InputFilename(cl::Positional, cl::desc(""), 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 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,