[llvm] [llvm-profdata][llvm-cov]Fix double-counted coverage for same-named functions across binaries (PR #153679)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 14 13:52:47 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-pgo
Author: None (awearden)
<details>
<summary>Changes</summary>
This PR is addressing this specific issue: #<!-- -->148279
Right now, the way llvm-profdata handles the merging of coverage reports with two binaries with the same function name leads to errors with the coverage counts. Looking at the mentioned issue, we can see that the coverage counts on the main function are counted twice for each binary the main function ran on, but because the main function only ran once on each binary, the coverage count should be “1”. This can cause issues when comparing coverage across architectures where the same function with different implementation exists in multiple binaries.
This patch fixes this issue by adding the --object-aware-hashing flag.
Example usage:
`llvm-profdata merge --object-aware-hashing=exec1 exec1.profraw --object-aware-hashing=exec2 exec2.profraw -o merged.profdata`
By adding this flag, we can associate each profraw file with the executable it comes from. This will hash the function names with the object file and separate the functions by object file. Then by adding the --merge-binary-coverage flag to llvm-cov, it will merge the coverage counts from the different binaries by considering the object file in the hash.
Example usage:
`llvm-cov show --instr-profile=merged.profdata --object=exec1 --object=exec2 --merge-binary-coverage.`
Using these two flags in conjunction with each other will give the correct counts on each function.
@<!-- -->quic-akaryaki
@<!-- -->quic-areg
@<!-- -->quic-seaswara
@<!-- -->evodius96
@<!-- -->chapuni
---
Patch is 26.83 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/153679.diff
12 Files Affected:
- (modified) llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h (+8-4)
- (modified) llvm/include/llvm/ProfileData/InstrProf.h (+17-2)
- (modified) llvm/include/llvm/ProfileData/InstrProfWriter.h (+4-2)
- (modified) llvm/lib/ProfileData/Coverage/CoverageMapping.cpp (+71-20)
- (modified) llvm/lib/ProfileData/InstrProfWriter.cpp (+11-3)
- (added) llvm/test/tools/llvm-cov/Inputs/merge-same-func-bin1-2.c (+5)
- (added) llvm/test/tools/llvm-cov/Inputs/merge-same-func-bin1.c (+8)
- (added) llvm/test/tools/llvm-cov/Inputs/merge-same-func-bin2.c (+2)
- (added) llvm/test/tools/llvm-cov/merge-same-func-diff-bin.c (+38)
- (modified) llvm/tools/llvm-cov/CodeCoverage.cpp (+11-3)
- (modified) llvm/tools/llvm-cov/CoverageViewOptions.h (+1)
- (modified) llvm/tools/llvm-profdata/llvm-profdata.cpp (+44-25)
``````````diff
diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
index 7d1a85ba528fc..8b87b4337a75e 100644
--- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
+++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
@@ -993,6 +993,7 @@ class CoverageMapping {
std::vector<FunctionRecord> Functions;
DenseMap<size_t, SmallVector<unsigned, 0>> FilenameHash2RecordIndices;
std::vector<std::pair<std::string, uint64_t>> FuncHashMismatches;
+ DenseMap<std::pair<size_t, hash_code>, unsigned> RecordIndices;
std::optional<bool> SingleByteCoverage;
@@ -1003,7 +1004,8 @@ class CoverageMapping {
ArrayRef<std::unique_ptr<CoverageMappingReader>> CoverageReaders,
std::optional<std::reference_wrapper<IndexedInstrProfReader>>
&ProfileReader,
- CoverageMapping &Coverage);
+ CoverageMapping &Coverage, StringRef ObjectFilename = "",
+ bool MergeBinaryCoverage = false);
// Load coverage records from file.
static Error
@@ -1011,13 +1013,15 @@ class CoverageMapping {
std::optional<std::reference_wrapper<IndexedInstrProfReader>>
&ProfileReader,
CoverageMapping &Coverage, bool &DataFound,
- SmallVectorImpl<object::BuildID> *FoundBinaryIDs = nullptr);
+ SmallVectorImpl<object::BuildID> *FoundBinaryIDs = nullptr,
+ StringRef ObjectFilename = "", bool MergeBinaryCoverage = false);
/// Add a function record corresponding to \p Record.
Error loadFunctionRecord(
const CoverageMappingRecord &Record,
const std::optional<std::reference_wrapper<IndexedInstrProfReader>>
- &ProfileReader);
+ &ProfileReader,
+ StringRef ObjectFilename = "", bool MergeBinaryCoverage = false);
/// Look up the indices for function records which are at least partially
/// defined in the specified file. This is guaranteed to return a superset of
@@ -1044,7 +1048,7 @@ class CoverageMapping {
std::optional<StringRef> ProfileFilename, vfs::FileSystem &FS,
ArrayRef<StringRef> Arches = {}, StringRef CompilationDir = "",
const object::BuildIDFetcher *BIDFetcher = nullptr,
- bool CheckBinaryIDs = false);
+ bool CheckBinaryIDs = false, bool MergeBinaryCoverage = false);
/// The number of functions that couldn't have their profiles mapped.
///
diff --git a/llvm/include/llvm/ProfileData/InstrProf.h b/llvm/include/llvm/ProfileData/InstrProf.h
index 85a9efe73855b..a41756e7d0dff 100644
--- a/llvm/include/llvm/ProfileData/InstrProf.h
+++ b/llvm/include/llvm/ProfileData/InstrProf.h
@@ -513,6 +513,7 @@ class InstrProfSymtab {
LLVM_ABI static StringRef getCanonicalName(StringRef PGOName);
private:
+ StringRef ObjectFilename;
using AddrIntervalMap =
IntervalMap<uint64_t, uint64_t, 4, IntervalMapHalfOpenInfo<uint64_t>>;
StringRef Data;
@@ -640,10 +641,18 @@ class InstrProfSymtab {
// Insert into NameTab so that MD5NameMap (a vector that will be sorted)
// won't have duplicated entries in the first place.
+ uint64_t HashValue = IndexedInstrProf::ComputeHash(SymbolName);
+ std::string HashStr(std::to_string(HashValue));
+ // if ObjectFilename is not empty from the --object-aware-hashing flag, add
+ // ObjectFilename to hash context.
+ if (!ObjectFilename.empty()) {
+ std::string CombinedStr = HashStr + ":" + ObjectFilename.str();
+ StringRef HashRef = CombinedStr;
+ HashValue = IndexedInstrProf::ComputeHash(HashRef);
+ }
auto Ins = NameTab.insert(SymbolName);
if (Ins.second) {
- MD5NameMap.push_back(std::make_pair(
- IndexedInstrProf::ComputeHash(SymbolName), Ins.first->getKey()));
+ MD5NameMap.push_back(std::make_pair(HashValue, Ins.first->getKey()));
Sorted = false;
}
return Error::success();
@@ -777,6 +786,12 @@ StringRef InstrProfSymtab::getFuncOrVarNameIfDefined(uint64_t MD5Hash) const {
StringRef InstrProfSymtab::getFuncOrVarName(uint64_t MD5Hash) const {
finalizeSymtab();
+ std::string TempMD5HashStr = std::to_string(MD5Hash);
+ if (!ObjectFilename.empty()) {
+ std::string CombinedHashStr = TempMD5HashStr + ":" + ObjectFilename.str();
+ llvm::StringRef CombinedHashRef(CombinedHashStr);
+ MD5Hash = IndexedInstrProf::ComputeHash(CombinedHashRef);
+ }
auto Result = llvm::lower_bound(MD5NameMap, MD5Hash,
[](const std::pair<uint64_t, StringRef> &LHS,
uint64_t RHS) { return LHS.first < RHS; });
diff --git a/llvm/include/llvm/ProfileData/InstrProfWriter.h b/llvm/include/llvm/ProfileData/InstrProfWriter.h
index 1b24425e68a9e..4375b280e50d3 100644
--- a/llvm/include/llvm/ProfileData/InstrProfWriter.h
+++ b/llvm/include/llvm/ProfileData/InstrProfWriter.h
@@ -113,7 +113,8 @@ class InstrProfWriter {
/// for this function and the hash and number of counts match, each counter is
/// summed. Optionally scale counts by \p Weight.
LLVM_ABI void addRecord(NamedInstrProfRecord &&I, uint64_t Weight,
- function_ref<void(Error)> Warn);
+ function_ref<void(Error)> Warn,
+ StringRef ObjectFilename = "");
void addRecord(NamedInstrProfRecord &&I, function_ref<void(Error)> Warn) {
addRecord(std::move(I), 1, Warn);
}
@@ -224,7 +225,8 @@ class InstrProfWriter {
private:
void addRecord(StringRef Name, uint64_t Hash, InstrProfRecord &&I,
- uint64_t Weight, function_ref<void(Error)> Warn);
+ uint64_t Weight, function_ref<void(Error)> Warn,
+ StringRef ObjectFilename = "");
bool shouldEncodeData(const ProfilingData &PD);
/// Add a memprof record for a function identified by its \p Id.
diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index 429ec5c19f1f8..8476800e443e3 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -824,7 +824,8 @@ class MCDCDecisionRecorder {
Error CoverageMapping::loadFunctionRecord(
const CoverageMappingRecord &Record,
const std::optional<std::reference_wrapper<IndexedInstrProfReader>>
- &ProfileReader) {
+ &ProfileReader,
+ StringRef ObjectFilename, bool MergeBinaryCoverage) {
StringRef OrigFuncName = Record.FunctionName;
if (OrigFuncName.empty())
return make_error<CoverageMapError>(coveragemap_error::malformed,
@@ -837,14 +838,21 @@ Error CoverageMapping::loadFunctionRecord(
CounterMappingContext Ctx(Record.Expressions);
+ uint64_t FuncObjectHash = Record.FunctionHash;
+ if (!ObjectFilename.empty() && MergeBinaryCoverage) {
+ std::string HashStr =
+ std::to_string(Record.FunctionHash) + ":" + ObjectFilename.str();
+ llvm::StringRef HashRef(HashStr);
+ FuncObjectHash = IndexedInstrProf::ComputeHash(HashRef);
+ }
std::vector<uint64_t> Counts;
if (ProfileReader) {
if (Error E = ProfileReader.value().get().getFunctionCounts(
- Record.FunctionName, Record.FunctionHash, Counts)) {
+ Record.FunctionName, FuncObjectHash, Counts)) {
instrprof_error IPE = std::get<0>(InstrProfError::take(std::move(E)));
if (IPE == instrprof_error::hash_mismatch) {
FuncHashMismatches.emplace_back(std::string(Record.FunctionName),
- Record.FunctionHash);
+ FuncObjectHash);
return Error::success();
}
if (IPE != instrprof_error::unknown_function)
@@ -863,11 +871,11 @@ Error CoverageMapping::loadFunctionRecord(
BitVector Bitmap;
if (ProfileReader) {
if (Error E = ProfileReader.value().get().getFunctionBitmap(
- Record.FunctionName, Record.FunctionHash, Bitmap)) {
+ Record.FunctionName, FuncObjectHash, Bitmap)) {
instrprof_error IPE = std::get<0>(InstrProfError::take(std::move(E)));
if (IPE == instrprof_error::hash_mismatch) {
FuncHashMismatches.emplace_back(std::string(Record.FunctionName),
- Record.FunctionHash);
+ FuncObjectHash);
return Error::success();
}
if (IPE != instrprof_error::unknown_function)
@@ -942,11 +950,46 @@ Error CoverageMapping::loadFunctionRecord(
Function.pushMCDCRecord(std::move(*Record));
}
- // Don't create records for (filenames, function) pairs we've already seen.
auto FilenamesHash = hash_combine_range(Record.Filenames);
- if (!RecordProvenance[FilenamesHash].insert(hash_value(OrigFuncName)).second)
- return Error::success();
+ std::string HashStr = OrigFuncName.str();
+ auto LogicalFuncKey = std::make_pair(FilenamesHash, hash_value(OrigFuncName));
+ auto It = RecordIndices.find(LogicalFuncKey);
+ if (It != RecordIndices.end()) {
+ auto &ExistingFunction = Functions[It->second];
+ // Create a map of existing regions for lookup.
+ // The key uniquely identifies the source region.
+ using RegionKey =
+ std::tuple<unsigned, unsigned, unsigned, unsigned, unsigned>;
+ std::map<RegionKey, CountedRegion *> ExistingRegionsMap;
+ for (auto &ExistingRegion : ExistingFunction.CountedRegions) {
+ RegionKey Key = {ExistingRegion.FileID, ExistingRegion.LineStart,
+ ExistingRegion.ColumnStart, ExistingRegion.LineEnd,
+ ExistingRegion.ColumnEnd};
+ ExistingRegionsMap[Key] = &ExistingRegion;
+ }
+ // Merge the new regions into the existing function's regions.
+ for (const auto &NewRegion : Function.CountedRegions) {
+ RegionKey Key = {NewRegion.FileID, NewRegion.LineStart,
+ NewRegion.ColumnStart, NewRegion.LineEnd,
+ NewRegion.ColumnEnd};
+ auto MapIt = ExistingRegionsMap.find(Key);
+ if (MapIt != ExistingRegionsMap.end()) {
+ // Region already exists, merge counts by summing the counts.
+ CountedRegion *ExistingRegion = MapIt->second;
+ ExistingRegion->ExecutionCount += NewRegion.ExecutionCount;
+ } else {
+ ExistingFunction.CountedRegions.push_back(NewRegion);
+ }
+ }
+ return Error::success();
+ }
+ RecordIndices.insert({LogicalFuncKey, Functions.size()});
+ // Don't create records for (filenames, function) pairs we've already seen.
+ StringRef Hash(HashStr);
+ if (!RecordProvenance[FilenamesHash].insert(hash_value(Hash)).second) {
+ return Error::success();
+ }
Functions.push_back(std::move(Function));
// Performance optimization: keep track of the indices of the function records
@@ -971,7 +1014,8 @@ Error CoverageMapping::loadFromReaders(
ArrayRef<std::unique_ptr<CoverageMappingReader>> CoverageReaders,
std::optional<std::reference_wrapper<IndexedInstrProfReader>>
&ProfileReader,
- CoverageMapping &Coverage) {
+ CoverageMapping &Coverage, StringRef ObjectFilename,
+ bool MergeBinaryCoverage) {
assert(!Coverage.SingleByteCoverage || !ProfileReader ||
*Coverage.SingleByteCoverage ==
ProfileReader.value().get().hasSingleByteCoverage());
@@ -982,7 +1026,8 @@ Error CoverageMapping::loadFromReaders(
if (Error E = RecordOrErr.takeError())
return E;
const auto &Record = *RecordOrErr;
- if (Error E = Coverage.loadFunctionRecord(Record, ProfileReader))
+ if (Error E = Coverage.loadFunctionRecord(
+ Record, ProfileReader, ObjectFilename, MergeBinaryCoverage))
return E;
}
}
@@ -1013,7 +1058,8 @@ Error CoverageMapping::loadFromFile(
std::optional<std::reference_wrapper<IndexedInstrProfReader>>
&ProfileReader,
CoverageMapping &Coverage, bool &DataFound,
- SmallVectorImpl<object::BuildID> *FoundBinaryIDs) {
+ SmallVectorImpl<object::BuildID> *FoundBinaryIDs, StringRef ObjectFilename,
+ bool MergeBinaryCoverage) {
auto CovMappingBufOrErr = MemoryBuffer::getFileOrSTDIN(
Filename, /*IsText=*/false, /*RequiresNullTerminator=*/false);
if (std::error_code EC = CovMappingBufOrErr.getError())
@@ -1043,16 +1089,19 @@ Error CoverageMapping::loadFromFile(
}));
}
DataFound |= !Readers.empty();
- if (Error E = loadFromReaders(Readers, ProfileReader, Coverage))
+ if (Error E = loadFromReaders(Readers, ProfileReader, Coverage,
+ ObjectFilename, MergeBinaryCoverage))
return createFileError(Filename, std::move(E));
return Error::success();
}
-Expected<std::unique_ptr<CoverageMapping>> CoverageMapping::load(
- ArrayRef<StringRef> ObjectFilenames,
- std::optional<StringRef> ProfileFilename, vfs::FileSystem &FS,
- ArrayRef<StringRef> Arches, StringRef CompilationDir,
- const object::BuildIDFetcher *BIDFetcher, bool CheckBinaryIDs) {
+Expected<std::unique_ptr<CoverageMapping>>
+CoverageMapping::load(ArrayRef<StringRef> ObjectFilenames,
+ std::optional<StringRef> ProfileFilename,
+ vfs::FileSystem &FS, ArrayRef<StringRef> Arches,
+ StringRef CompilationDir,
+ const object::BuildIDFetcher *BIDFetcher,
+ bool CheckBinaryIDs, bool MergeBinaryCoverage) {
std::unique_ptr<IndexedInstrProfReader> ProfileReader;
if (ProfileFilename) {
auto ProfileReaderOrErr =
@@ -1079,9 +1128,11 @@ Expected<std::unique_ptr<CoverageMapping>> CoverageMapping::load(
SmallVector<object::BuildID> FoundBinaryIDs;
for (const auto &File : llvm::enumerate(ObjectFilenames)) {
- if (Error E = loadFromFile(File.value(), GetArch(File.index()),
- CompilationDir, ProfileReaderRef, *Coverage,
- DataFound, &FoundBinaryIDs))
+ if (Error E = loadFromFile(
+ File.value(), GetArch(File.index()), CompilationDir,
+ ProfileReaderRef, *Coverage, DataFound, &FoundBinaryIDs,
+ MergeBinaryCoverage ? ObjectFilenames[File.index()] : "",
+ MergeBinaryCoverage))
return std::move(E);
}
diff --git a/llvm/lib/ProfileData/InstrProfWriter.cpp b/llvm/lib/ProfileData/InstrProfWriter.cpp
index df807fc02b910..c4661652cb133 100644
--- a/llvm/lib/ProfileData/InstrProfWriter.cpp
+++ b/llvm/lib/ProfileData/InstrProfWriter.cpp
@@ -154,10 +154,11 @@ void InstrProfWriter::setValueProfDataEndianness(llvm::endianness Endianness) {
void InstrProfWriter::setOutputSparse(bool Sparse) { this->Sparse = Sparse; }
void InstrProfWriter::addRecord(NamedInstrProfRecord &&I, uint64_t Weight,
- function_ref<void(Error)> Warn) {
+ function_ref<void(Error)> Warn,
+ StringRef ObjectFilename) {
auto Name = I.Name;
auto Hash = I.Hash;
- addRecord(Name, Hash, std::move(I), Weight, Warn);
+ addRecord(Name, Hash, std::move(I), Weight, Warn, ObjectFilename);
}
void InstrProfWriter::overlapRecord(NamedInstrProfRecord &&Other,
@@ -193,9 +194,16 @@ void InstrProfWriter::overlapRecord(NamedInstrProfRecord &&Other,
void InstrProfWriter::addRecord(StringRef Name, uint64_t Hash,
InstrProfRecord &&I, uint64_t Weight,
- function_ref<void(Error)> Warn) {
+ function_ref<void(Error)> Warn,
+ StringRef ObjectFilename) {
auto &ProfileDataMap = FunctionData[Name];
+ // Add object file name to hash value if --object-aware-hashing flag is used.
+ if (!ObjectFilename.empty()) {
+ std::string HashStr = std::to_string(Hash) + ":" + ObjectFilename.str();
+ llvm::StringRef HashRef(HashStr);
+ Hash = IndexedInstrProf::ComputeHash(HashRef);
+ }
auto [Where, NewFunc] = ProfileDataMap.try_emplace(Hash);
InstrProfRecord &Dest = Where->second;
diff --git a/llvm/test/tools/llvm-cov/Inputs/merge-same-func-bin1-2.c b/llvm/test/tools/llvm-cov/Inputs/merge-same-func-bin1-2.c
new file mode 100644
index 0000000000000..428cbfd163a72
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/Inputs/merge-same-func-bin1-2.c
@@ -0,0 +1,5 @@
+int foo() { return 0; }
+
+int bar() { return 0; }
+
+int bun() { return 0; }
diff --git a/llvm/test/tools/llvm-cov/Inputs/merge-same-func-bin1.c b/llvm/test/tools/llvm-cov/Inputs/merge-same-func-bin1.c
new file mode 100644
index 0000000000000..863b2baa017f6
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/Inputs/merge-same-func-bin1.c
@@ -0,0 +1,8 @@
+extern int foo();
+extern int bar();
+extern int bun();
+
+
+int main() {
+ return foo() + bar() + bun();
+}
diff --git a/llvm/test/tools/llvm-cov/Inputs/merge-same-func-bin2.c b/llvm/test/tools/llvm-cov/Inputs/merge-same-func-bin2.c
new file mode 100644
index 0000000000000..2fcea0fce9546
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/Inputs/merge-same-func-bin2.c
@@ -0,0 +1,2 @@
+int baz() { return 0; }
+int main() { return 1; }
diff --git a/llvm/test/tools/llvm-cov/merge-same-func-diff-bin.c b/llvm/test/tools/llvm-cov/merge-same-func-diff-bin.c
new file mode 100644
index 0000000000000..f09192daa175c
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/merge-same-func-diff-bin.c
@@ -0,0 +1,38 @@
+// RUN: clang -O0 -fprofile-instr-generate -fcoverage-mapping \
+// RUN: %S/Inputs/merge-same-func-bin1.c %S/Inputs/merge-same-func-bin1-2.c -o %t.bin1
+// RUN: %t.bin1
+// RUN: cp default.profraw %t.bin1.profraw
+//
+
+// RUN: clang -O0 -fprofile-instr-generate -fcoverage-mapping \
+// RUN: %S/Inputs/merge-same-func-bin2.c %S/Inputs/merge-same-func-bin1-2.c -o %t.bin2
+// RUN: %t.bin2 || true
+// RUN: cp default.profraw %t.bin2.profraw
+//---------------- merge the raw profiles ------------------------------------//
+// RUN: llvm-profdata merge --object-aware-hashing=%t.bin2 %t.bin2.profraw \
+// RUN: --object-aware-hashing=%t.bin1 %t.bin1.profraw \
+// RUN: -o %t.profdata
+//
+
+// RUN: llvm-cov show -instr-profile=%t.profdata --object=%t.bin2 --object=%t.bin1 --merge-binary-coverage | FileCheck %s
+//
+// CHECK-LABEL: {{.*merge-same-func-bin1-2\.c}}:
+// CHECK: 1| 1|int foo() { return 0; }
+// CHECK: 2| |
+// CHECK: 3| 1|int bar() { return 0; }
+// CHECK: 4| |
+// CHECK: 5| 1|int bun() { return 0; }
+//
+// CHECK-LABEL: {{.*merge-same-func-bin1\.c}}:
+// CHECK: 1| |extern int foo();
+// CHECK: 2| |extern int bar();
+// CHECK: 3| |extern int bun();
+// CHECK: 4| |
+// CHECK: 5| |
+// CHECK: 6| 1|int main()
+// CHECK: 7| 1| return foo() + bar() + bun();
+// CHECK: 8| 1|}
+//
+// CHECK-LABEL: {{.*merge-same-func-bin2\.c}}:
+// CHECK: 1| 0|int baz() { return 0; }
+// CHECK: 2| 1|int main() { return 1; }
diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp
index 6c66858c4de8c..f827a6c3620b8 100644
--- a/llvm/tools/llvm-cov/CodeCoverage.cpp
+++ b/llvm/tools/llvm-cov/CodeCoverage.cpp
@@ -462,9 +462,10 @@ std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
ObjectFilename);
}
auto FS = vfs::getRealFileSystem();
- auto CoverageOrErr = CoverageMapping::load(
- ObjectFilenames, PGOFilename, *FS, CoverageArches,
- ViewOpts.CompilationDirectory, BIDFetcher.get(), CheckBinaryIDs);
+ auto CoverageOrErr =
+ CoverageMapping::load(ObjectFilenames, PGOFilename, *FS, CoverageArches,
+ ViewOpts.CompilationDirectory, BIDFetcher.get(),
+ CheckBinaryIDs, ViewOpts.MergeBinaryCoverage);
if (Error E = CoverageOrErr.takeError()) {
error("failed to load coverage: " + toString(std::move(E)));
return nullptr;
@@ -801,6 +802,12 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
"check-binary-ids", cl::desc("Fail if an object couldn't be found for a "
"binary ID in the profile"));
+ cl::opt<bool> MergeBinaryCoverage(
+ "merge-binary-coverage",
+ cl::desc("Enable merging of coverage profiles from binaries compiled for "
+ "different architectures"),
+ cl::init(false));
+
auto commandLineParser = [&, this](int argc, const char **a...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/153679
More information about the llvm-commits
mailing list