Skip to content

Commit 6efc1b4

Browse files
Reland: [llvm-cov][WebAssembly] Read __llvm_prf_names from data segments (llvm#112569)
On WebAssembly, most coverage metadata contents read by llvm-cov (like `__llvm_covmap` and `__llvm_covfun`) are stored in custom sections because they are not referenced at runtime. However, `__llvm_prf_names` is referenced at runtime by the profile runtime library and is read by llvm-cov post-processing tools, so it needs to be stored in a data segment, which is allocatable at runtime and accessible by tools as long as "name" section is present in the binary. This patch changes the way llvm-cov reads `__llvm_prf_names` on WebAssembly. Instead of looking for a section, it looks for a data segment with the same name. This reverts commit 157f10d and fixes PE/COFF `.lprfn$A` section handling.
1 parent 5fe29a8 commit 6efc1b4

File tree

4 files changed

+85
-18
lines changed

4 files changed

+85
-18
lines changed

llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp

Lines changed: 74 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818
#include "llvm/ADT/SmallVector.h"
1919
#include "llvm/ADT/Statistic.h"
2020
#include "llvm/ADT/StringRef.h"
21+
#include "llvm/BinaryFormat/Wasm.h"
2122
#include "llvm/Object/Archive.h"
2223
#include "llvm/Object/Binary.h"
2324
#include "llvm/Object/COFF.h"
2425
#include "llvm/Object/Error.h"
2526
#include "llvm/Object/MachOUniversal.h"
2627
#include "llvm/Object/ObjectFile.h"
28+
#include "llvm/Object/Wasm.h"
2729
#include "llvm/ProfileData/InstrProf.h"
2830
#include "llvm/Support/Casting.h"
2931
#include "llvm/Support/Compression.h"
@@ -492,22 +494,29 @@ Expected<bool> RawCoverageMappingDummyChecker::isDummy() {
492494
return Tag == Counter::Zero;
493495
}
494496

495-
Error InstrProfSymtab::create(SectionRef &Section) {
496-
Expected<StringRef> DataOrErr = Section.getContents();
497-
if (!DataOrErr)
498-
return DataOrErr.takeError();
499-
Data = *DataOrErr;
500-
Address = Section.getAddress();
501-
497+
/// Determine if we should skip the first byte of the section content
498+
static bool shouldSkipSectionFirstByte(SectionRef &Section) {
499+
const ObjectFile *Obj = Section.getObject();
502500
// If this is a linked PE/COFF file, then we have to skip over the null byte
503501
// that is allocated in the .lprfn$A section in the LLVM profiling runtime.
504502
// If the name section is .lprfcovnames, it doesn't have the null byte at the
505503
// beginning.
506-
const ObjectFile *Obj = Section.getObject();
507504
if (isa<COFFObjectFile>(Obj) && !Obj->isRelocatableObject())
508505
if (Expected<StringRef> NameOrErr = Section.getName())
509506
if (*NameOrErr != getInstrProfSectionName(IPSK_covname, Triple::COFF))
510-
Data = Data.drop_front(1);
507+
return true;
508+
return false;
509+
}
510+
511+
Error InstrProfSymtab::create(SectionRef &Section) {
512+
Expected<StringRef> DataOrErr = Section.getContents();
513+
if (!DataOrErr)
514+
return DataOrErr.takeError();
515+
Data = *DataOrErr;
516+
Address = Section.getAddress();
517+
518+
if (shouldSkipSectionFirstByte(Section))
519+
Data = Data.substr(1);
511520

512521
return Error::success();
513522
}
@@ -1075,6 +1084,56 @@ lookupSections(ObjectFile &OF, InstrProfSectKind IPSK) {
10751084
return Sections;
10761085
}
10771086

1087+
/// Find a section that matches \p Name and is allocatable at runtime.
1088+
///
1089+
/// Returns the contents of the section and its start offset in the object file.
1090+
static Expected<std::pair<StringRef, uint64_t>>
1091+
lookupAllocatableSection(ObjectFile &OF, InstrProfSectKind IPSK) {
1092+
// On Wasm, allocatable sections can live only in data segments.
1093+
if (auto *WOF = dyn_cast<WasmObjectFile>(&OF)) {
1094+
std::vector<const WasmSegment *> Segments;
1095+
auto ObjFormat = OF.getTripleObjectFormat();
1096+
auto Name =
1097+
getInstrProfSectionName(IPSK, ObjFormat, /*AddSegmentInfo=*/false);
1098+
for (const auto &DebugName : WOF->debugNames()) {
1099+
if (DebugName.Type != wasm::NameType::DATA_SEGMENT ||
1100+
DebugName.Name != Name)
1101+
continue;
1102+
if (DebugName.Index >= WOF->dataSegments().size())
1103+
return make_error<CoverageMapError>(coveragemap_error::malformed);
1104+
auto &Segment = WOF->dataSegments()[DebugName.Index];
1105+
Segments.push_back(&Segment);
1106+
}
1107+
if (Segments.empty())
1108+
return make_error<CoverageMapError>(coveragemap_error::no_data_found);
1109+
if (Segments.size() != 1)
1110+
return make_error<CoverageMapError>(coveragemap_error::malformed);
1111+
1112+
const auto &Segment = *Segments.front();
1113+
auto &Data = Segment.Data;
1114+
StringRef Content(reinterpret_cast<const char *>(Data.Content.data()),
1115+
Data.Content.size());
1116+
return std::make_pair(Content, Segment.SectionOffset);
1117+
}
1118+
1119+
// On other object file types, delegate to lookupSections to find the section.
1120+
auto Sections = lookupSections(OF, IPSK);
1121+
if (!Sections)
1122+
return Sections.takeError();
1123+
if (Sections->size() != 1)
1124+
return make_error<CoverageMapError>(
1125+
coveragemap_error::malformed,
1126+
"the size of coverage mapping section is not one");
1127+
auto &Section = Sections->front();
1128+
auto ContentsOrErr = Section.getContents();
1129+
if (!ContentsOrErr)
1130+
return ContentsOrErr.takeError();
1131+
auto Content = *ContentsOrErr;
1132+
if (shouldSkipSectionFirstByte(Section))
1133+
Content = Content.drop_front(1);
1134+
return std::make_pair(Content, Section.getAddress());
1135+
}
1136+
10781137
static Expected<std::unique_ptr<BinaryCoverageReader>>
10791138
loadBinaryFormat(std::unique_ptr<Binary> Bin, StringRef Arch,
10801139
StringRef CompilationDir = "",
@@ -1105,23 +1164,20 @@ loadBinaryFormat(std::unique_ptr<Binary> Bin, StringRef Arch,
11051164

11061165
// Look for the sections that we are interested in.
11071166
auto ProfileNames = std::make_unique<InstrProfSymtab>();
1108-
std::vector<SectionRef> NamesSectionRefs;
11091167
// If IPSK_name is not found, fallback to search for IPK_covname, which is
11101168
// used when binary correlation is enabled.
1111-
auto NamesSection = lookupSections(*OF, IPSK_name);
1169+
auto NamesSection = lookupAllocatableSection(*OF, IPSK_name);
11121170
if (auto E = NamesSection.takeError()) {
11131171
consumeError(std::move(E));
1114-
NamesSection = lookupSections(*OF, IPSK_covname);
1172+
NamesSection = lookupAllocatableSection(*OF, IPSK_covname);
11151173
if (auto E = NamesSection.takeError())
11161174
return std::move(E);
11171175
}
1118-
NamesSectionRefs = *NamesSection;
11191176

1120-
if (NamesSectionRefs.size() != 1)
1121-
return make_error<CoverageMapError>(
1122-
coveragemap_error::malformed,
1123-
"the size of coverage mapping section is not one");
1124-
if (Error E = ProfileNames->create(NamesSectionRefs.back()))
1177+
uint64_t NamesAddress;
1178+
StringRef NamesContent;
1179+
std::tie(NamesContent, NamesAddress) = *NamesSection;
1180+
if (Error E = ProfileNames->create(NamesContent, NamesAddress))
11251181
return std::move(E);
11261182

11271183
auto CoverageSection = lookupSections(*OF, IPSK_covmap);
Binary file not shown.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
__main_argc_argv
2+
0x0
3+
1
4+
100

llvm/test/tools/llvm-cov/binary-formats.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,11 @@ int main(int argc, const char *argv[]) {}
1010
// RUN: llvm-cov show %S/Inputs/binary-formats.v3.macho64l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s
1111
// RUN: llvm-cov show %S/Inputs/binary-formats.v6.linux64l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s
1212

13+
// RUN: llvm-profdata merge %S/Inputs/binary-formats.wasm.proftext -o %t.wasm.profdata
14+
// NOTE: The wasm binary is built with the following command:
15+
// clang -target wasm32-unknown-wasi %s -o %S/Inputs/binary-formats.v6.wasm32 \
16+
// -mllvm -enable-name-compression=false \
17+
// -fprofile-instr-generate -fcoverage-mapping -lwasi-emulated-getpid -lwasi-emulated-mman
18+
// RUN: llvm-cov show %S/Inputs/binary-formats.v6.wasm32 -instr-profile %t.wasm.profdata -path-equivalence=/tmp,%S %s | FileCheck %s
19+
1320
// RUN: llvm-cov export %S/Inputs/binary-formats.macho64l -instr-profile %t.profdata | FileCheck %S/Inputs/binary-formats.canonical.json

0 commit comments

Comments
 (0)