diff options
Diffstat (limited to 'gnu/llvm/lib/ProfileData')
| -rw-r--r-- | gnu/llvm/lib/ProfileData/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | gnu/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp | 287 | ||||
| -rw-r--r-- | gnu/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp | 41 | ||||
| -rw-r--r-- | gnu/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp | 8 | ||||
| -rw-r--r-- | gnu/llvm/lib/ProfileData/GCOV.cpp | 821 | ||||
| -rw-r--r-- | gnu/llvm/lib/ProfileData/InstrProf.cpp | 9 | ||||
| -rw-r--r-- | gnu/llvm/lib/ProfileData/InstrProfReader.cpp | 6 | ||||
| -rw-r--r-- | gnu/llvm/lib/ProfileData/ProfileSummaryBuilder.cpp | 1 | ||||
| -rw-r--r-- | gnu/llvm/lib/ProfileData/SampleProfReader.cpp | 6 | ||||
| -rw-r--r-- | gnu/llvm/lib/ProfileData/SampleProfWriter.cpp | 9 |
10 files changed, 1093 insertions, 96 deletions
diff --git a/gnu/llvm/lib/ProfileData/CMakeLists.txt b/gnu/llvm/lib/ProfileData/CMakeLists.txt index cd65762ae6a..3a981d8acf4 100644 --- a/gnu/llvm/lib/ProfileData/CMakeLists.txt +++ b/gnu/llvm/lib/ProfileData/CMakeLists.txt @@ -1,4 +1,5 @@ add_llvm_library(LLVMProfileData + GCOV.cpp InstrProf.cpp InstrProfReader.cpp InstrProfWriter.cpp diff --git a/gnu/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/gnu/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp index 8c5f136ea27..8dbd58632f0 100644 --- a/gnu/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp +++ b/gnu/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp @@ -33,6 +33,7 @@ #include <cassert> #include <cstdint> #include <iterator> +#include <map> #include <memory> #include <string> #include <system_error> @@ -217,7 +218,7 @@ Error CoverageMapping::loadFunctionRecord( Record.FunctionHash, Counts)) { instrprof_error IPE = InstrProfError::take(std::move(E)); if (IPE == instrprof_error::hash_mismatch) { - MismatchedFunctionCount++; + FuncHashMismatches.emplace_back(Record.FunctionName, Record.FunctionHash); return Error::success(); } else if (IPE != instrprof_error::unknown_function) return make_error<InstrProfError>(IPE); @@ -237,7 +238,8 @@ Error CoverageMapping::loadFunctionRecord( Function.pushRegion(Region, *ExecutionCount); } if (Function.CountedRegions.size() != Record.MappingRegions.size()) { - MismatchedFunctionCount++; + FuncCounterMismatches.emplace_back(Record.FunctionName, + Function.CountedRegions.size()); return Error::success(); } @@ -250,17 +252,22 @@ Expected<std::unique_ptr<CoverageMapping>> CoverageMapping::load( IndexedInstrProfReader &ProfileReader) { auto Coverage = std::unique_ptr<CoverageMapping>(new CoverageMapping()); - for (const auto &CoverageReader : CoverageReaders) - for (const auto &Record : *CoverageReader) + for (const auto &CoverageReader : CoverageReaders) { + for (auto RecordOrErr : *CoverageReader) { + if (Error E = RecordOrErr.takeError()) + return std::move(E); + const auto &Record = *RecordOrErr; if (Error E = Coverage->loadFunctionRecord(Record, ProfileReader)) return std::move(E); + } + } return std::move(Coverage); } Expected<std::unique_ptr<CoverageMapping>> CoverageMapping::load(ArrayRef<StringRef> ObjectFilenames, - StringRef ProfileFilename, StringRef Arch) { + StringRef ProfileFilename, ArrayRef<StringRef> Arches) { auto ProfileReaderOrErr = IndexedInstrProfReader::create(ProfileFilename); if (Error E = ProfileReaderOrErr.takeError()) return std::move(E); @@ -268,10 +275,11 @@ CoverageMapping::load(ArrayRef<StringRef> ObjectFilenames, SmallVector<std::unique_ptr<CoverageMappingReader>, 4> Readers; SmallVector<std::unique_ptr<MemoryBuffer>, 4> Buffers; - for (StringRef ObjectFilename : ObjectFilenames) { - auto CovMappingBufOrErr = MemoryBuffer::getFileOrSTDIN(ObjectFilename); + for (const auto &File : llvm::enumerate(ObjectFilenames)) { + auto CovMappingBufOrErr = MemoryBuffer::getFileOrSTDIN(File.value()); if (std::error_code EC = CovMappingBufOrErr.getError()) return errorCodeToError(EC); + StringRef Arch = Arches.empty() ? StringRef() : Arches[File.index()]; auto CoverageReaderOrErr = BinaryCoverageReader::create(CovMappingBufOrErr.get(), Arch); if (Error E = CoverageReaderOrErr.takeError()) @@ -289,8 +297,7 @@ namespace { /// An instantiation set is a collection of functions that have the same source /// code, ie, template functions specializations. class FunctionInstantiationSetCollector { - using MapT = DenseMap<std::pair<unsigned, unsigned>, - std::vector<const FunctionRecord *>>; + using MapT = std::map<LineColPair, std::vector<const FunctionRecord *>>; MapT InstantiatedFunctions; public: @@ -313,59 +320,139 @@ class SegmentBuilder { SegmentBuilder(std::vector<CoverageSegment> &Segments) : Segments(Segments) {} - /// Start a segment with no count specified. - void startSegment(unsigned Line, unsigned Col) { - DEBUG(dbgs() << "Top level segment at " << Line << ":" << Col << "\n"); - Segments.emplace_back(Line, Col, /*IsRegionEntry=*/false); - } + /// Emit a segment with the count from \p Region starting at \p StartLoc. + // + /// \p IsRegionEntry: The segment is at the start of a new non-gap region. + /// \p EmitSkippedRegion: The segment must be emitted as a skipped region. + void startSegment(const CountedRegion &Region, LineColPair StartLoc, + bool IsRegionEntry, bool EmitSkippedRegion = false) { + bool HasCount = !EmitSkippedRegion && + (Region.Kind != CounterMappingRegion::SkippedRegion); + + // If the new segment wouldn't affect coverage rendering, skip it. + if (!Segments.empty() && !IsRegionEntry && !EmitSkippedRegion) { + const auto &Last = Segments.back(); + if (Last.HasCount == HasCount && Last.Count == Region.ExecutionCount && + !Last.IsRegionEntry) + return; + } - /// Start a segment with the given Region's count. - void startSegment(unsigned Line, unsigned Col, bool IsRegionEntry, - const CountedRegion &Region) { - // Avoid creating empty regions. - if (!Segments.empty() && Segments.back().Line == Line && - Segments.back().Col == Col) - Segments.pop_back(); - DEBUG(dbgs() << "Segment at " << Line << ":" << Col); - // Set this region's count. - if (Region.Kind != CounterMappingRegion::SkippedRegion) { - DEBUG(dbgs() << " with count " << Region.ExecutionCount); - Segments.emplace_back(Line, Col, Region.ExecutionCount, IsRegionEntry); - } else - Segments.emplace_back(Line, Col, IsRegionEntry); - DEBUG(dbgs() << "\n"); + if (HasCount) + Segments.emplace_back(StartLoc.first, StartLoc.second, + Region.ExecutionCount, IsRegionEntry, + Region.Kind == CounterMappingRegion::GapRegion); + else + Segments.emplace_back(StartLoc.first, StartLoc.second, IsRegionEntry); + + DEBUG({ + const auto &Last = Segments.back(); + dbgs() << "Segment at " << Last.Line << ":" << Last.Col + << " (count = " << Last.Count << ")" + << (Last.IsRegionEntry ? ", RegionEntry" : "") + << (!Last.HasCount ? ", Skipped" : "") + << (Last.IsGapRegion ? ", Gap" : "") << "\n"; + }); } - /// Start a segment for the given region. - void startSegment(const CountedRegion &Region) { - startSegment(Region.LineStart, Region.ColumnStart, true, Region); - } + /// Emit segments for active regions which end before \p Loc. + /// + /// \p Loc: The start location of the next region. If None, all active + /// regions are completed. + /// \p FirstCompletedRegion: Index of the first completed region. + void completeRegionsUntil(Optional<LineColPair> Loc, + unsigned FirstCompletedRegion) { + // Sort the completed regions by end location. This makes it simple to + // emit closing segments in sorted order. + auto CompletedRegionsIt = ActiveRegions.begin() + FirstCompletedRegion; + std::stable_sort(CompletedRegionsIt, ActiveRegions.end(), + [](const CountedRegion *L, const CountedRegion *R) { + return L->endLoc() < R->endLoc(); + }); + + // Emit segments for all completed regions. + for (unsigned I = FirstCompletedRegion + 1, E = ActiveRegions.size(); I < E; + ++I) { + const auto *CompletedRegion = ActiveRegions[I]; + assert((!Loc || CompletedRegion->endLoc() <= *Loc) && + "Completed region ends after start of new region"); + + const auto *PrevCompletedRegion = ActiveRegions[I - 1]; + auto CompletedSegmentLoc = PrevCompletedRegion->endLoc(); + + // Don't emit any more segments if they start where the new region begins. + if (Loc && CompletedSegmentLoc == *Loc) + break; + + // Don't emit a segment if the next completed region ends at the same + // location as this one. + if (CompletedSegmentLoc == CompletedRegion->endLoc()) + continue; - /// Pop the top region off of the active stack, starting a new segment with - /// the containing Region's count. - void popRegion() { - const CountedRegion *Active = ActiveRegions.back(); - unsigned Line = Active->LineEnd, Col = Active->ColumnEnd; - ActiveRegions.pop_back(); - if (ActiveRegions.empty()) - startSegment(Line, Col); - else - startSegment(Line, Col, false, *ActiveRegions.back()); + // Use the count from the last completed region which ends at this loc. + for (unsigned J = I + 1; J < E; ++J) + if (CompletedRegion->endLoc() == ActiveRegions[J]->endLoc()) + CompletedRegion = ActiveRegions[J]; + + startSegment(*CompletedRegion, CompletedSegmentLoc, false); + } + + auto Last = ActiveRegions.back(); + if (FirstCompletedRegion && Last->endLoc() != *Loc) { + // If there's a gap after the end of the last completed region and the + // start of the new region, use the last active region to fill the gap. + startSegment(*ActiveRegions[FirstCompletedRegion - 1], Last->endLoc(), + false); + } else if (!FirstCompletedRegion && (!Loc || *Loc != Last->endLoc())) { + // Emit a skipped segment if there are no more active regions. This + // ensures that gaps between functions are marked correctly. + startSegment(*Last, Last->endLoc(), false, true); + } + + // Pop the completed regions. + ActiveRegions.erase(CompletedRegionsIt, ActiveRegions.end()); } void buildSegmentsImpl(ArrayRef<CountedRegion> Regions) { - for (const auto &Region : Regions) { - // Pop any regions that end before this one starts. - while (!ActiveRegions.empty() && - ActiveRegions.back()->endLoc() <= Region.startLoc()) - popRegion(); - // Add this region to the stack. - ActiveRegions.push_back(&Region); - startSegment(Region); + for (const auto &CR : enumerate(Regions)) { + auto CurStartLoc = CR.value().startLoc(); + + // Active regions which end before the current region need to be popped. + auto CompletedRegions = + std::stable_partition(ActiveRegions.begin(), ActiveRegions.end(), + [&](const CountedRegion *Region) { + return !(Region->endLoc() <= CurStartLoc); + }); + if (CompletedRegions != ActiveRegions.end()) { + unsigned FirstCompletedRegion = + std::distance(ActiveRegions.begin(), CompletedRegions); + completeRegionsUntil(CurStartLoc, FirstCompletedRegion); + } + + bool GapRegion = CR.value().Kind == CounterMappingRegion::GapRegion; + + // Try to emit a segment for the current region. + if (CurStartLoc == CR.value().endLoc()) { + // Avoid making zero-length regions active. If it's the last region, + // emit a skipped segment. Otherwise use its predecessor's count. + const bool Skipped = (CR.index() + 1) == Regions.size(); + startSegment(ActiveRegions.empty() ? CR.value() : *ActiveRegions.back(), + CurStartLoc, !GapRegion, Skipped); + continue; + } + if (CR.index() + 1 == Regions.size() || + CurStartLoc != Regions[CR.index() + 1].startLoc()) { + // Emit a segment if the next region doesn't start at the same location + // as this one. + startSegment(CR.value(), CurStartLoc, !GapRegion); + } + + // This region is active (i.e not completed). + ActiveRegions.push_back(&CR.value()); } - // Pop any regions that are left in the stack. - while (!ActiveRegions.empty()) - popRegion(); + + // Complete any remaining active regions. + if (!ActiveRegions.empty()) + completeRegionsUntil(None, 0); } /// Sort a nested sequence of regions from a single file. @@ -426,7 +513,7 @@ class SegmentBuilder { } public: - /// Build a list of CoverageSegments from a list of Regions. + /// Build a sorted list of CoverageSegments from a list of Regions. static std::vector<CoverageSegment> buildSegments(MutableArrayRef<CountedRegion> Regions) { std::vector<CoverageSegment> Segments; @@ -435,7 +522,28 @@ public: sortNestedRegions(Regions); ArrayRef<CountedRegion> CombinedRegions = combineRegions(Regions); + DEBUG({ + dbgs() << "Combined regions:\n"; + for (const auto &CR : CombinedRegions) + dbgs() << " " << CR.LineStart << ":" << CR.ColumnStart << " -> " + << CR.LineEnd << ":" << CR.ColumnEnd + << " (count=" << CR.ExecutionCount << ")\n"; + }); + Builder.buildSegmentsImpl(CombinedRegions); + +#ifndef NDEBUG + for (unsigned I = 1, E = Segments.size(); I < E; ++I) { + const auto &L = Segments[I - 1]; + const auto &R = Segments[I]; + if (!(L.Line < R.Line) && !(L.Line == R.Line && L.Col < R.Col)) { + DEBUG(dbgs() << " ! Segment " << L.Line << ":" << L.Col + << " followed by " << R.Line << ":" << R.Col << "\n"); + assert(false && "Coverage segments not unique or sorted"); + } + } +#endif + return Segments; } }; @@ -509,8 +617,8 @@ CoverageData CoverageMapping::getCoverageForFile(StringRef Filename) const { return FileCoverage; } -std::vector<const FunctionRecord *> -CoverageMapping::getInstantiations(StringRef Filename) const { +std::vector<InstantiationGroup> +CoverageMapping::getInstantiationGroups(StringRef Filename) const { FunctionInstantiationSetCollector InstantiationSetCollector; for (const auto &Function : Functions) { auto MainFileID = findMainViewFileID(Filename, Function); @@ -519,12 +627,12 @@ CoverageMapping::getInstantiations(StringRef Filename) const { InstantiationSetCollector.insert(Function, *MainFileID); } - std::vector<const FunctionRecord *> Result; - for (const auto &InstantiationSet : InstantiationSetCollector) { - if (InstantiationSet.second.size() < 2) - continue; - Result.insert(Result.end(), InstantiationSet.second.begin(), - InstantiationSet.second.end()); + std::vector<InstantiationGroup> Result; + for (auto &InstantiationSet : InstantiationSetCollector) { + InstantiationGroup IG{InstantiationSet.first.first, + InstantiationSet.first.second, + std::move(InstantiationSet.second)}; + Result.emplace_back(std::move(IG)); } return Result; } @@ -569,6 +677,59 @@ CoverageData CoverageMapping::getCoverageForExpansion( return ExpansionCoverage; } +LineCoverageStats::LineCoverageStats( + ArrayRef<const CoverageSegment *> LineSegments, + const CoverageSegment *WrappedSegment, unsigned Line) + : ExecutionCount(0), HasMultipleRegions(false), Mapped(false), Line(Line), + LineSegments(LineSegments), WrappedSegment(WrappedSegment) { + // Find the minimum number of regions which start in this line. + unsigned MinRegionCount = 0; + auto isStartOfRegion = [](const CoverageSegment *S) { + return !S->IsGapRegion && S->HasCount && S->IsRegionEntry; + }; + for (unsigned I = 0; I < LineSegments.size() && MinRegionCount < 2; ++I) + if (isStartOfRegion(LineSegments[I])) + ++MinRegionCount; + + bool StartOfSkippedRegion = !LineSegments.empty() && + !LineSegments.front()->HasCount && + LineSegments.front()->IsRegionEntry; + + HasMultipleRegions = MinRegionCount > 1; + Mapped = + !StartOfSkippedRegion && + ((WrappedSegment && WrappedSegment->HasCount) || (MinRegionCount > 0)); + + if (!Mapped) + return; + + // Pick the max count from the non-gap, region entry segments and the + // wrapped count. + if (WrappedSegment) + ExecutionCount = WrappedSegment->Count; + if (!MinRegionCount) + return; + for (const auto *LS : LineSegments) + if (isStartOfRegion(LS)) + ExecutionCount = std::max(ExecutionCount, LS->Count); +} + +LineCoverageIterator &LineCoverageIterator::operator++() { + if (Next == CD.end()) { + Stats = LineCoverageStats(); + Ended = true; + return *this; + } + if (Segments.size()) + WrappedSegment = Segments.back(); + Segments.clear(); + while (Next != CD.end() && Next->Line == Line) + Segments.push_back(&*Next++); + Stats = LineCoverageStats(Segments, WrappedSegment, Line); + ++Line; + return *this; +} + static std::string getCoverageMapErrString(coveragemap_error Err) { switch (Err) { case coveragemap_error::success: diff --git a/gnu/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp b/gnu/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp index fff0a03ccbe..649cf507357 100644 --- a/gnu/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp +++ b/gnu/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp @@ -20,7 +20,6 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.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" @@ -33,13 +32,6 @@ #include "llvm/Support/LEB128.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" -#include <algorithm> -#include <cassert> -#include <cstddef> -#include <cstdint> -#include <limits> -#include <memory> -#include <utility> #include <vector> using namespace llvm; @@ -49,16 +41,18 @@ using namespace object; #define DEBUG_TYPE "coverage-mapping" void CoverageMappingIterator::increment() { + if (ReadErr != coveragemap_error::success) + return; + // Check if all the records were read or if an error occurred while reading // the next record. - if (auto E = Reader->readNextRecord(Record)) { + if (auto E = Reader->readNextRecord(Record)) handleAllErrors(std::move(E), [&](const CoverageMapError &CME) { if (CME.get() == coveragemap_error::eof) *this = CoverageMappingIterator(); else - llvm_unreachable("Unexpected error in coverage mapping iterator"); + ReadErr = CME.get(); }); - } } Error RawCoverageReader::readULEB128(uint64_t &Result) { @@ -214,6 +208,13 @@ Error RawCoverageMappingReader::readMappingRegionsSubArray( if (auto Err = readIntMax(ColumnEnd, std::numeric_limits<unsigned>::max())) return Err; LineStart += LineStartDelta; + + // If the high bit of ColumnEnd is set, this is a gap region. + if (ColumnEnd & (1U << 31)) { + Kind = CounterMappingRegion::GapRegion; + ColumnEnd &= ~(1U << 31); + } + // Adjust the column locations for the empty regions that are supposed to // cover whole lines. Those regions should be encoded with the // column range (1 -> std::numeric_limits<unsigned>::max()), but because @@ -238,9 +239,12 @@ Error RawCoverageMappingReader::readMappingRegionsSubArray( dbgs() << "\n"; }); - MappingRegions.push_back(CounterMappingRegion( - C, InferredFileID, ExpandedFileID, LineStart, ColumnStart, - LineStart + NumLines, ColumnEnd, Kind)); + auto CMR = CounterMappingRegion(C, InferredFileID, ExpandedFileID, + LineStart, ColumnStart, + LineStart + NumLines, ColumnEnd, Kind); + if (CMR.startLoc() > CMR.endLoc()) + return make_error<CoverageMapError>(coveragemap_error::malformed); + MappingRegions.push_back(CMR); } return Error::success(); } @@ -529,11 +533,16 @@ Expected<std::unique_ptr<CovMapFuncRecordReader>> CovMapFuncRecordReader::get( return llvm::make_unique<VersionedCovMapFuncRecordReader< CovMapVersion::Version1, IntPtrT, Endian>>(P, R, F); case CovMapVersion::Version2: + case CovMapVersion::Version3: // Decompress the name data. if (Error E = P.create(P.getNameData())) return std::move(E); - return llvm::make_unique<VersionedCovMapFuncRecordReader< - CovMapVersion::Version2, IntPtrT, Endian>>(P, R, F); + if (Version == CovMapVersion::Version2) + return llvm::make_unique<VersionedCovMapFuncRecordReader< + CovMapVersion::Version2, IntPtrT, Endian>>(P, R, F); + else + return llvm::make_unique<VersionedCovMapFuncRecordReader< + CovMapVersion::Version3, IntPtrT, Endian>>(P, R, F); } llvm_unreachable("Unsupported version"); } diff --git a/gnu/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp b/gnu/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp index 6fe93530da2..49e82e48105 100644 --- a/gnu/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp +++ b/gnu/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp @@ -116,6 +116,13 @@ static void writeCounter(ArrayRef<CounterExpression> Expressions, Counter C, } void CoverageMappingWriter::write(raw_ostream &OS) { + // Check that we don't have any bogus regions. + assert(all_of(MappingRegions, + [](const CounterMappingRegion &CMR) { + return CMR.startLoc() <= CMR.endLoc(); + }) && + "Source region does not begin before it ends"); + // Sort the regions in an ascending order by the file id and the starting // location. Sort by region kinds to ensure stable order for tests. std::stable_sort( @@ -164,6 +171,7 @@ void CoverageMappingWriter::write(raw_ostream &OS) { Counter Count = Minimizer.adjust(I->Count); switch (I->Kind) { case CounterMappingRegion::CodeRegion: + case CounterMappingRegion::GapRegion: writeCounter(MinExpressions, Count, OS); break; case CounterMappingRegion::ExpansionRegion: { diff --git a/gnu/llvm/lib/ProfileData/GCOV.cpp b/gnu/llvm/lib/ProfileData/GCOV.cpp new file mode 100644 index 00000000000..d6e44389f2b --- /dev/null +++ b/gnu/llvm/lib/ProfileData/GCOV.cpp @@ -0,0 +1,821 @@ +//===- GCOV.cpp - LLVM coverage tool --------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// GCOV implements the interface to read and write coverage files that use +// 'gcov' format. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ProfileData/GCOV.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <system_error> + +using namespace llvm; + +//===----------------------------------------------------------------------===// +// GCOVFile implementation. + +/// readGCNO - Read GCNO buffer. +bool GCOVFile::readGCNO(GCOVBuffer &Buffer) { + if (!Buffer.readGCNOFormat()) + return false; + if (!Buffer.readGCOVVersion(Version)) + return false; + + if (!Buffer.readInt(Checksum)) + return false; + while (true) { + if (!Buffer.readFunctionTag()) + break; + auto GFun = make_unique<GCOVFunction>(*this); + if (!GFun->readGCNO(Buffer, Version)) + return false; + Functions.push_back(std::move(GFun)); + } + + GCNOInitialized = true; + return true; +} + +/// readGCDA - Read GCDA buffer. It is required that readGCDA() can only be +/// called after readGCNO(). +bool GCOVFile::readGCDA(GCOVBuffer &Buffer) { + assert(GCNOInitialized && "readGCDA() can only be called after readGCNO()"); + if (!Buffer.readGCDAFormat()) + return false; + GCOV::GCOVVersion GCDAVersion; + if (!Buffer.readGCOVVersion(GCDAVersion)) + return false; + if (Version != GCDAVersion) { + errs() << "GCOV versions do not match.\n"; + return false; + } + + uint32_t GCDAChecksum; + if (!Buffer.readInt(GCDAChecksum)) + return false; + if (Checksum != GCDAChecksum) { + errs() << "File checksums do not match: " << Checksum + << " != " << GCDAChecksum << ".\n"; + return false; + } + for (size_t i = 0, e = Functions.size(); i < e; ++i) { + if (!Buffer.readFunctionTag()) { + errs() << "Unexpected number of functions.\n"; + return false; + } + if (!Functions[i]->readGCDA(Buffer, Version)) + return false; + } + if (Buffer.readObjectTag()) { + uint32_t Length; + uint32_t Dummy; + if (!Buffer.readInt(Length)) + return false; + if (!Buffer.readInt(Dummy)) + return false; // checksum + if (!Buffer.readInt(Dummy)) + return false; // num + if (!Buffer.readInt(RunCount)) + return false; + Buffer.advanceCursor(Length - 3); + } + while (Buffer.readProgramTag()) { + uint32_t Length; + if (!Buffer.readInt(Length)) + return false; + Buffer.advanceCursor(Length); + ++ProgramCount; + } + + return true; +} + +void GCOVFile::print(raw_ostream &OS) const { + for (const auto &FPtr : Functions) + FPtr->print(OS); +} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +/// dump - Dump GCOVFile content to dbgs() for debugging purposes. +LLVM_DUMP_METHOD void GCOVFile::dump() const { + print(dbgs()); +} +#endif + +/// collectLineCounts - Collect line counts. This must be used after +/// reading .gcno and .gcda files. +void GCOVFile::collectLineCounts(FileInfo &FI) { + for (const auto &FPtr : Functions) + FPtr->collectLineCounts(FI); + FI.setRunCount(RunCount); + FI.setProgramCount(ProgramCount); +} + +//===----------------------------------------------------------------------===// +// GCOVFunction implementation. + +/// readGCNO - Read a function from the GCNO buffer. Return false if an error +/// occurs. +bool GCOVFunction::readGCNO(GCOVBuffer &Buff, GCOV::GCOVVersion Version) { + uint32_t Dummy; + if (!Buff.readInt(Dummy)) + return false; // Function header length + if (!Buff.readInt(Ident)) + return false; + if (!Buff.readInt(Checksum)) + return false; + if (Version != GCOV::V402) { + uint32_t CfgChecksum; + if (!Buff.readInt(CfgChecksum)) + return false; + if (Parent.getChecksum() != CfgChecksum) { + errs() << "File checksums do not match: " << Parent.getChecksum() + << " != " << CfgChecksum << " in (" << Name << ").\n"; + return false; + } + } + if (!Buff.readString(Name)) + return false; + if (!Buff.readString(Filename)) + return false; + if (!Buff.readInt(LineNumber)) + return false; + + // read blocks. + if (!Buff.readBlockTag()) { + errs() << "Block tag not found.\n"; + return false; + } + uint32_t BlockCount; + if (!Buff.readInt(BlockCount)) + return false; + for (uint32_t i = 0, e = BlockCount; i != e; ++i) { + if (!Buff.readInt(Dummy)) + return false; // Block flags; + Blocks.push_back(make_unique<GCOVBlock>(*this, i)); + } + + // read edges. + while (Buff.readEdgeTag()) { + uint32_t EdgeCount; + if (!Buff.readInt(EdgeCount)) + return false; + EdgeCount = (EdgeCount - 1) / 2; + uint32_t BlockNo; + if (!Buff.readInt(BlockNo)) + return false; + if (BlockNo >= BlockCount) { + errs() << "Unexpected block number: " << BlockNo << " (in " << Name + << ").\n"; + return false; + } + for (uint32_t i = 0, e = EdgeCount; i != e; ++i) { + uint32_t Dst; + if (!Buff.readInt(Dst)) + return false; + Edges.push_back(make_unique<GCOVEdge>(*Blocks[BlockNo], *Blocks[Dst])); + GCOVEdge *Edge = Edges.back().get(); + Blocks[BlockNo]->addDstEdge(Edge); + Blocks[Dst]->addSrcEdge(Edge); + if (!Buff.readInt(Dummy)) + return false; // Edge flag + } + } + + // read line table. + while (Buff.readLineTag()) { + uint32_t LineTableLength; + // Read the length of this line table. + if (!Buff.readInt(LineTableLength)) + return false; + uint32_t EndPos = Buff.getCursor() + LineTableLength * 4; + uint32_t BlockNo; + // Read the block number this table is associated with. + if (!Buff.readInt(BlockNo)) + return false; + if (BlockNo >= BlockCount) { + errs() << "Unexpected block number: " << BlockNo << " (in " << Name + << ").\n"; + return false; + } + GCOVBlock &Block = *Blocks[BlockNo]; + // Read the word that pads the beginning of the line table. This may be a + // flag of some sort, but seems to always be zero. + if (!Buff.readInt(Dummy)) + return false; + + // Line information starts here and continues up until the last word. + if (Buff.getCursor() != (EndPos - sizeof(uint32_t))) { + StringRef F; + // Read the source file name. + if (!Buff.readString(F)) + return false; + if (Filename != F) { + errs() << "Multiple sources for a single basic block: " << Filename + << " != " << F << " (in " << Name << ").\n"; + return false; + } + // Read lines up to, but not including, the null terminator. + while (Buff.getCursor() < (EndPos - 2 * sizeof(uint32_t))) { + uint32_t Line; + if (!Buff.readInt(Line)) + return false; + // Line 0 means this instruction was injected by the compiler. Skip it. + if (!Line) + continue; + Block.addLine(Line); + } + // Read the null terminator. + if (!Buff.readInt(Dummy)) + return false; + } + // The last word is either a flag or padding, it isn't clear which. Skip + // over it. + if (!Buff.readInt(Dummy)) + return false; + } + return true; +} + +/// readGCDA - Read a function from the GCDA buffer. Return false if an error +/// occurs. +bool GCOVFunction::readGCDA(GCOVBuffer &Buff, GCOV::GCOVVersion Version) { + uint32_t HeaderLength; + if (!Buff.readInt(HeaderLength)) + return false; // Function header length + + uint64_t EndPos = Buff.getCursor() + HeaderLength * sizeof(uint32_t); + + uint32_t GCDAIdent; + if (!Buff.readInt(GCDAIdent)) + return false; + if (Ident != GCDAIdent) { + errs() << "Function identifiers do not match: " << Ident + << " != " << GCDAIdent << " (in " << Name << ").\n"; + return false; + } + + uint32_t GCDAChecksum; + if (!Buff.readInt(GCDAChecksum)) + return false; + if (Checksum != GCDAChecksum) { + errs() << "Function checksums do not match: " << Checksum + << " != " << GCDAChecksum << " (in " << Name << ").\n"; + return false; + } + + uint32_t CfgChecksum; + if (Version != GCOV::V402) { + if (!Buff.readInt(CfgChecksum)) + return false; + if (Parent.getChecksum() != CfgChecksum) { + errs() << "File checksums do not match: " << Parent.getChecksum() + << " != " << CfgChecksum << " (in " << Name << ").\n"; + return false; + } + } + + if (Buff.getCursor() < EndPos) { + StringRef GCDAName; + if (!Buff.readString(GCDAName)) + return false; + if (Name != GCDAName) { + errs() << "Function names do not match: " << Name << " != " << GCDAName + << ".\n"; + return false; + } + } + + if (!Buff.readArcTag()) { + errs() << "Arc tag not found (in " << Name << ").\n"; + return false; + } + + uint32_t Count; + if (!Buff.readInt(Count)) + return false; + Count /= 2; + + // This for loop adds the counts for each block. A second nested loop is + // required to combine the edge counts that are contained in the GCDA file. + for (uint32_t BlockNo = 0; Count > 0; ++BlockNo) { + // The last block is always reserved for exit block + if (BlockNo >= Blocks.size()) { + errs() << "Unexpected number of edges (in " << Name << ").\n"; + return false; + } + if (BlockNo == Blocks.size() - 1) + errs() << "(" << Name << ") has arcs from exit block.\n"; + GCOVBlock &Block = *Blocks[BlockNo]; + for (size_t EdgeNo = 0, End = Block.getNumDstEdges(); EdgeNo < End; + ++EdgeNo) { + if (Count == 0) { + errs() << "Unexpected number of edges (in " << Name << ").\n"; + return false; + } + uint64_t ArcCount; + if (!Buff.readInt64(ArcCount)) + return false; + Block.addCount(EdgeNo, ArcCount); + --Count; + } + Block.sortDstEdges(); + } + return true; +} + +/// getEntryCount - Get the number of times the function was called by +/// retrieving the entry block's count. +uint64_t GCOVFunction::getEntryCount() const { + return Blocks.front()->getCount(); +} + +/// getExitCount - Get the number of times the function returned by retrieving +/// the exit block's count. +uint64_t GCOVFunction::getExitCount() const { + return Blocks.back()->getCount(); +} + +void GCOVFunction::print(raw_ostream &OS) const { + OS << "===== " << Name << " (" << Ident << ") @ " << Filename << ":" + << LineNumber << "\n"; + for (const auto &Block : Blocks) + Block->print(OS); +} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +/// dump - Dump GCOVFunction content to dbgs() for debugging purposes. +LLVM_DUMP_METHOD void GCOVFunction::dump() const { + print(dbgs()); +} +#endif + +/// collectLineCounts - Collect line counts. This must be used after +/// reading .gcno and .gcda files. +void GCOVFunction::collectLineCounts(FileInfo &FI) { + // If the line number is zero, this is a function that doesn't actually appear + // in the source file, so there isn't anything we can do with it. + if (LineNumber == 0) + return; + + for (const auto &Block : Blocks) + Block->collectLineCounts(FI); + FI.addFunctionLine(Filename, LineNumber, this); +} + +//===----------------------------------------------------------------------===// +// GCOVBlock implementation. + +/// ~GCOVBlock - Delete GCOVBlock and its content. +GCOVBlock::~GCOVBlock() { + SrcEdges.clear(); + DstEdges.clear(); + Lines.clear(); +} + +/// addCount - Add to block counter while storing the edge count. If the +/// destination has no outgoing edges, also update that block's count too. +void GCOVBlock::addCount(size_t DstEdgeNo, uint64_t N) { + assert(DstEdgeNo < DstEdges.size()); // up to caller to ensure EdgeNo is valid + DstEdges[DstEdgeNo]->Count = N; + Counter += N; + if (!DstEdges[DstEdgeNo]->Dst.getNumDstEdges()) + DstEdges[DstEdgeNo]->Dst.Counter += N; +} + +/// sortDstEdges - Sort destination edges by block number, nop if already +/// sorted. This is required for printing branch info in the correct order. +void GCOVBlock::sortDstEdges() { + if (!DstEdgesAreSorted) { + SortDstEdgesFunctor SortEdges; + std::stable_sort(DstEdges.begin(), DstEdges.end(), SortEdges); + } +} + +/// collectLineCounts - Collect line counts. This must be used after +/// reading .gcno and .gcda files. +void GCOVBlock::collectLineCounts(FileInfo &FI) { + for (uint32_t N : Lines) + FI.addBlockLine(Parent.getFilename(), N, this); +} + +void GCOVBlock::print(raw_ostream &OS) const { + OS << "Block : " << Number << " Counter : " << Counter << "\n"; + if (!SrcEdges.empty()) { + OS << "\tSource Edges : "; + for (const GCOVEdge *Edge : SrcEdges) + OS << Edge->Src.Number << " (" << Edge->Count << "), "; + OS << "\n"; + } + if (!DstEdges.empty()) { + OS << "\tDestination Edges : "; + for (const GCOVEdge *Edge : DstEdges) + OS << Edge->Dst.Number << " (" << Edge->Count << "), "; + OS << "\n"; + } + if (!Lines.empty()) { + OS << "\tLines : "; + for (uint32_t N : Lines) + OS << (N) << ","; + OS << "\n"; + } +} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +/// dump - Dump GCOVBlock content to dbgs() for debugging purposes. +LLVM_DUMP_METHOD void GCOVBlock::dump() const { + print(dbgs()); +} +#endif + +//===----------------------------------------------------------------------===// +// FileInfo implementation. + +// Safe integer division, returns 0 if numerator is 0. +static uint32_t safeDiv(uint64_t Numerator, uint64_t Divisor) { + if (!Numerator) + return 0; + return Numerator / Divisor; +} + +// This custom division function mimics gcov's branch ouputs: +// - Round to closest whole number +// - Only output 0% or 100% if it's exactly that value +static uint32_t branchDiv(uint64_t Numerator, uint64_t Divisor) { + if (!Numerator) + return 0; + if (Numerator == Divisor) + return 100; + + uint8_t Res = (Numerator * 100 + Divisor / 2) / Divisor; + if (Res == 0) + return 1; + if (Res == 100) + return 99; + return Res; +} + +namespace { +struct formatBranchInfo { + formatBranchInfo(const GCOV::Options &Options, uint64_t Count, uint64_t Total) + : Options(Options), Count(Count), Total(Total) {} + + void print(raw_ostream &OS) const { + if (!Total) + OS << "never executed"; + else if (Options.BranchCount) + OS << "taken " << Count; + else + OS << "taken " << branchDiv(Count, Total) << "%"; + } + + const GCOV::Options &Options; + uint64_t Count; + uint64_t Total; +}; + +static raw_ostream &operator<<(raw_ostream &OS, const formatBranchInfo &FBI) { + FBI.print(OS); + return OS; +} + +class LineConsumer { + std::unique_ptr<MemoryBuffer> Buffer; + StringRef Remaining; + +public: + LineConsumer(StringRef Filename) { + ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = + MemoryBuffer::getFileOrSTDIN(Filename); + if (std::error_code EC = BufferOrErr.getError()) { + errs() << Filename << ": " << EC.message() << "\n"; + Remaining = ""; + } else { + Buffer = std::move(BufferOrErr.get()); + Remaining = Buffer->getBuffer(); + } + } + bool empty() { return Remaining.empty(); } + void printNext(raw_ostream &OS, uint32_t LineNum) { + StringRef Line; + if (empty()) + Line = "/*EOF*/"; + else + std::tie(Line, Remaining) = Remaining.split("\n"); + OS << format("%5u:", LineNum) << Line << "\n"; + } +}; +} // end anonymous namespace + +/// Convert a path to a gcov filename. If PreservePaths is true, this +/// translates "/" to "#", ".." to "^", and drops ".", to match gcov. +static std::string mangleCoveragePath(StringRef Filename, bool PreservePaths) { + if (!PreservePaths) + return sys::path::filename(Filename).str(); + + // This behaviour is defined by gcov in terms of text replacements, so it's + // not likely to do anything useful on filesystems with different textual + // conventions. + llvm::SmallString<256> Result(""); + StringRef::iterator I, S, E; + for (I = S = Filename.begin(), E = Filename.end(); I != E; ++I) { + if (*I != '/') + continue; + + if (I - S == 1 && *S == '.') { + // ".", the current directory, is skipped. + } else if (I - S == 2 && *S == '.' && *(S + 1) == '.') { + // "..", the parent directory, is replaced with "^". + Result.append("^#"); + } else { + if (S < I) + // Leave other components intact, + Result.append(S, I); + // And separate with "#". + Result.push_back('#'); + } + S = I + 1; + } + + if (S < I) + Result.append(S, I); + return Result.str(); +} + +std::string FileInfo::getCoveragePath(StringRef Filename, + StringRef MainFilename) { + if (Options.NoOutput) + // This is probably a bug in gcov, but when -n is specified, paths aren't + // mangled at all, and the -l and -p options are ignored. Here, we do the + // same. + return Filename; + + std::string CoveragePath; + if (Options.LongFileNames && !Filename.equals(MainFilename)) + CoveragePath = + mangleCoveragePath(MainFilename, Options.PreservePaths) + "##"; + CoveragePath += mangleCoveragePath(Filename, Options.PreservePaths) + ".gcov"; + return CoveragePath; +} + +std::unique_ptr<raw_ostream> +FileInfo::openCoveragePath(StringRef CoveragePath) { + if (Options.NoOutput) + return llvm::make_unique<raw_null_ostream>(); + + std::error_code EC; + auto OS = llvm::make_unique<raw_fd_ostream>(CoveragePath, EC, + sys::fs::F_Text); + if (EC) { + errs() << EC.message() << "\n"; + return llvm::make_unique<raw_null_ostream>(); + } + return std::move(OS); +} + +/// print - Print source files with collected line count information. +void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename, + StringRef GCNOFile, StringRef GCDAFile) { + SmallVector<StringRef, 4> Filenames; + for (const auto &LI : LineInfo) + Filenames.push_back(LI.first()); + std::sort(Filenames.begin(), Filenames.end()); + + for (StringRef Filename : Filenames) { + auto AllLines = LineConsumer(Filename); + + std::string CoveragePath = getCoveragePath(Filename, MainFilename); + std::unique_ptr<raw_ostream> CovStream = openCoveragePath(CoveragePath); + raw_ostream &CovOS = *CovStream; + + CovOS << " -: 0:Source:" << Filename << "\n"; + CovOS << " -: 0:Graph:" << GCNOFile << "\n"; + CovOS << " -: 0:Data:" << GCDAFile << "\n"; + CovOS << " -: 0:Runs:" << RunCount << "\n"; + CovOS << " -: 0:Programs:" << ProgramCount << "\n"; + + const LineData &Line = LineInfo[Filename]; + GCOVCoverage FileCoverage(Filename); + for (uint32_t LineIndex = 0; LineIndex < Line.LastLine || !AllLines.empty(); + ++LineIndex) { + if (Options.BranchInfo) { + FunctionLines::const_iterator FuncsIt = Line.Functions.find(LineIndex); + if (FuncsIt != Line.Functions.end()) + printFunctionSummary(CovOS, FuncsIt->second); + } + + BlockLines::const_iterator BlocksIt = Line.Blocks.find(LineIndex); + if (BlocksIt == Line.Blocks.end()) { + // No basic blocks are on this line. Not an executable line of code. + CovOS << " -:"; + AllLines.printNext(CovOS, LineIndex + 1); + } else { + const BlockVector &Blocks = BlocksIt->second; + + // Add up the block counts to form line counts. + DenseMap<const GCOVFunction *, bool> LineExecs; + uint64_t LineCount = 0; + for (const GCOVBlock *Block : Blocks) { + if (Options.AllBlocks) { + // Only take the highest block count for that line. + uint64_t BlockCount = Block->getCount(); + LineCount = LineCount > BlockCount ? LineCount : BlockCount; + } else { + // Sum up all of the block counts. + LineCount += Block->getCount(); + } + + if (Options.FuncCoverage) { + // This is a slightly convoluted way to most accurately gather line + // statistics for functions. Basically what is happening is that we + // don't want to count a single line with multiple blocks more than + // once. However, we also don't simply want to give the total line + // count to every function that starts on the line. Thus, what is + // happening here are two things: + // 1) Ensure that the number of logical lines is only incremented + // once per function. + // 2) If there are multiple blocks on the same line, ensure that the + // number of lines executed is incremented as long as at least + // one of the blocks are executed. + const GCOVFunction *Function = &Block->getParent(); + if (FuncCoverages.find(Function) == FuncCoverages.end()) { + std::pair<const GCOVFunction *, GCOVCoverage> KeyValue( + Function, GCOVCoverage(Function->getName())); + FuncCoverages.insert(KeyValue); + } + GCOVCoverage &FuncCoverage = FuncCoverages.find(Function)->second; + + if (LineExecs.find(Function) == LineExecs.end()) { + if (Block->getCount()) { + ++FuncCoverage.LinesExec; + LineExecs[Function] = true; + } else { + LineExecs[Function] = false; + } + ++FuncCoverage.LogicalLines; + } else if (!LineExecs[Function] && Block->getCount()) { + ++FuncCoverage.LinesExec; + LineExecs[Function] = true; + } + } + } + + if (LineCount == 0) + CovOS << " #####:"; + else { + CovOS << format("%9" PRIu64 ":", LineCount); + ++FileCoverage.LinesExec; + } + ++FileCoverage.LogicalLines; + + AllLines.printNext(CovOS, LineIndex + 1); + + uint32_t BlockNo = 0; + uint32_t EdgeNo = 0; + for (const GCOVBlock *Block : Blocks) { + // Only print block and branch information at the end of the block. + if (Block->getLastLine() != LineIndex + 1) + continue; + if (Options.AllBlocks) + printBlockInfo(CovOS, *Block, LineIndex, BlockNo); + if (Options.BranchInfo) { + size_t NumEdges = Block->getNumDstEdges(); + if (NumEdges > 1) + printBranchInfo(CovOS, *Block, FileCoverage, EdgeNo); + else if (Options.UncondBranch && NumEdges == 1) + printUncondBranchInfo(CovOS, EdgeNo, + (*Block->dst_begin())->Count); + } + } + } + } + FileCoverages.push_back(std::make_pair(CoveragePath, FileCoverage)); + } + + // FIXME: There is no way to detect calls given current instrumentation. + if (Options.FuncCoverage) + printFuncCoverage(InfoOS); + printFileCoverage(InfoOS); +} + +/// printFunctionSummary - Print function and block summary. +void FileInfo::printFunctionSummary(raw_ostream &OS, + const FunctionVector &Funcs) const { + for (const GCOVFunction *Func : Funcs) { + uint64_t EntryCount = Func->getEntryCount(); + uint32_t BlocksExec = 0; + for (const GCOVBlock &Block : Func->blocks()) + if (Block.getNumDstEdges() && Block.getCount()) + ++BlocksExec; + + OS << "function " << Func->getName() << " called " << EntryCount + << " returned " << safeDiv(Func->getExitCount() * 100, EntryCount) + << "% blocks executed " + << safeDiv(BlocksExec * 100, Func->getNumBlocks() - 1) << "%\n"; + } +} + +/// printBlockInfo - Output counts for each block. +void FileInfo::printBlockInfo(raw_ostream &OS, const GCOVBlock &Block, + uint32_t LineIndex, uint32_t &BlockNo) const { + if (Block.getCount() == 0) + OS << " $$$$$:"; + else + OS << format("%9" PRIu64 ":", Block.getCount()); + OS << format("%5u-block %2u\n", LineIndex + 1, BlockNo++); +} + +/// printBranchInfo - Print conditional branch probabilities. +void FileInfo::printBranchInfo(raw_ostream &OS, const GCOVBlock &Block, + GCOVCoverage &Coverage, uint32_t &EdgeNo) { + SmallVector<uint64_t, 16> BranchCounts; + uint64_t TotalCounts = 0; + for (const GCOVEdge *Edge : Block.dsts()) { + BranchCounts.push_back(Edge->Count); + TotalCounts += Edge->Count; + if (Block.getCount()) + ++Coverage.BranchesExec; + if (Edge->Count) + ++Coverage.BranchesTaken; + ++Coverage.Branches; + + if (Options.FuncCoverage) { + const GCOVFunction *Function = &Block.getParent(); + GCOVCoverage &FuncCoverage = FuncCoverages.find(Function)->second; + if (Block.getCount()) + ++FuncCoverage.BranchesExec; + if (Edge->Count) + ++FuncCoverage.BranchesTaken; + ++FuncCoverage.Branches; + } + } + + for (uint64_t N : BranchCounts) + OS << format("branch %2u ", EdgeNo++) + << formatBranchInfo(Options, N, TotalCounts) << "\n"; +} + +/// printUncondBranchInfo - Print unconditional branch probabilities. +void FileInfo::printUncondBranchInfo(raw_ostream &OS, uint32_t &EdgeNo, + uint64_t Count) const { + OS << format("unconditional %2u ", EdgeNo++) + << formatBranchInfo(Options, Count, Count) << "\n"; +} + +// printCoverage - Print generic coverage info used by both printFuncCoverage +// and printFileCoverage. +void FileInfo::printCoverage(raw_ostream &OS, + const GCOVCoverage &Coverage) const { + OS << format("Lines executed:%.2f%% of %u\n", + double(Coverage.LinesExec) * 100 / Coverage.LogicalLines, + Coverage.LogicalLines); + if (Options.BranchInfo) { + if (Coverage.Branches) { + OS << format("Branches executed:%.2f%% of %u\n", + double(Coverage.BranchesExec) * 100 / Coverage.Branches, + Coverage.Branches); + OS << format("Taken at least once:%.2f%% of %u\n", + double(Coverage.BranchesTaken) * 100 / Coverage.Branches, + Coverage.Branches); + } else { + OS << "No branches\n"; + } + OS << "No calls\n"; // to be consistent with gcov + } +} + +// printFuncCoverage - Print per-function coverage info. +void FileInfo::printFuncCoverage(raw_ostream &OS) const { + for (const auto &FC : FuncCoverages) { + const GCOVCoverage &Coverage = FC.second; + OS << "Function '" << Coverage.Name << "'\n"; + printCoverage(OS, Coverage); + OS << "\n"; + } +} + +// printFileCoverage - Print per-file coverage info. +void FileInfo::printFileCoverage(raw_ostream &OS) const { + for (const auto &FC : FileCoverages) { + const std::string &Filename = FC.first; + const GCOVCoverage &Coverage = FC.second; + OS << "File '" << Coverage.Name << "'\n"; + printCoverage(OS, Coverage); + if (!Options.NoOutput) + OS << Coverage.Name << ":creating '" << Filename << "'\n"; + OS << "\n"; + } +} diff --git a/gnu/llvm/lib/ProfileData/InstrProf.cpp b/gnu/llvm/lib/ProfileData/InstrProf.cpp index 48c1643cb13..8ab5df59f53 100644 --- a/gnu/llvm/lib/ProfileData/InstrProf.cpp +++ b/gnu/llvm/lib/ProfileData/InstrProf.cpp @@ -56,7 +56,7 @@ using namespace llvm; static cl::opt<bool> StaticFuncFullModulePrefix( - "static-func-full-module-prefix", cl::init(true), + "static-func-full-module-prefix", cl::init(true), cl::Hidden, cl::desc("Use full module build paths in the profile counter names for " "static functions.")); @@ -69,7 +69,7 @@ static cl::opt<bool> StaticFuncFullModulePrefix( // the source directory name not being stripped. A non-zero option value here // can potentially prevent some inter-module indirect-call-promotions. static cl::opt<unsigned> StaticFuncStripDirNamePrefix( - "static-func-strip-dirname-prefix", cl::init(0), + "static-func-strip-dirname-prefix", cl::init(0), cl::Hidden, cl::desc("Strip specified level of directory name from source path in " "the profile counter name for static functions.")); @@ -111,6 +111,8 @@ static std::string getInstrProfErrString(instrprof_error Err) { return "Failed to uncompress data (zlib)"; case instrprof_error::empty_raw_profile: return "Empty raw profile file"; + case instrprof_error::zlib_unavailable: + return "Profile uses zlib compression but the profile reader was built without zlib support"; } llvm_unreachable("A value of instrprof_error has no message."); } @@ -430,6 +432,9 @@ Error readPGOFuncNameStrings(StringRef NameStrings, InstrProfSymtab &Symtab) { SmallString<128> UncompressedNameStrings; StringRef NameStrings; if (isCompressed) { + if (!llvm::zlib::isAvailable()) + return make_error<InstrProfError>(instrprof_error::zlib_unavailable); + StringRef CompressedNameStrings(reinterpret_cast<const char *>(P), CompressedSize); if (Error E = diff --git a/gnu/llvm/lib/ProfileData/InstrProfReader.cpp b/gnu/llvm/lib/ProfileData/InstrProfReader.cpp index 1b39a0695aa..23c9a2676b9 100644 --- a/gnu/llvm/lib/ProfileData/InstrProfReader.cpp +++ b/gnu/llvm/lib/ProfileData/InstrProfReader.cpp @@ -61,7 +61,7 @@ InstrProfReader::create(const Twine &Path) { Expected<std::unique_ptr<InstrProfReader>> InstrProfReader::create(std::unique_ptr<MemoryBuffer> Buffer) { // Sanity check the buffer. - if (Buffer->getBufferSize() > std::numeric_limits<unsigned>::max()) + if (uint64_t(Buffer->getBufferSize()) > std::numeric_limits<unsigned>::max()) return make_error<InstrProfError>(instrprof_error::too_large); if (Buffer->getBufferSize() == 0) @@ -99,7 +99,7 @@ IndexedInstrProfReader::create(const Twine &Path) { Expected<std::unique_ptr<IndexedInstrProfReader>> IndexedInstrProfReader::create(std::unique_ptr<MemoryBuffer> Buffer) { // Sanity check the buffer. - if (Buffer->getBufferSize() > std::numeric_limits<unsigned>::max()) + if (uint64_t(Buffer->getBufferSize()) > std::numeric_limits<unsigned>::max()) return make_error<InstrProfError>(instrprof_error::too_large); // Create the reader. @@ -733,8 +733,6 @@ Error IndexedInstrProfReader::getFunctionCounts(StringRef FuncName, } Error IndexedInstrProfReader::readNextRecord(NamedInstrProfRecord &Record) { - static unsigned RecordIndex = 0; - ArrayRef<NamedInstrProfRecord> Data; Error E = Index->getRecords(Data); diff --git a/gnu/llvm/lib/ProfileData/ProfileSummaryBuilder.cpp b/gnu/llvm/lib/ProfileData/ProfileSummaryBuilder.cpp index 9fb2ec1b39d..5fa1e2cf7d1 100644 --- a/gnu/llvm/lib/ProfileData/ProfileSummaryBuilder.cpp +++ b/gnu/llvm/lib/ProfileData/ProfileSummaryBuilder.cpp @@ -12,7 +12,6 @@ //===----------------------------------------------------------------------===// #include "llvm/IR/Attributes.h" -#include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Type.h" diff --git a/gnu/llvm/lib/ProfileData/SampleProfReader.cpp b/gnu/llvm/lib/ProfileData/SampleProfReader.cpp index 234fe02ac8a..44547e3dffa 100644 --- a/gnu/llvm/lib/ProfileData/SampleProfReader.cpp +++ b/gnu/llvm/lib/ProfileData/SampleProfReader.cpp @@ -749,7 +749,7 @@ setupMemoryBuffer(const Twine &Filename) { auto Buffer = std::move(BufferOrErr.get()); // Sanity check the file. - if (Buffer->getBufferSize() > std::numeric_limits<uint32_t>::max()) + if (uint64_t(Buffer->getBufferSize()) > std::numeric_limits<uint32_t>::max()) return sampleprof_error::too_large; return std::move(Buffer); @@ -759,8 +759,6 @@ setupMemoryBuffer(const Twine &Filename) { /// /// \param Filename The file to open. /// -/// \param Reader The reader to instantiate according to \p Filename's format. -/// /// \param C The LLVM context to use to emit diagnostics. /// /// \returns an error code indicating the status of the created reader. @@ -776,8 +774,6 @@ SampleProfileReader::create(const Twine &Filename, LLVMContext &C) { /// /// \param B The memory buffer to create the reader from (assumes ownership). /// -/// \param Reader The reader to instantiate according to \p Filename's format. -/// /// \param C The LLVM context to use to emit diagnostics. /// /// \returns an error code indicating the status of the created reader. diff --git a/gnu/llvm/lib/ProfileData/SampleProfWriter.cpp b/gnu/llvm/lib/ProfileData/SampleProfWriter.cpp index b45026140c9..59c4885fcdb 100644 --- a/gnu/llvm/lib/ProfileData/SampleProfWriter.cpp +++ b/gnu/llvm/lib/ProfileData/SampleProfWriter.cpp @@ -222,7 +222,10 @@ std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) { } // Recursively emit all the callsite samples. - encodeULEB128(S.getCallsiteSamples().size(), OS); + uint64_t NumCallsites = 0; + for (const auto &J : S.getCallsiteSamples()) + NumCallsites += J.second.size(); + encodeULEB128(NumCallsites, OS); for (const auto &J : S.getCallsiteSamples()) for (const auto &FS : J.second) { LineLocation Loc = J.first; @@ -248,8 +251,6 @@ std::error_code SampleProfileWriterBinary::write(const FunctionSamples &S) { /// /// \param Filename The file to create. /// -/// \param Writer The writer to instantiate according to the specified format. -/// /// \param Format Encoding format for the profile file. /// /// \returns an error code indicating the status of the created writer. @@ -271,8 +272,6 @@ SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) { /// /// \param OS The output stream to store the profile data to. /// -/// \param Writer The writer to instantiate according to the specified format. -/// /// \param Format Encoding format for the profile file. /// /// \returns an error code indicating the status of the created writer. |
