diff --git a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp index f10ef3f877b521b..5cd40e9710194e1 100644 --- a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp @@ -18,12 +18,14 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Wasm.h" #include "llvm/Object/Archive.h" #include "llvm/Object/Binary.h" #include "llvm/Object/COFF.h" #include "llvm/Object/Error.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Object/Wasm.h" #include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compression.h" @@ -492,22 +494,29 @@ Expected RawCoverageMappingDummyChecker::isDummy() { return Tag == Counter::Zero; } -Error InstrProfSymtab::create(SectionRef &Section) { - Expected DataOrErr = Section.getContents(); - if (!DataOrErr) - return DataOrErr.takeError(); - Data = *DataOrErr; - Address = Section.getAddress(); - +/// Determine if we should skip the first byte of the section content +static bool shouldSkipSectionFirstByte(SectionRef &Section) { + const ObjectFile *Obj = Section.getObject(); // If this is a linked PE/COFF file, then we have to skip over the null byte // that is allocated in the .lprfn$A section in the LLVM profiling runtime. // If the name section is .lprfcovnames, it doesn't have the null byte at the // beginning. - const ObjectFile *Obj = Section.getObject(); if (isa(Obj) && !Obj->isRelocatableObject()) if (Expected NameOrErr = Section.getName()) if (*NameOrErr != getInstrProfSectionName(IPSK_covname, Triple::COFF)) - Data = Data.drop_front(1); + return true; + return false; +} + +Error InstrProfSymtab::create(SectionRef &Section) { + Expected DataOrErr = Section.getContents(); + if (!DataOrErr) + return DataOrErr.takeError(); + Data = *DataOrErr; + Address = Section.getAddress(); + + if (shouldSkipSectionFirstByte(Section)) + Data = Data.substr(1); return Error::success(); } @@ -1075,6 +1084,56 @@ lookupSections(ObjectFile &OF, InstrProfSectKind IPSK) { return Sections; } +/// Find a section that matches \p Name and is allocatable at runtime. +/// +/// Returns the contents of the section and its start offset in the object file. +static Expected> +lookupAllocatableSection(ObjectFile &OF, InstrProfSectKind IPSK) { + // On Wasm, allocatable sections can live only in data segments. + if (auto *WOF = dyn_cast(&OF)) { + std::vector Segments; + auto ObjFormat = OF.getTripleObjectFormat(); + auto Name = + getInstrProfSectionName(IPSK, ObjFormat, /*AddSegmentInfo=*/false); + for (const auto &DebugName : WOF->debugNames()) { + if (DebugName.Type != wasm::NameType::DATA_SEGMENT || + DebugName.Name != Name) + continue; + if (DebugName.Index >= WOF->dataSegments().size()) + return make_error(coveragemap_error::malformed); + auto &Segment = WOF->dataSegments()[DebugName.Index]; + Segments.push_back(&Segment); + } + if (Segments.empty()) + return make_error(coveragemap_error::no_data_found); + if (Segments.size() != 1) + return make_error(coveragemap_error::malformed); + + const auto &Segment = *Segments.front(); + auto &Data = Segment.Data; + StringRef Content(reinterpret_cast(Data.Content.data()), + Data.Content.size()); + return std::make_pair(Content, Segment.SectionOffset); + } + + // On other object file types, delegate to lookupSections to find the section. + auto Sections = lookupSections(OF, IPSK); + if (!Sections) + return Sections.takeError(); + if (Sections->size() != 1) + return make_error( + coveragemap_error::malformed, + "the size of coverage mapping section is not one"); + auto &Section = Sections->front(); + auto ContentsOrErr = Section.getContents(); + if (!ContentsOrErr) + return ContentsOrErr.takeError(); + auto Content = *ContentsOrErr; + if (shouldSkipSectionFirstByte(Section)) + Content = Content.drop_front(1); + return std::make_pair(Content, Section.getAddress()); +} + static Expected> loadBinaryFormat(std::unique_ptr Bin, StringRef Arch, StringRef CompilationDir = "", @@ -1105,23 +1164,20 @@ loadBinaryFormat(std::unique_ptr Bin, StringRef Arch, // Look for the sections that we are interested in. auto ProfileNames = std::make_unique(); - std::vector NamesSectionRefs; // If IPSK_name is not found, fallback to search for IPK_covname, which is // used when binary correlation is enabled. - auto NamesSection = lookupSections(*OF, IPSK_name); + auto NamesSection = lookupAllocatableSection(*OF, IPSK_name); if (auto E = NamesSection.takeError()) { consumeError(std::move(E)); - NamesSection = lookupSections(*OF, IPSK_covname); + NamesSection = lookupAllocatableSection(*OF, IPSK_covname); if (auto E = NamesSection.takeError()) return std::move(E); } - NamesSectionRefs = *NamesSection; - if (NamesSectionRefs.size() != 1) - return make_error( - coveragemap_error::malformed, - "the size of coverage mapping section is not one"); - if (Error E = ProfileNames->create(NamesSectionRefs.back())) + uint64_t NamesAddress; + StringRef NamesContent; + std::tie(NamesContent, NamesAddress) = *NamesSection; + if (Error E = ProfileNames->create(NamesContent, NamesAddress)) return std::move(E); auto CoverageSection = lookupSections(*OF, IPSK_covmap); diff --git a/llvm/test/tools/llvm-cov/Inputs/binary-formats.v6.wasm32 b/llvm/test/tools/llvm-cov/Inputs/binary-formats.v6.wasm32 new file mode 100755 index 000000000000000..5a606d5a2f69fd1 Binary files /dev/null and b/llvm/test/tools/llvm-cov/Inputs/binary-formats.v6.wasm32 differ diff --git a/llvm/test/tools/llvm-cov/Inputs/binary-formats.wasm.proftext b/llvm/test/tools/llvm-cov/Inputs/binary-formats.wasm.proftext new file mode 100644 index 000000000000000..20fc3816c2255a5 --- /dev/null +++ b/llvm/test/tools/llvm-cov/Inputs/binary-formats.wasm.proftext @@ -0,0 +1,4 @@ +__main_argc_argv +0x0 +1 +100 diff --git a/llvm/test/tools/llvm-cov/binary-formats.c b/llvm/test/tools/llvm-cov/binary-formats.c index a5bfc012860ec3a..bb61b288cfc62e6 100644 --- a/llvm/test/tools/llvm-cov/binary-formats.c +++ b/llvm/test/tools/llvm-cov/binary-formats.c @@ -10,4 +10,11 @@ int main(int argc, const char *argv[]) {} // RUN: llvm-cov show %S/Inputs/binary-formats.v3.macho64l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s // RUN: llvm-cov show %S/Inputs/binary-formats.v6.linux64l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s +// RUN: llvm-profdata merge %S/Inputs/binary-formats.wasm.proftext -o %t.wasm.profdata +// NOTE: The wasm binary is built with the following command: +// clang -target wasm32-unknown-wasi %s -o %S/Inputs/binary-formats.v6.wasm32 \ +// -mllvm -enable-name-compression=false \ +// -fprofile-instr-generate -fcoverage-mapping -lwasi-emulated-getpid -lwasi-emulated-mman +// RUN: llvm-cov show %S/Inputs/binary-formats.v6.wasm32 -instr-profile %t.wasm.profdata -path-equivalence=/tmp,%S %s | FileCheck %s + // RUN: llvm-cov export %S/Inputs/binary-formats.macho64l -instr-profile %t.profdata | FileCheck %S/Inputs/binary-formats.canonical.json