diff --git a/llvm/include/llvm/ObjectYAML/CovMap.h b/llvm/include/llvm/ObjectYAML/CovMap.h index 644ea38a08364..697145154d9cc 100644 --- a/llvm/include/llvm/ObjectYAML/CovMap.h +++ b/llvm/include/llvm/ObjectYAML/CovMap.h @@ -24,6 +24,7 @@ #define LLVM_OBJECTYAML_COVMAP_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ObjectYAML/ELFYAML.h" #include "llvm/Support/Endian.h" @@ -35,14 +36,67 @@ #include #include #include +#include #include namespace llvm { +class InstrProfSymtab; class raw_ostream; } // namespace llvm namespace llvm::coverage::yaml { +/// This works like vector container but can be replaced with +/// MutableArrayRef. See also SequenceTraits. +template > class VectorOrRef { + using Ref = MutableArrayRef; + + /// Holds vector type initially. + std::variant Array; + +public: + // FIXME: Iterator impl is minimal easy. + using iterator = T *; + + iterator begin() { + if (auto *V = std::get_if(&Array)) + return &V->front(); + return &std::get(Array).front(); + } + + iterator end() { + if (auto *V = std::get_if(&Array)) + return &V->back() + 1; + return &std::get(Array).back() + 1; + } + + size_t size() const { + if (const auto *V = std::get_if(&Array)) + return V->size(); + return std::get(Array).size(); + } + + T &operator[](int Idx) { + if (auto *V = std::get_if(&Array)) + return (*V)[Idx]; + return std::get(Array)[Idx]; + } + + void resize(size_t Size) { std::get(Array).resize(Size); } + + VectorOrRef() = default; + + /// Initialize with MutableArrayRef. + VectorOrRef(Ref &&Tmp) : Array(std::move(Tmp)) {} +}; + +/// Options for Decoder. +struct DecoderParam { + bool Detailed; ///< Generate and show processed records. + bool Raw; ///< Show raw data oriented records. + bool dLoc; ///< Show raw dLoc (differential Loc). +}; + struct DecoderContext; /// Base Counter, corresponding to coverage::Counter. @@ -54,14 +108,24 @@ struct CounterTy { Add, }; - TagTy Tag; - uint64_t Val; + /// Optional in detailed view, since most Tag can be determined from + /// other optional fields. + std::optional Tag; + + /// Internal use. + std::optional Val; + + std::optional RefOpt; + std::optional SubOpt; + std::optional AddOpt; virtual ~CounterTy() {} virtual void mapping(llvm::yaml::IO &IO); - uint64_t getExtTagVal() const { return (Tag == Zero && Val != 0 ? Val : 0); } + uint64_t getExtTagVal() const { + return (((!Tag || *Tag == Zero) && Val && *Val != 0) ? *Val : 0); + } /// Holds Val for extensions. Error decodeOrTag(DecoderContext &Data); @@ -120,28 +184,35 @@ struct RecTy : CounterTy { /// Stored in ColumnEnd:31. std::optional isGap; - LocTy dLoc; ///< Differential line numbers. + std::optional Loc; ///< Absolute line numbers. + std::optional dLoc; ///< Differential line numbers. void mapping(llvm::yaml::IO &IO) override; Error decode(DecoderContext &Data); - void encode(raw_ostream &OS) const; + void encode(uint64_t &StartLoc, raw_ostream &OS) const; }; /// {NumRecs, Recs...} struct FileRecsTy { + std::optional Index; ///< Shown in detailed view. + std::optional Filename; ///< Resolved by FileIDs. std::vector Recs; void mapping(llvm::yaml::IO &IO); }; +/// Key is FilenamesRef. +using CovMapByRefTy = llvm::DenseMap; + /// An element of CovFun array. struct CovFunTy { - llvm::yaml::Hex64 NameRef; ///< Hash value of the symbol. - llvm::yaml::Hex64 FuncHash; ///< Signature of this function. - llvm::yaml::Hex64 FilenamesRef; ///< Pointer to CovMap - std::vector FileIDs; ///< Resolved by CovMap + std::optional NameRef; ///< Hash value of the symbol. + std::optional FuncName; ///< Resolved by symtab. + llvm::yaml::Hex64 FuncHash; ///< Signature of this function. + llvm::yaml::Hex64 FilenamesRef; ///< Pointer to CovMap + std::optional> FileIDs; ///< Resolved by CovMap std::vector Expressions; std::vector Files; ///< 2-dimension array of Recs. @@ -149,7 +220,8 @@ struct CovFunTy { /// Depends on CovMap and SymTab(IPSK_names) Expected decode(const ArrayRef Content, uint64_t Offset, - endianness Endianness); + endianness Endianness, CovMapByRefTy &CovMapByRef, + InstrProfSymtab *SymTab, const DecoderParam &Param); void encode(raw_ostream &OS, endianness Endianness) const; }; @@ -160,24 +232,63 @@ struct CovMapTy { /// format. Calculate and store with Filenames. llvm::yaml::Hex64 FilenamesRef; - uint32_t Version; + std::optional Version; /// Raw Filenames (and storage of Files) - std::vector Filenames; + std::optional> Filenames; + + /// Since Version5: Filenames[0] is the working directory (or + /// zero-length string). Note that indices in CovFun::FileIDs is + /// base on Filenames. (Then, +0, as WD, is not expected to appear) + std::optional WD; + /// This may be ArrayRef in Decoder since Filenames has been + /// filled. On the other hand in Encoder, this should be a vector + /// since YAML parser doesn't endorse references. + std::optional> Files; void mapping(llvm::yaml::IO &IO); + bool useWD() const { return (!Version || *Version >= 4); } + StringRef getWD() const { return (WD ? *WD : StringRef()); } + Expected decode(const ArrayRef Content, uint64_t Offset, - endianness Endianness); + endianness Endianness, const DecoderParam &Param); + + /// Generate Accumulated list with WD. + /// Returns a single element {WD} if AccFiles is not given. + std::vector + generateAccFilenames(const std::optional> &AccFilesOpt = + std::nullopt) const; + /// Regenerate Filenames with WD. + /// Use Files if it is not None. Or given AccFiles is used. + void + regenerateFilenames(const std::optional> &AccFilesOpt); /// Encode Filenames. This is mostly used just to obtain FilenamesRef. - std::pair encodeFilenames(bool Compress = false) const; + std::pair encodeFilenames( + const std::optional> &AccFilesOpt = std::nullopt, + bool Compress = false) const; void encode(raw_ostream &OS, endianness Endianness) const; }; } // namespace llvm::coverage::yaml +namespace llvm::yaml { +template +struct SequenceTraits> { + static size_t size(IO &io, llvm::coverage::yaml::VectorOrRef &seq) { + return seq.size(); + } + static T &element(IO &, llvm::coverage::yaml::VectorOrRef &seq, + size_t index) { + if (index >= seq.size()) + seq.resize(index + 1); + return seq[index]; + } +}; +} // namespace llvm::yaml + LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::coverage::yaml::CovMapTy) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::coverage::yaml::CovFunTy) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::coverage::yaml::ExpressionTy) @@ -233,22 +344,46 @@ class Decoder { virtual ~Decoder(); /// Returns DecoderImpl. - static std::unique_ptr get(endianness Endianness, - bool CovMapEnabled); + static std::unique_ptr + get(endianness Endianness, const coverage::yaml::DecoderParam &Param); /// Called from the Sections loop in advance of the final dump. - /// Decoder predecodes CovMap for Version info. - virtual Error acquire(unsigned AddressAlign, StringRef Name, + /// Decoder predecodes Names and CovMap, and captures Contents of + /// CovFuns. + virtual Error acquire(uint64_t Offset, unsigned AddressAlign, StringRef Name, ArrayRef Content) = 0; - /// Make contents on ELFYAML object. CovMap is predecoded. - virtual Error make(ELFYAML::CovMapSectionBase *Base, - ArrayRef Content) = 0; + /// Called before the final dump after `acquire`. + /// Decode contents partially and resolve names. + virtual Error fixup() = 0; + + /// Make contents on ELFYAML object with predecoded contents. + virtual Error make(ELFYAML::CovMapSectionBase *Base, uint64_t Offset) = 0; /// Suppress emission of CovMap unless enabled. static bool enabled; }; +class Encoder { +protected: + endianness Endianness; + +public: + Encoder(endianness Endianness) : Endianness(Endianness) {} + virtual ~Encoder() {} + + /// Returns EncoderImpl. + static std::unique_ptr get(endianness Endianness); + + /// Called from the Sections loop. + virtual void collect(ELFYAML::Chunk *Chunk) = 0; + + /// Resolves names from CovFuns in advance of Emitter. It'd be too + /// late to resolve sections in Emitter since they are immutable + /// then. + virtual void fixup() = 0; +}; + /// Returns whether Name is interested. bool nameMatches(StringRef Name); diff --git a/llvm/lib/ObjectYAML/CovMap.cpp b/llvm/lib/ObjectYAML/CovMap.cpp index 267ea0223659a..e59046ade5423 100644 --- a/llvm/lib/ObjectYAML/CovMap.cpp +++ b/llvm/lib/ObjectYAML/CovMap.cpp @@ -12,8 +12,11 @@ #include "llvm/ObjectYAML/CovMap.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ObjectYAML/ELFYAML.h" +#include "llvm/ProfileData/Coverage/CoverageMapping.h" #include "llvm/ProfileData/Coverage/CoverageMappingReader.h" #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" #include "llvm/ProfileData/InstrProf.h" @@ -24,8 +27,10 @@ #include "llvm/Support/MD5.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" +#include #include #include +#include #include #include #include @@ -41,10 +46,15 @@ bool Decoder::enabled; Decoder::~Decoder() {} // DataExtractor w/ single Cursor -struct coverage::yaml::DecoderContext : DataExtractor, DataExtractor::Cursor { - DecoderContext(const ArrayRef Content, bool IsLE) +struct coverage::yaml::DecoderContext : DataExtractor, + DataExtractor::Cursor, + DecoderParam { + uint64_t LineStart = 0; + + DecoderContext(const ArrayRef Content, bool IsLE, + const DecoderParam &Param) : DataExtractor(Content, IsLE, /*AddressSize=*/0), - DataExtractor::Cursor(0) {} + DataExtractor::Cursor(0), DecoderParam(Param) {} bool eof() { return DataExtractor::eof(*this); } uint32_t getU32() { return DataExtractor::getU32(*this); } @@ -59,15 +69,52 @@ struct coverage::yaml::DecoderContext : DataExtractor, DataExtractor::Cursor { }; void CounterTy::encode(raw_ostream &OS) const { - encodeULEB128(Tag | (Val << 2), OS); + std::pair C; + if (RefOpt) + C = {Ref, *RefOpt}; + else if (SubOpt) + C = {Sub, *SubOpt}; + else if (AddOpt) + C = {Add, *AddOpt}; + else if (Tag && Val) + C = {*Tag, *Val}; + else if (Tag && *Tag == Zero) + C = {Zero, 0u}; + else + llvm_unreachable("Null value cannot be met"); + + encodeULEB128(C.first | (C.second << 2), OS); } Error CounterTy::decodeOrTag(DecoderContext &Data) { auto COrErr = Data.getULEB128(); if (!COrErr) return COrErr.takeError(); - Tag = static_cast(*COrErr & 0x03); - Val = (*COrErr >> 2); + auto T = static_cast(*COrErr & 0x03); + auto V = (*COrErr >> 2); + if (Data.Raw) { + Tag = T; + Val = V; + } else { + switch (T) { + case Zero: + if (V) + Val = V; + else + Tag = Zero; + break; + case Ref: + RefOpt = V; + break; + case Sub: + SubOpt = V; + break; + case Add: + AddOpt = V; + break; + } + } + return Error::success(); } @@ -100,7 +147,7 @@ Error DecisionTy::decode(DecoderContext &Data) { return Error::success(); } -void RecTy::encode(raw_ostream &OS) const { +void RecTy::encode(uint64_t &StartLoc, raw_ostream &OS) const { if (Expansion) { encodeULEB128(4 + (*Expansion << 3), OS); } else if (ExtTag && *ExtTag == Skip) { @@ -130,10 +177,18 @@ void RecTy::encode(raw_ostream &OS) const { assert((!isGap || *isGap) && "Don't set isGap=false"); uint32_t Gap = (isGap ? (1u << 31) : 0u); - encodeULEB128(dLoc[0], OS); - encodeULEB128(dLoc[1], OS); - encodeULEB128(dLoc[2], OS); - encodeULEB128(dLoc[3] | Gap, OS); + if (Loc) { + encodeULEB128((*Loc)[0] - StartLoc, OS); + encodeULEB128((*Loc)[1], OS); + encodeULEB128((*Loc)[2] - (*Loc)[0], OS); + encodeULEB128((*Loc)[3] | Gap, OS); + StartLoc = (*Loc)[0]; + } else { + encodeULEB128((*dLoc)[0], OS); + encodeULEB128((*dLoc)[1], OS); + encodeULEB128((*dLoc)[2], OS); + encodeULEB128((*dLoc)[3] | Gap, OS); + } } Error RecTy::decode(DecoderContext &Data) { @@ -165,8 +220,18 @@ Error RecTy::decode(DecoderContext &Data) { // Compatible to CounterTy } else if (V & 1u) { Expansion = (V >> 1); + if (!Data.Raw) { + assert(!this->Tag || *this->Tag == Zero); + this->Tag.reset(); + this->Val.reset(); + } } else { auto Tag = (V >> 1); + if (!Data.Raw) { + assert(!this->Tag || *this->Tag == Zero); + this->Tag.reset(); + this->Val.reset(); + } switch (Tag) { case Skip: ExtTag = Skip; // w/o Val @@ -174,12 +239,14 @@ Error RecTy::decode(DecoderContext &Data) { case Decision: if (auto E = DecisionOpt.emplace().decode(Data)) return E; - ExtTag = Decision; + if (Data.Raw) + ExtTag = Decision; break; case Branch: if (auto E = decodeBranch()) return E; - ExtTag = Branch; + if (Data.Raw) + ExtTag = Branch; break; case MCDCBranch: { if (auto E = decodeBranch()) @@ -194,7 +261,8 @@ Error RecTy::decode(DecoderContext &Data) { if (!I2OrErr) return I2OrErr.takeError(); MCDC = {*I0OrErr, *I1OrErr, *I2OrErr}; - ExtTag = MCDCBranch; + if (Data.Raw) + ExtTag = MCDCBranch; break; } default: @@ -208,6 +276,7 @@ Error RecTy::decode(DecoderContext &Data) { auto LSDeltaOrErr = Data.getULEB128(); if (!LSDeltaOrErr) return LSDeltaOrErr.takeError(); + Data.LineStart += *LSDeltaOrErr; auto CSOrErr = Data.getULEB128(); if (!CSOrErr) @@ -216,6 +285,7 @@ Error RecTy::decode(DecoderContext &Data) { auto NLOrErr = Data.getULEB128(); if (!NLOrErr) return NLOrErr.takeError(); + auto LineEnd = Data.LineStart + *NLOrErr; auto CEOrErr = Data.getULEB128(); if (!CEOrErr) @@ -228,6 +298,7 @@ Error RecTy::decode(DecoderContext &Data) { ColumnEnd &= ((1u << 31) - 1); dLoc = {*LSDeltaOrErr, *CSOrErr, *NLOrErr, ColumnEnd}; + Loc = {Data.LineStart, *CSOrErr, LineEnd, ColumnEnd}; return Error::success(); } @@ -237,6 +308,8 @@ void CovFunTy::encode(raw_ostream &OS, endianness Endianness) const { std::string Body; raw_string_ostream SS(Body); + assert(this->FileIDs); + auto &FileIDs = *this->FileIDs; encodeULEB128(FileIDs.size(), SS); for (auto I : FileIDs) encodeULEB128(I, SS); @@ -249,12 +322,14 @@ void CovFunTy::encode(raw_ostream &OS, endianness Endianness) const { for (const auto &File : Files) { encodeULEB128(File.Recs.size(), SS); + uint64_t StartLoc = 0; for (const auto &Rec : File.Recs) - Rec.encode(SS); + Rec.encode(StartLoc, SS); } // Emit the Header - uint64_t NameRef = this->NameRef; + uint64_t NameRef = (this->NameRef ? static_cast(*this->NameRef) + : MD5Hash(*this->FuncName)); uint32_t DataSize = Body.size(); uint64_t FuncHash = this->FuncHash; char CoverageMapping = 0; // dummy @@ -271,18 +346,60 @@ void CovFunTy::encode(raw_ostream &OS, endianness Endianness) const { OS << std::move(Body); } +std::vector CovMapTy::generateAccFilenames( + const std::optional> &AccFilesOpt) const { + std::vector Result; + if (useWD()) + Result.push_back(getWD().str()); + // Returns {WD} if AccFiles is None. + if (AccFilesOpt) { + for (auto &Filename : *AccFilesOpt) + Result.push_back(Filename.str()); + } + return Result; +} + +void CovMapTy::regenerateFilenames( + const std::optional> &AccFilesOpt) { + assert(!this->Filenames); + if (this->Files) { + auto &CovMapFilenames = this->Filenames.emplace(generateAccFilenames()); + assert(CovMapFilenames.size() <= 1); + for (auto &&File : *this->Files) + CovMapFilenames.push_back(std::move(File)); + } else { + // Encode Accfiles, that comes from CovFun. + this->Filenames = generateAccFilenames(AccFilesOpt); + } +} + std::pair -CovMapTy::encodeFilenames(bool Compress) const { +CovMapTy::encodeFilenames(const std::optional> &AccFilesOpt, + bool Compress) const { + ArrayRef TempFilenames; + std::vector AccFilenames; // Storage + + if (AccFilesOpt) { + AccFilenames = generateAccFilenames(AccFilesOpt); + TempFilenames = AccFilenames; + } else { + assert(this->Filenames); + TempFilenames = ArrayRef(*this->Filenames); + } + std::string FilenamesBlob; llvm::raw_string_ostream OS(FilenamesBlob); - CoverageFilenamesSectionWriter(this->Filenames).write(OS, Compress); + CoverageFilenamesSectionWriter(TempFilenames).write(OS, Compress); return {llvm::IndexedInstrProf::ComputeHash(FilenamesBlob), FilenamesBlob}; } Expected CovFunTy::decode(const ArrayRef Content, - uint64_t Offset, endianness Endianness) { - DecoderContext Data(Content, (Endianness == endianness::little)); + uint64_t Offset, endianness Endianness, + CovMapByRefTy &CovMapByRef, + InstrProfSymtab *Symtab, + const DecoderParam &Param) { + DecoderContext Data(Content, (Endianness == endianness::little), Param); Data.seek(Offset); uint32_t DataSize; @@ -301,9 +418,19 @@ Expected CovFunTy::decode(const ArrayRef Content, if (!Data) return Data.takeError(); + if (Data.Detailed) + FuncName = Symtab->getFuncOrVarNameIfDefined(*NameRef); + + if (!Data.Raw) + NameRef.reset(); + [[maybe_unused]] auto ExpectedEndOffset = Data.tell() + DataSize; // Decode body. + assert(CovMapByRef.contains(this->FilenamesRef)); + auto &CovMap = *CovMapByRef[this->FilenamesRef]; + auto &FileIDs = this->FileIDs.emplace(); + auto NumFilesOrErr = Data.getULEB128(); if (!NumFilesOrErr) return NumFilesOrErr.takeError(); @@ -330,15 +457,30 @@ Expected CovFunTy::decode(const ArrayRef Content, if (!NumRegionsOrErr) return NumRegionsOrErr.takeError(); auto &File = Files.emplace_back(); + if (Data.Detailed) { + File.Index = FileIdx; // Sequential number. + File.Filename = (*CovMap.Filenames)[FileIDs[FileIdx]]; + } // 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); + + // Hide either Loc or dLoc. + if (!Data.Detailed || Data.dLoc) + Rec.Loc.reset(); + else if (!Data.Raw) + Rec.dLoc.reset(); } } + // Hide FileIDs. + if (!Data.Raw) + this->FileIDs.reset(); + assert(Data.tell() == ExpectedEndOffset); return Data.tell(); } @@ -349,7 +491,8 @@ void CovMapTy::encode(raw_ostream &OS, endianness Endianness) const { uint32_t NRecords = 0; uint32_t FilenamesSize = FilenamesBlob.size(); uint32_t CoverageSize = 0; - uint32_t Version = this->Version; + uint32_t Version = + (this->Version ? *this->Version : INSTR_PROF_COVMAP_VERSION); struct { #define COVMAP_HEADER(Type, LLVMType, Name, Initializer) Type Name; #include "llvm/ProfileData/InstrProfData.inc" @@ -368,8 +511,9 @@ void CovMapTy::encode(raw_ostream &OS, endianness Endianness) const { } Expected CovMapTy::decode(const ArrayRef Content, - uint64_t Offset, endianness Endianness) { - DecoderContext Data(Content, (Endianness == endianness::little)); + uint64_t Offset, endianness Endianness, + const DecoderParam &Param) { + DecoderContext Data(Content, (Endianness == endianness::little), Param); Data.seek(Offset); #define COVMAP_HEADER(Type, LLVMType, Name, Initializer) \ @@ -388,17 +532,31 @@ Expected CovMapTy::decode(const ArrayRef Content, if (!Data) return Data.takeError(); this->FilenamesRef = MD5Hash(FnBlob); + auto &Filenames = this->Filenames.emplace(); if (auto E = RawCoverageFilenamesReader(FnBlob, Filenames) .read(static_cast(Version))) return E; + if (Param.Detailed && useWD()) { + assert(Filenames.size() >= 1); + auto FilenamesI = Filenames.begin(); + StringRef WD = *FilenamesI++; + if (!WD.empty()) + this->WD = WD; + // Use Filenames as a storage. + this->Files.emplace(MutableArrayRef(&*FilenamesI, &*Filenames.end())); + } + Offset = Data.tell(); return Offset; } void CounterTy::mapping(llvm::yaml::IO &IO) { - IO.mapRequired("Tag", Tag); - IO.mapRequired("Val", Val); + IO.mapOptional("Tag", Tag); + IO.mapOptional("Val", Val); + IO.mapOptional("Ref", RefOpt); + IO.mapOptional("Sub", SubOpt); + IO.mapOptional("Add", AddOpt); } void DecisionTy::mapping(llvm::yaml::IO &IO) { @@ -407,7 +565,8 @@ void DecisionTy::mapping(llvm::yaml::IO &IO) { } void RecTy::mapping(llvm::yaml::IO &IO) { - IO.mapRequired("dLoc", dLoc); + IO.mapOptional("Loc", Loc); + IO.mapOptional("dLoc", dLoc); IO.mapOptional("isGap", isGap); CounterTy::mapping(IO); IO.mapOptional("ExtTag", ExtTag); @@ -418,22 +577,32 @@ void RecTy::mapping(llvm::yaml::IO &IO) { } void FileRecsTy::mapping(llvm::yaml::IO &IO) { + IO.mapOptional("Index", Index); + IO.mapOptional("Filename", Filename); IO.mapRequired("Regions", Recs); } void CovFunTy::mapping(llvm::yaml::IO &IO) { - IO.mapRequired("NameRef", NameRef); + IO.mapOptional("NameRef", NameRef); + IO.mapOptional("FuncName", FuncName); IO.mapRequired("FuncHash", FuncHash); IO.mapRequired("FilenamesRef", FilenamesRef); - IO.mapRequired("FileIDs", FileIDs); + IO.mapOptional("FileIDs", FileIDs); IO.mapRequired("Expressions", Expressions); IO.mapRequired("Files", Files); } void CovMapTy::mapping(llvm::yaml::IO &IO) { IO.mapRequired("FilenamesRef", FilenamesRef); - IO.mapRequired("Version", Version); - IO.mapRequired("Filenames", Filenames); + IO.mapOptional("Version", Version); + + if (!WD && !Files) + // Suppress this regardless of (Detailed && Raw). + // Since it is obviously redundant. + IO.mapOptional("Filenames", Filenames); + + IO.mapOptional("WD", WD); + IO.mapOptional("Files", Files); } #define ECase(N, X) IO.enumCase(Value, #X, N::X) @@ -496,7 +665,7 @@ struct CovMapSection : ELFYAML::CovMapSectionBase { } Error decode(ArrayRef Blob, unsigned AddressAlign, - endianness Endianness) { + endianness Endianness, const DecoderParam &Param) { uint64_t Offset = 0; while (true) { @@ -505,7 +674,7 @@ struct CovMapSection : ELFYAML::CovMapSectionBase { break; } auto &CovMap = CovMaps.emplace_back(); - auto Result = CovMap.decode(Blob, Offset, Endianness); + auto Result = CovMap.decode(Blob, Offset, Endianness, Param); if (!Result) { return Result.takeError(); } @@ -541,9 +710,10 @@ struct CovFunSection : ELFYAML::CovMapSectionBase { IO.mapOptional("CovFun", CovFuns); } - static Expected> decode(ArrayRef CovFunA, - unsigned AddressAlign, - endianness Endianness) { + static Expected> + decode(ArrayRef CovFunA, unsigned AddressAlign, + endianness Endianness, CovMapByRefTy &CovMapByRef, + InstrProfSymtab *Symtab, const DecoderParam &Param) { std::vector CovFuns; uint64_t Offset = 0; @@ -553,7 +723,8 @@ struct CovFunSection : ELFYAML::CovMapSectionBase { break; auto &CovFun = CovFuns.emplace_back(); - auto Result = CovFun.decode(CovFunA, Offset, Endianness); + auto Result = CovFun.decode(CovFunA, Offset, Endianness, CovMapByRef, + Symtab, Param); if (!Result) return Result.takeError(); @@ -574,17 +745,127 @@ struct CovFunSection : ELFYAML::CovMapSectionBase { } }; -class DecoderImpl : public Decoder { +class CovMapFilenamesResolver { + DenseMap> FilenamesByCovMap; + std::vector UnresolvedCovFuns; + +protected: + CovMapByRefTy CovMapByRef; + std::vector TempCovMaps; // For Decoder + +public: + void collectCovMap(std::vector &CovMaps) { + for (auto &CovMap : CovMaps) + CovMapByRef[CovMap.FilenamesRef] = &CovMap; + } + + void moveAndCollectCovMap(std::vector &&CovMaps) { + TempCovMaps = std::move(CovMaps); + collectCovMap(TempCovMaps); + } + + void collectCovFunFilenames(std::vector &CovFuns) { + for (auto &CovFun : CovFuns) { + auto &Filenames = FilenamesByCovMap[CovFun.FilenamesRef]; + for (const auto &File : CovFun.Files) { + if (!File.Filename) + goto skip; + Filenames.insert(*File.Filename); + } + UnresolvedCovFuns.push_back(&CovFun); + skip:; + } + } + + void decMaybeResetFilenames(std::vector &CovMaps) { + for (auto &CovMap : CovMaps) { + auto FilenamesI = FilenamesByCovMap.find(CovMap.FilenamesRef); + if (FilenamesI == FilenamesByCovMap.end()) + continue; + + // Calculate FilenamesRef with Filenames from CovFuns. + // If matches, hide Filenames from CovMap. + auto [AccFilenamesRef, _] = + CovMap.encodeFilenames(FilenamesI->second.getArrayRef()); + if (CovMap.FilenamesRef == AccFilenamesRef) { + CovMap.Files.reset(); + CovMap.Filenames.reset(); // FilenamesI has been invalidated. + } + } + } + + void encFixup() { + for (auto &[_, CovMap] : CovMapByRef) { + auto FilenamesI = FilenamesByCovMap.find(CovMap->FilenamesRef); + if (FilenamesI != FilenamesByCovMap.end()) { + // Check Filenames satisfies covfuns + DenseSet FilenamesSet; + if (CovMap->Files) { + for (const auto &Filename : *CovMap->Files) + FilenamesSet.insert(Filename); + } else if (CovMap->Filenames) { + for (const auto &Filename : *CovMap->Filenames) + FilenamesSet.insert(Filename); + } + + for (const auto &Filename : FilenamesI->second) { + if (!FilenamesSet.contains(Filename)) { + // If not, regenerate Filenames. + CovMap->Files.reset(); + CovMap->Filenames.reset(); + break; + } + } + } + + if (!CovMap->Filenames) { + // Regenerate. + // Use Files if exists. + // Use CovFuns (FilenamesI) otherwise. + assert(CovMap->Files || FilenamesI != FilenamesByCovMap.end()); + CovMap->regenerateFilenames( + CovMap->Files ? std::nullopt : FilenamesI->second.getArrayRef()); + } + auto [FilenamesRef, FilenamesBlob] = CovMap->encodeFilenames(); + assert(CovMap->FilenamesRef == FilenamesRef); + } + + // Fill FileIDs + for (auto *CovFun : UnresolvedCovFuns) { + assert(CovMapByRef[CovFun->FilenamesRef]); + assert(CovMapByRef[CovFun->FilenamesRef]->Filenames); + const auto &CovMapFilenames = + *CovMapByRef[CovFun->FilenamesRef]->Filenames; + auto &FileIDs = CovFun->FileIDs.emplace(); + for (const auto &File : CovFun->Files) { + auto I = std::find(CovMapFilenames.begin(), CovMapFilenames.end(), + File.Filename); + assert(I != CovMapFilenames.end()); + FileIDs.push_back(std::distance(CovMapFilenames.begin(), I)); + } + assert(CovFun->Files.size() == FileIDs.size()); + } + } +}; + +class DecoderImpl : public Decoder, CovMapFilenamesResolver { + DecoderParam Param; + std::unique_ptr ProfileNames; - std::vector TempCovMaps; + + InstrProfSymtab::PrfNamesChunksTy PrfNames; + + MapVector, unsigned>> CovFunBlobs; + DenseMap> TempCovFuns; public: - DecoderImpl(endianness Endianness, bool CovMapEnabled) - : Decoder(Endianness), ProfileNames(std::make_unique()) { - enabled = CovMapEnabled; + DecoderImpl(endianness Endianness, const DecoderParam &Param) + : Decoder(Endianness), Param(Param), + ProfileNames(std::make_unique()) { + enabled = (Param.Detailed || Param.Raw); } - Error acquire(unsigned AddressAlign, StringRef Name, + Error acquire(uint64_t Offset, unsigned AddressAlign, StringRef Name, ArrayRef Content) override { // Don't register anything. if (!enabled) @@ -594,44 +875,82 @@ class DecoderImpl : public Decoder { // 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)) + if (auto E = TempCovMap->decode(Content, AddressAlign, Endianness, Param)) return E; - TempCovMaps = std::move(TempCovMap->CovMaps); + moveAndCollectCovMap(std::move(TempCovMap->CovMaps)); + } else if (PrfNamesSection::nameMatches(Name)) { + // Decode PrfNames in advance since CovFun depends on it. + auto PrfNamesOrErr = ProfileNames->createAndGetList(Content); + if (!PrfNamesOrErr) + return PrfNamesOrErr.takeError(); + PrfNames = std::move(*PrfNamesOrErr); + } else if (CovFunSection::nameMatches(Name)) { + // Will be decoded after CovMap is met. + CovFunBlobs[Offset] = {Content, AddressAlign}; } return Error::success(); } - Error make(ELFYAML::CovMapSectionBase *Base, - ArrayRef Content) override { + Error fixup() override { + // Decode CovFun(s) with predecoded PrfNames and CovMap. + for (const auto &[Offset, CovFunBlob] : CovFunBlobs) { + auto CovFunsOrErr = + CovFunSection::decode(CovFunBlob.first, CovFunBlob.second, Endianness, + CovMapByRef, ProfileNames.get(), Param); + if (!CovFunsOrErr) + return CovFunsOrErr.takeError(); + TempCovFuns[Offset] = std::move(*CovFunsOrErr); + collectCovFunFilenames(TempCovFuns[Offset]); + } + // Hide Filenames if it is reproducible from CovFuns. + if (Param.Detailed) + decMaybeResetFilenames(TempCovMaps); + return Error::success(); + } + + Error make(ELFYAML::CovMapSectionBase *Base, uint64_t Offset) 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); + S->PrfNames = std::move(PrfNames); 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); + assert(S->CovFuns.empty()); + assert(TempCovFuns.contains(Offset)); + S->CovFuns = std::move(TempCovFuns[Offset]); return Error::success(); } llvm_unreachable("Unknown Section"); } }; + +class EncoderImpl : public Encoder, CovMapFilenamesResolver { +public: + EncoderImpl(endianness Endianness) : Encoder(Endianness) {} + + void collect(ELFYAML::Chunk *Chunk) override { + if (auto S = dyn_cast(Chunk)) { + collectCovMap(S->CovMaps); + } else if (auto S = dyn_cast(Chunk)) { + collectCovFunFilenames(S->CovFuns); + } + } + + void fixup() override { encFixup(); } +}; } // namespace std::unique_ptr Decoder::get(endianness Endianness, - bool CovMapEnabled) { - return std::make_unique(Endianness, CovMapEnabled); + const DecoderParam &Param) { + return std::make_unique(Endianness, Param); +} + +std::unique_ptr Encoder::get(endianness Endianness) { + return std::make_unique(Endianness); } bool covmap::nameMatches(StringRef Name) { diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp index e55daa72b3cee..d13ab60822a02 100644 --- a/llvm/lib/ObjectYAML/ELFEmitter.cpp +++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -772,6 +772,13 @@ void ELFState::initSectionHeaders(std::vector &SHeaders, // valid SHN_UNDEF entry since SHT_NULL == 0. SHeaders.resize(Doc.getSections().size()); + auto CovMapEncoder = covmap::Encoder::get(ELFT::Endianness); + for (auto &Chunk : Doc.Chunks) { + if (isa(Chunk.get())) + CovMapEncoder->collect(Chunk.get()); + } + CovMapEncoder->fixup(); + for (const std::unique_ptr &D : Doc.Chunks) { if (ELFYAML::Fill *S = dyn_cast(D.get())) { S->Offset = alignToOffset(CBA, /*Align=*/1, S->Offset); diff --git a/llvm/test/tools/obj2yaml/ELF/covmap-be.yaml b/llvm/test/tools/obj2yaml/ELF/covmap-be.yaml index 4456bbbcb4a86..ec1352b40a9cc 100644 --- a/llvm/test/tools/obj2yaml/ELF/covmap-be.yaml +++ b/llvm/test/tools/obj2yaml/ELF/covmap-be.yaml @@ -1,8 +1,15 @@ # RUN: yaml2obj %s -o %t.o # RUN: obj2yaml %t.o > %t.plain.yaml # RUN: obj2yaml --covmap-raw %t.o > %t.raw.yaml +# RUN: obj2yaml --covmap --covmap-raw %t.o > %t.mixed.yaml +# RUN: obj2yaml --covmap --covmap-dloc %t.o > %t.dloc.yaml +# RUN: obj2yaml --covmap %t.o > %t.covmap.yaml +# RUN: sed -E '/^(#.*)?$/d' %s | diff %t.covmap.yaml - # RUN: yaml2obj %t.plain.yaml -o - | cmp %t.o - # RUN: yaml2obj %t.raw.yaml -o - | cmp %t.o - +# RUN: yaml2obj %t.mixed.yaml -o - | cmp %t.o - +# RUN: yaml2obj %t.dloc.yaml -o - | cmp %t.o - +# RUN: yaml2obj %t.covmap.yaml -o - | cmp %t.o - # FIXME: This is synthetically created. s/ELFDATA2LSB/ELF2DATAMSB/ s/EM_X86_64/EM_PPC64/ --- !ELF @@ -19,45 +26,48 @@ Sections: Flags: [ SHF_GNU_RETAIN ] AddressAlign: 0x8 CovFun: - - NameRef: 0xA72DB7A33048E6A3 + - FuncName: _Z4funci FuncHash: 0xAFC772D567676299 FilenamesRef: 0x70DA2CAFD8CE198E - FileIDs: [ 1, 1 ] Expressions: - - [ { Tag: Ref, Val: 0 }, { Tag: Ref, Val: 1 } ] - - [ { Tag: Add, Val: 0 }, { Tag: Ref, Val: 2 } ] - - [ { Tag: Add, Val: 0 }, { Tag: Ref, Val: 2 } ] - - [ { Tag: Add, Val: 0 }, { Tag: Ref, Val: 2 } ] - - [ { Tag: Ref, Val: 0 }, { Tag: Ref, Val: 2 } ] - - [ { Tag: Ref, Val: 0 }, { Tag: Ref, Val: 2 } ] - - [ { Tag: Ref, Val: 0 }, { Tag: Ref, Val: 2 } ] - - [ { Tag: Add, Val: 0 }, { Tag: Ref, Val: 5 } ] + - [ { Ref: 0 }, { Ref: 1 } ] + - [ { Add: 0 }, { Ref: 2 } ] + - [ { Add: 0 }, { Ref: 2 } ] + - [ { Add: 0 }, { Ref: 2 } ] + - [ { Ref: 0 }, { Ref: 2 } ] + - [ { Ref: 0 }, { Ref: 2 } ] + - [ { Ref: 0 }, { Ref: 2 } ] + - [ { Add: 0 }, { Ref: 5 } ] Files: - - Regions: - - { dLoc: [ 3, 17, 11, 2 ], Tag: Ref, Val: 0 } - - { dLoc: [ 3, 6, 5, 4 ], Tag: Add, Val: 0 } - - { dLoc: [ 1, 9, 0, 13 ], Tag: Zero, Val: 3, Expansion: 1 } - - { dLoc: [ 0, 23, 1, 7 ], isGap: true, Tag: Ref, Val: 2 } - - { dLoc: [ 1, 7, 0, 16 ], Tag: Ref, Val: 2 } - - { dLoc: [ 0, 17, 2, 5 ], isGap: true, Tag: Sub, Val: 3 } - - { 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 } - - { 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: [ 0, 12, 1, 3 ], isGap: true, Tag: Zero, Val: 0 } - - { dLoc: [ 1, 3, 0, 12 ], Tag: Zero, Val: 0 } - - Regions: - - { dLoc: [ 1, 17, 0, 41 ], Tag: Add, Val: 0 } - - { dLoc: [ 0, 18, 0, 32 ], Tag: Add, Val: 0 } - - { 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 } - - { 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: [ 0, 36, 0, 40 ], Tag: Zero, Val: 12, ExtTag: MCDCBranch, Branch: [ { Tag: Ref, Val: 4 }, { Tag: Zero, Val: 0 } ], MCDC: [ 2, 0, 0 ] } + - Index: 0 + Filename: func.cpp + Regions: + - { Loc: [ 3, 17, 14, 2 ], Ref: 0 } + - { Loc: [ 6, 6, 11, 4 ], Add: 0 } + - { Loc: [ 7, 9, 7, 13 ], Expansion: 1 } + - { Loc: [ 7, 23, 8, 7 ], isGap: true, Ref: 2 } + - { Loc: [ 8, 7, 8, 16 ], Ref: 2 } + - { Loc: [ 8, 17, 10, 5 ], isGap: true, Sub: 3 } + - { Loc: [ 9, 1, 9, 1 ], ExtTag: Skip } + - { Loc: [ 10, 5, 11, 4 ], Sub: 3 } + - { Loc: [ 11, 12, 11, 17 ], Sub: 3 } + - { Loc: [ 11, 12, 11, 17 ], Branch: [ { Ref: 1 }, { Sub: 6 } ] } + - { Loc: [ 11, 19, 12, 3 ], isGap: true, Sub: 6 } + - { Loc: [ 12, 3, 12, 11 ], Sub: 6 } + - { Loc: [ 12, 12, 13, 3 ], isGap: true, Tag: Zero } + - { Loc: [ 13, 3, 13, 12 ], Tag: Zero } + - Index: 1 + Filename: func.cpp + Regions: + - { Loc: [ 1, 17, 1, 41 ], Add: 0 } + - { Loc: [ 1, 18, 1, 32 ], Add: 0 } + - { Loc: [ 1, 18, 1, 40 ], Decision: { BIdx: 5, NCond: 3 } } + - { Loc: [ 1, 19, 1, 22 ], Add: 0 } + - { Loc: [ 1, 19, 1, 22 ], Branch: [ { Sub: 7 }, { Ref: 5 } ], MCDC: [ 1, 2, 3 ] } + - { Loc: [ 1, 26, 1, 31 ], Ref: 5 } + - { Loc: [ 1, 26, 1, 31 ], Branch: [ { Tag: Zero }, { Ref: 6 } ], MCDC: [ 3, 2, 0 ] } + - { Loc: [ 1, 36, 1, 40 ], Ref: 3 } + - { Loc: [ 1, 36, 1, 40 ], Branch: [ { Ref: 4 }, { Tag: Zero } ], MCDC: [ 2, 0, 0 ] } - Name: __llvm_covmap Type: SHT_PROGBITS Flags: [ SHF_GNU_RETAIN ] @@ -65,9 +75,6 @@ Sections: CovMap: - FilenamesRef: 0x70DA2CAFD8CE198E Version: 6 - Filenames: - - '' - - func.cpp - Name: __llvm_prf_names Type: SHT_PROGBITS Flags: [ SHF_ALLOC, SHF_GNU_RETAIN ] diff --git a/llvm/test/tools/obj2yaml/ELF/covmap.yaml b/llvm/test/tools/obj2yaml/ELF/covmap.yaml index 09bcf86b44f47..1876492351d6a 100644 --- a/llvm/test/tools/obj2yaml/ELF/covmap.yaml +++ b/llvm/test/tools/obj2yaml/ELF/covmap.yaml @@ -1,9 +1,15 @@ # 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: obj2yaml --covmap --covmap-raw %t.o | tee %t.mixed.yaml | FileCheck %s --check-prefixes=CHECK,COVMAP,RAW,DET,LOC,DLOC +# RUN: obj2yaml --covmap --covmap-dloc %t.o | tee %t.dloc.yaml | FileCheck %s --check-prefixes=CHECK,COVMAP,DET,DETONLY,DLOC +# RUN: obj2yaml --covmap %t.o | tee %t.covmap.yaml | FileCheck %s --check-prefixes=CHECK,COVMAP,DET,DETONLY,LOC +# RUN: sed -E '/^(#.*)?$/d' %s | diff %t.covmap.yaml - # RUN: yaml2obj %t.plain.yaml -o - | cmp %t.o - # RUN: yaml2obj %t.raw.yaml -o - | cmp %t.o - +# RUN: yaml2obj %t.mixed.yaml -o - | cmp %t.o - +# RUN: yaml2obj %t.dloc.yaml -o - | cmp %t.o - +# RUN: yaml2obj %t.covmap.yaml -o - | cmp %t.o - --- !ELF FileHeader: @@ -23,74 +29,91 @@ Sections: # COVMAP: CovFun: CovFun: # RAW:- NameRef: 0xA72DB7A33048E6A3 - - NameRef: 0xA72DB7A33048E6A3 + - FuncName: _Z4funci # 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 } ] - - [ { Tag: Add, Val: 0 }, { Tag: Ref, Val: 2 } ] - - [ { Tag: Ref, Val: 0 }, { Tag: Ref, Val: 2 } ] - - [ { Tag: Ref, Val: 0 }, { Tag: Ref, Val: 2 } ] - - [ { Tag: Ref, Val: 0 }, { Tag: Ref, Val: 2 } ] - - [ { Tag: Add, Val: 0 }, { Tag: Ref, Val: 5 } ] + - [ { Ref: 0 }, { Ref: 1 } ] + - [ { Add: 0 }, { Ref: 2 } ] + - [ { Add: 0 }, { Ref: 2 } ] + - [ { Add: 0 }, { Ref: 2 } ] + - [ { Ref: 0 }, { Ref: 2 } ] + - [ { Ref: 0 }, { Ref: 2 } ] + - [ { Ref: 0 }, { Ref: 2 } ] + - [ { Add: 0 }, { Ref: 5 } ] # COVMAP: Files: Files: +# DET: - Index: 0 + - Index: 0 +# DET: Filename: func.cpp + Filename: func.cpp # COVMAP: Regions: - - Regions: + Regions: +# LOC: Loc: [ 3, 17, 14, 2 ] # 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 } +# DETONLY: Ref: 0 + - { Loc: [ 3, 17, 14, 2 ], Ref: 0 } +# LOC: Loc: [ 6, 6, 11, 4 ] # DLOC: dLoc: [ 3, 6, 5, 4 ] # RAW: Tag: Add, Val: 0 - - { dLoc: [ 3, 6, 5, 4 ], Tag: Add, Val: 0 } +# DETONLY: Add: 0 + - { Loc: [ 6, 6, 11, 4 ], Add: 0 } # RAW: Tag: Zero, Val: 3, # COVMAP: Expansion: 1 - - { dLoc: [ 1, 9, 0, 13 ], Tag: Zero, Val: 3, Expansion: 1 } + - { Loc: [ 7, 9, 7, 13 ], 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 } +# DETONLY: Ref: 2 + - { Loc: [ 7, 23, 8, 7 ], isGap: true, Ref: 2 } + - { Loc: [ 8, 7, 8, 16 ], Ref: 2 } # COVMAP: isGap: true # RAW: Tag: Sub, Val: 3 - - { dLoc: [ 0, 17, 2, 5 ], isGap: true, Tag: Sub, Val: 3 } +# DETONLY: Sub: 3 + - { Loc: [ 8, 17, 10, 5 ], isGap: true, Sub: 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 } + - { Loc: [ 9, 1, 9, 1 ], ExtTag: Skip } + - { Loc: [ 10, 5, 11, 4 ], Sub: 3 } + - { Loc: [ 11, 12, 11, 17 ], Sub: 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 } +# DETONLY: Branch: [ { Ref: 1 }, { Sub: 6 } ] + - { Loc: [ 11, 12, 11, 17 ], Branch: [ { Ref: 1 }, { Sub: 6 } ] } + - { Loc: [ 11, 19, 12, 3 ], isGap: true, Sub: 6 } + - { Loc: [ 12, 3, 12, 11 ], Sub: 6 } + - { Loc: [ 12, 12, 13, 3 ], isGap: true, Tag: Zero } +# LOC: Loc: [ 13, 3, 13, 12 ] # 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 } + - { Loc: [ 13, 3, 13, 12 ], Tag: Zero } +# DET: - Index: 1 + - Index: 1 +# DET: Filename: func.cpp + Filename: func.cpp # COVMAP: Regions: - - Regions: - - { dLoc: [ 1, 17, 0, 41 ], Tag: Add, Val: 0 } - - { dLoc: [ 0, 18, 0, 32 ], Tag: Add, Val: 0 } + Regions: + - { Loc: [ 1, 17, 1, 41 ], Add: 0 } + - { Loc: [ 1, 18, 1, 32 ], Add: 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 } + - { Loc: [ 1, 18, 1, 40 ], Decision: { BIdx: 5, NCond: 3 } } + - { Loc: [ 1, 19, 1, 22 ], Add: 0 } # RAW: Tag: Zero, Val: 12, ExtTag: MCDCBranch, Branch: [ { Tag: Sub, Val: 7 }, { Tag: Ref, Val: 5 } ] +# DETONLY: Branch: [ { Sub: 7 }, { Ref: 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 } + - { Loc: [ 1, 19, 1, 22 ], Branch: [ { Sub: 7 }, { Ref: 5 } ], MCDC: [ 1, 2, 3 ] } + - { Loc: [ 1, 26, 1, 31 ], Ref: 5 } + - { Loc: [ 1, 26, 1, 31 ], Branch: [ { Tag: Zero }, { Ref: 6 } ], MCDC: [ 3, 2, 0 ] } + - { Loc: [ 1, 36, 1, 40 ], Ref: 3 } +# LOC: Loc: [ 1, 36, 1, 40 ] # 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 ] } + - { Loc: [ 1, 36, 1, 40 ], Branch: [ { Ref: 4 }, { Tag: Zero } ], MCDC: [ 2, 0, 0 ] } # CHECK: __llvm_covmap - Name: __llvm_covmap Type: SHT_PROGBITS @@ -104,11 +127,8 @@ Sections: # COVMAP: Version: Version: 6 # RAWONLY: Filenames: - Filenames: # RAWONLY: - '' - - '' # RAWONLY: - func.cpp - - func.cpp # CHECK: __llvm_prf_names - Name: __llvm_prf_names Type: SHT_PROGBITS diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp index aba494c0f1173..4a072b528a5ea 100644 --- a/llvm/tools/obj2yaml/elf2yaml.cpp +++ b/llvm/tools/obj2yaml/elf2yaml.cpp @@ -23,9 +23,15 @@ using namespace llvm; +static cl::opt + CovMapDetailed("covmap", cl::desc("Dump detailed YAML in Coverage Map."), + cl::cat(Cat)); static cl::opt CovMapRaw("covmap-raw", cl::desc("Dump raw YAML in Coverage Map."), cl::cat(Cat)); +static cl::opt CovMapDLoc("covmap-dloc", + cl::desc("Prefer dLoc over absolute Loc."), + cl::cat(Cat)); namespace { @@ -588,7 +594,11 @@ ELFDumper::dumpSections() { return Error::success(); }; - auto CovMapDecoder = covmap::Decoder::get(ELFT::Endianness, CovMapRaw); + coverage::yaml::DecoderParam Param; + Param.Detailed = CovMapDetailed; + Param.Raw = CovMapRaw; + Param.dLoc = CovMapDLoc; + auto CovMapDecoder = covmap::Decoder::get(ELFT::Endianness, Param); if (covmap::Decoder::enabled) { // Look up covmap-related sections in advance. for (const auto &Sec : Sections) { @@ -606,10 +616,13 @@ ELFDumper::dumpSections() { if (!ContentOrErr) return ContentOrErr.takeError(); - if (auto E = CovMapDecoder->acquire(Sec.sh_addralign, *NameOrErr, - *ContentOrErr)) + if (auto E = CovMapDecoder->acquire(Sec.sh_offset, Sec.sh_addralign, + *NameOrErr, *ContentOrErr)) return std::move(E); } + + if (auto E = CovMapDecoder->fixup()) + return std::move(E); } auto GetDumper = [this](unsigned Type) @@ -1708,11 +1721,7 @@ ELFDumper::dumpCovMap(const Elf_Shdr *Shdr, StringRef Name, 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)) + if (auto E = CovMapDecoder->make(S.get(), Shdr->sh_offset)) return std::move(E); return S.release();