llvm.org GIT mirror llvm / 88a1274
[Coverage] Fix an issue where improper coverage mapping data could be loaded for an inline function. If an inline function is observed but unused in a translation unit, dummy coverage mapping data with zero hash is stored for this function. If such a coverage mapping section came earlier than real one, the latter was ignored. As a result, llvm-cov was unable to show coverage information for those functions. Differential Revision: http://reviews.llvm.org/D20286 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@270194 91177308-0d34-0410-b5e6-96231b3b80d8 Igor Kudrin 4 years ago
6 changed file(s) with 152 addition(s) and 18 deletion(s). Raw diff Collapse all Expand all
102102 Error read();
103103 };
104104
105 /// \brief Checks if the given coverage mapping data is exported for
106 /// an unused function.
107 class RawCoverageMappingDummyChecker : public RawCoverageReader {
108 public:
109 RawCoverageMappingDummyChecker(StringRef MappingData)
110 : RawCoverageReader(MappingData) {}
111
112 Expected isDummy();
113 };
114
105115 /// \brief Reader for the raw coverage mapping data.
106116 class RawCoverageMappingReader : public RawCoverageReader {
107117 ArrayRef TranslationUnitFilenames;
1212 //===----------------------------------------------------------------------===//
1313
1414 #include "llvm/ProfileData/Coverage/CoverageMappingReader.h"
15 #include "llvm/ADT/DenseSet.h"
15 #include "llvm/ADT/DenseMap.h"
1616 #include "llvm/Object/MachOUniversal.h"
1717 #include "llvm/Object/ObjectFile.h"
1818 #include "llvm/Support/Debug.h"
293293 return Error::success();
294294 }
295295
296 Expected RawCoverageMappingDummyChecker::isDummy() {
297 // A dummy coverage mapping data consists of just one region with zero count.
298 uint64_t NumFileMappings;
299 if (Error Err = readSize(NumFileMappings))
300 return std::move(Err);
301 if (NumFileMappings != 1)
302 return false;
303 // We don't expect any specific value for the filename index, just skip it.
304 uint64_t FilenameIndex;
305 if (Error Err =
306 readIntMax(FilenameIndex, std::numeric_limits::max()))
307 return std::move(Err);
308 uint64_t NumExpressions;
309 if (Error Err = readSize(NumExpressions))
310 return std::move(Err);
311 if (NumExpressions != 0)
312 return false;
313 uint64_t NumRegions;
314 if (Error Err = readSize(NumRegions))
315 return std::move(Err);
316 if (NumRegions != 1)
317 return false;
318 uint64_t EncodedCounterAndRegion;
319 if (Error Err = readIntMax(EncodedCounterAndRegion,
320 std::numeric_limits::max()))
321 return std::move(Err);
322 unsigned Tag = EncodedCounterAndRegion & Counter::EncodingTagMask;
323 return Tag == Counter::Zero;
324 }
325
296326 Error InstrProfSymtab::create(SectionRef &Section) {
297327 if (auto EC = Section.getContents(Data))
298328 return errorCodeToError(EC);
307337 if (Offset + Size > Data.size())
308338 return StringRef();
309339 return Data.substr(Pointer - Address, Size);
340 }
341
342 // Check if the mapping data is a dummy, i.e. is emitted for an unused function.
343 static Expected isCoverageMappingDummy(uint64_t Hash, StringRef Mapping) {
344 // The hash value of dummy mapping records is always zero.
345 if (Hash)
346 return false;
347 return RawCoverageMappingDummyChecker(Mapping).isDummy();
310348 }
311349
312350 namespace {
333371 typedef typename coverage::CovMapTraits::NameRefType
334372 NameRefType;
335373
336 llvm::DenseSet UniqueFunctionMappingData;
374 // Maps function's name references to the indexes of their records
375 // in \c Records.
376 llvm::DenseMap FunctionRecords;
337377 InstrProfSymtab &ProfileNames;
338378 std::vector &Filenames;
339379 std::vector &Records;
380
381 // Add the record to the collection if we don't already have a record that
382 // points to the same function name. This is useful to ignore the redundant
383 // records for the functions with ODR linkage.
384 // In addition, prefer records with real coverage mapping data to dummy
385 // records, which were emitted for inline functions which were seen but
386 // not used in the corresponding translation unit.
387 Error insertFunctionRecordIfNeeded(const FuncRecordType *CFR,
388 StringRef Mapping, size_t FilenamesBegin) {
389 uint64_t FuncHash = CFR->template getFuncHash();
390 NameRefType NameRef = CFR->template getFuncNameRef();
391 auto InsertResult =
392 FunctionRecords.insert(std::make_pair(NameRef, Records.size()));
393 if (InsertResult.second) {
394 StringRef FuncName;
395 if (Error Err = CFR->template getFuncName(ProfileNames, FuncName))
396 return Err;
397 Records.emplace_back(Version, FuncName, FuncHash, Mapping, FilenamesBegin,
398 Filenames.size() - FilenamesBegin);
399 return Error::success();
400 }
401 // Update the existing record if it's a dummy and the new record is real.
402 size_t OldRecordIndex = InsertResult.first->second;
403 BinaryCoverageReader::ProfileMappingRecord &OldRecord =
404 Records[OldRecordIndex];
405 Expected OldIsDummyExpected = isCoverageMappingDummy(
406 OldRecord.FunctionHash, OldRecord.CoverageMapping);
407 if (Error Err = OldIsDummyExpected.takeError())
408 return Err;
409 if (!*OldIsDummyExpected)
410 return Error::success();
411 Expected NewIsDummyExpected =
412 isCoverageMappingDummy(FuncHash, Mapping);
413 if (Error Err = NewIsDummyExpected.takeError())
414 return Err;
415 if (*NewIsDummyExpected)
416 return Error::success();
417 OldRecord.FunctionHash = FuncHash;
418 OldRecord.CoverageMapping = Mapping;
419 OldRecord.FilenamesBegin = FilenamesBegin;
420 OldRecord.FilenamesSize = Filenames.size() - FilenamesBegin;
421 return Error::success();
422 }
340423
341424 public:
342425 VersionedCovMapFuncRecordReader(
386469 while ((const char *)CFR < FunEnd) {
387470 // Read the function information
388471 uint32_t DataSize = CFR->template getDataSize();
389 uint64_t FuncHash = CFR->template getFuncHash();
390472
391473 // Now use that to read the coverage data.
392474 if (CovBuf + DataSize > CovEnd)
394476 auto Mapping = StringRef(CovBuf, DataSize);
395477 CovBuf += DataSize;
396478
397 // Ignore this record if we already have a record that points to the same
398 // function name. This is useful to ignore the redundant records for the
399 // functions with ODR linkage.
400 NameRefType NameRef = CFR->template getFuncNameRef();
401 if (!UniqueFunctionMappingData.insert(NameRef).second) {
402 CFR++;
403 continue;
404 }
405
406 StringRef FuncName;
407 if (Error E = CFR->template getFuncName(ProfileNames, FuncName))
408 return E;
409 Records.push_back(BinaryCoverageReader::ProfileMappingRecord(
410 Version, FuncName, FuncHash, Mapping, FilenamesBegin,
411 Filenames.size() - FilenamesBegin));
479 if (Error Err =
480 insertFunctionRecordIfNeeded(CFR, Mapping, FilenamesBegin))
481 return Err;
412482 CFR++;
413483 }
414484 return Error::success();
0 #include "prefer_used_to_unused.h"
1
2 int main() {
3 return sampleFunc(5) + simpleFunc(5);
4 }
0 _Z10sampleFunci
1 # Func Hash:
2 10
3 # Num Counters:
4 2
5 # Counter Values:
6 1
7 1
8
9 main
10 # Func Hash:
11 0
12 # Num Counters:
13 1
14 # Counter Values:
15 1
16
17 _Z10simpleFunci
18 # Func Hash:
19 0
20 # Num Counters:
21 1
22 # Counter Values:
23 1
24
0 // Check that llvm-cov loads real coverage mapping data for a function
1 // even though dummy coverage data for that function comes first.
2 // Dummy coverage data is exported if the definition of an inline function
3 // is seen but the function is not used in the translation unit.
4
5 // If you need to rebuild the 'covmapping' file for this test, please use
6 // the following commands:
7 // clang++ -fprofile-instr-generate -fcoverage-mapping -o tmp -x c++ prefer_used_to_unused.h prefer_used_to_unused.cpp
8 // llvm-cov convert-for-testing -o prefer_used_to_unused.covmapping tmp
9
10 // RUN: llvm-profdata merge %S/Inputs/prefer_used_to_unused.proftext -o %t.profdata
11 // RUN: llvm-cov show %S/Inputs/prefer_used_to_unused.covmapping -instr-profile %t.profdata -filename-equivalence %s | FileCheck %s
12
13 // Coverage data for this function has a non-zero hash value if it is used in the translation unit.
14 inline int sampleFunc(int A) { // CHECK: 1| [[@LINE]]|inline int sampleFunc(int A) {
15 if (A > 0) // CHECK-NEXT: 1| [[@LINE]]| if (A > 0)
16 return A; // CHECK-NEXT: 1| [[@LINE]]| return A;
17 return 0; // CHECK-NEXT: 0| [[@LINE]]| return 0;
18 } // CHECK-NEXT: 1| [[@LINE]]|}
19
20 // The hash for this function is zero in both cases, either it is used in the translation unit or not.
21 inline int simpleFunc(int A) { // CHECK: 1| [[@LINE]]|inline int simpleFunc(int A) {
22 return A; // CHECK-NEXT: 1| [[@LINE]]| return A;
23 } // CHECK-NEXT: 1| [[@LINE]]|}