summaryrefslogtreecommitdiff
path: root/lib/ProfileData
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-01-19 10:01:25 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-01-19 10:01:25 +0000
commitd8e91e46262bc44006913e6796843909f1ac7bcd (patch)
tree7d0c143d9b38190e0fa0180805389da22cd834c5 /lib/ProfileData
parentb7eb8e35e481a74962664b63dfb09483b200209a (diff)
Notes
Diffstat (limited to 'lib/ProfileData')
-rw-r--r--lib/ProfileData/Coverage/CoverageMapping.cpp31
-rw-r--r--lib/ProfileData/GCOV.cpp154
-rw-r--r--lib/ProfileData/InstrProf.cpp11
-rw-r--r--lib/ProfileData/InstrProfReader.cpp164
-rw-r--r--lib/ProfileData/ProfileSummaryBuilder.cpp2
-rw-r--r--lib/ProfileData/SampleProf.cpp10
-rw-r--r--lib/ProfileData/SampleProfReader.cpp167
-rw-r--r--lib/ProfileData/SampleProfWriter.cpp57
8 files changed, 532 insertions, 64 deletions
diff --git a/lib/ProfileData/Coverage/CoverageMapping.cpp b/lib/ProfileData/Coverage/CoverageMapping.cpp
index b3c2b182e76c..b2dde3406a63 100644
--- a/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -83,7 +83,7 @@ Counter CounterExpressionBuilder::simplify(Counter ExpressionTree) {
return Counter::getZero();
// Group the terms by counter ID.
- llvm::sort(Terms.begin(), Terms.end(), [](const Term &LHS, const Term &RHS) {
+ llvm::sort(Terms, [](const Term &LHS, const Term &RHS) {
return LHS.CounterID < RHS.CounterID;
});
@@ -207,12 +207,6 @@ Error CoverageMapping::loadFunctionRecord(
else
OrigFuncName = getFuncNameWithoutPrefix(OrigFuncName, Record.Filenames[0]);
- // Don't load records for (filenames, function) pairs we've already seen.
- auto FilenamesHash = hash_combine_range(Record.Filenames.begin(),
- Record.Filenames.end());
- if (!RecordProvenance[FilenamesHash].insert(hash_value(OrigFuncName)).second)
- return Error::success();
-
CounterMappingContext Ctx(Record.Expressions);
std::vector<uint64_t> Counts;
@@ -230,6 +224,15 @@ Error CoverageMapping::loadFunctionRecord(
assert(!Record.MappingRegions.empty() && "Function has no regions");
+ // This coverage record is a zero region for a function that's unused in
+ // some TU, but used in a different TU. Ignore it. The coverage maps from the
+ // the other TU will either be loaded (providing full region counts) or they
+ // won't (in which case we don't unintuitively report functions as uncovered
+ // when they have non-zero counts in the profile).
+ if (Record.MappingRegions.size() == 1 &&
+ Record.MappingRegions[0].Count.isZero() && Counts[0] > 0)
+ return Error::success();
+
FunctionRecord Function(OrigFuncName, Record.Filenames);
for (const auto &Region : Record.MappingRegions) {
Expected<int64_t> ExecutionCount = Ctx.evaluate(Region.Count);
@@ -239,11 +242,12 @@ Error CoverageMapping::loadFunctionRecord(
}
Function.pushRegion(Region, *ExecutionCount);
}
- if (Function.CountedRegions.size() != Record.MappingRegions.size()) {
- FuncCounterMismatches.emplace_back(Record.FunctionName,
- Function.CountedRegions.size());
+
+ // Don't create records for (filenames, function) pairs we've already seen.
+ auto FilenamesHash = hash_combine_range(Record.Filenames.begin(),
+ Record.Filenames.end());
+ if (!RecordProvenance[FilenamesHash].insert(hash_value(OrigFuncName)).second)
return Error::success();
- }
Functions.push_back(std::move(Function));
return Error::success();
@@ -459,8 +463,7 @@ class SegmentBuilder {
/// Sort a nested sequence of regions from a single file.
static void sortNestedRegions(MutableArrayRef<CountedRegion> Regions) {
- llvm::sort(Regions.begin(), Regions.end(), [](const CountedRegion &LHS,
- const CountedRegion &RHS) {
+ llvm::sort(Regions, [](const CountedRegion &LHS, const CountedRegion &RHS) {
if (LHS.startLoc() != RHS.startLoc())
return LHS.startLoc() < RHS.startLoc();
if (LHS.endLoc() != RHS.endLoc())
@@ -557,7 +560,7 @@ std::vector<StringRef> CoverageMapping::getUniqueSourceFiles() const {
for (const auto &Function : getCoveredFunctions())
Filenames.insert(Filenames.end(), Function.Filenames.begin(),
Function.Filenames.end());
- llvm::sort(Filenames.begin(), Filenames.end());
+ llvm::sort(Filenames);
auto Last = std::unique(Filenames.begin(), Filenames.end());
Filenames.erase(Last, Filenames.end());
return Filenames;
diff --git a/lib/ProfileData/GCOV.cpp b/lib/ProfileData/GCOV.cpp
index c9155439ec46..b687346a2c05 100644
--- a/lib/ProfileData/GCOV.cpp
+++ b/lib/ProfileData/GCOV.cpp
@@ -111,9 +111,7 @@ void GCOVFile::print(raw_ostream &OS) const {
#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());
-}
+LLVM_DUMP_METHOD void GCOVFile::dump() const { print(dbgs()); }
#endif
/// collectLineCounts - Collect line counts. This must be used after
@@ -359,9 +357,7 @@ void GCOVFunction::print(raw_ostream &OS) const {
#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());
-}
+LLVM_DUMP_METHOD void GCOVFunction::dump() const { print(dbgs()); }
#endif
/// collectLineCounts - Collect line counts. This must be used after
@@ -437,12 +433,135 @@ void GCOVBlock::print(raw_ostream &OS) const {
#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());
-}
+LLVM_DUMP_METHOD void GCOVBlock::dump() const { print(dbgs()); }
#endif
//===----------------------------------------------------------------------===//
+// Cycles detection
+//
+// The algorithm in GCC is based on the algorihtm by Hawick & James:
+// "Enumerating Circuits and Loops in Graphs with Self-Arcs and Multiple-Arcs"
+// http://complexity.massey.ac.nz/cstn/013/cstn-013.pdf.
+
+/// Get the count for the detected cycle.
+uint64_t GCOVBlock::getCycleCount(const Edges &Path) {
+ uint64_t CycleCount = std::numeric_limits<uint64_t>::max();
+ for (auto E : Path) {
+ CycleCount = std::min(E->CyclesCount, CycleCount);
+ }
+ for (auto E : Path) {
+ E->CyclesCount -= CycleCount;
+ }
+ return CycleCount;
+}
+
+/// Unblock a vertex previously marked as blocked.
+void GCOVBlock::unblock(const GCOVBlock *U, BlockVector &Blocked,
+ BlockVectorLists &BlockLists) {
+ auto it = find(Blocked, U);
+ if (it == Blocked.end()) {
+ return;
+ }
+
+ const size_t index = it - Blocked.begin();
+ Blocked.erase(it);
+
+ const BlockVector ToUnblock(BlockLists[index]);
+ BlockLists.erase(BlockLists.begin() + index);
+ for (auto GB : ToUnblock) {
+ GCOVBlock::unblock(GB, Blocked, BlockLists);
+ }
+}
+
+bool GCOVBlock::lookForCircuit(const GCOVBlock *V, const GCOVBlock *Start,
+ Edges &Path, BlockVector &Blocked,
+ BlockVectorLists &BlockLists,
+ const BlockVector &Blocks, uint64_t &Count) {
+ Blocked.push_back(V);
+ BlockLists.emplace_back(BlockVector());
+ bool FoundCircuit = false;
+
+ for (auto E : V->dsts()) {
+ const GCOVBlock *W = &E->Dst;
+ if (W < Start || find(Blocks, W) == Blocks.end()) {
+ continue;
+ }
+
+ Path.push_back(E);
+
+ if (W == Start) {
+ // We've a cycle.
+ Count += GCOVBlock::getCycleCount(Path);
+ FoundCircuit = true;
+ } else if (find(Blocked, W) == Blocked.end() && // W is not blocked.
+ GCOVBlock::lookForCircuit(W, Start, Path, Blocked, BlockLists,
+ Blocks, Count)) {
+ FoundCircuit = true;
+ }
+
+ Path.pop_back();
+ }
+
+ if (FoundCircuit) {
+ GCOVBlock::unblock(V, Blocked, BlockLists);
+ } else {
+ for (auto E : V->dsts()) {
+ const GCOVBlock *W = &E->Dst;
+ if (W < Start || find(Blocks, W) == Blocks.end()) {
+ continue;
+ }
+ const size_t index = find(Blocked, W) - Blocked.begin();
+ BlockVector &List = BlockLists[index];
+ if (find(List, V) == List.end()) {
+ List.push_back(V);
+ }
+ }
+ }
+
+ return FoundCircuit;
+}
+
+/// Get the count for the list of blocks which lie on the same line.
+void GCOVBlock::getCyclesCount(const BlockVector &Blocks, uint64_t &Count) {
+ for (auto Block : Blocks) {
+ Edges Path;
+ BlockVector Blocked;
+ BlockVectorLists BlockLists;
+
+ GCOVBlock::lookForCircuit(Block, Block, Path, Blocked, BlockLists, Blocks,
+ Count);
+ }
+}
+
+/// Get the count for the list of blocks which lie on the same line.
+uint64_t GCOVBlock::getLineCount(const BlockVector &Blocks) {
+ uint64_t Count = 0;
+
+ for (auto Block : Blocks) {
+ if (Block->getNumSrcEdges() == 0) {
+ // The block has no predecessors and a non-null counter
+ // (can be the case with entry block in functions).
+ Count += Block->getCount();
+ } else {
+ // Add counts from predecessors that are not on the same line.
+ for (auto E : Block->srcs()) {
+ const GCOVBlock *W = &E->Src;
+ if (find(Blocks, W) == Blocks.end()) {
+ Count += E->Count;
+ }
+ }
+ }
+ for (auto E : Block->dsts()) {
+ E->CyclesCount = E->Count;
+ }
+ }
+
+ GCOVBlock::getCyclesCount(Blocks, Count);
+
+ return Count;
+}
+
+//===----------------------------------------------------------------------===//
// FileInfo implementation.
// Safe integer division, returns 0 if numerator is 0.
@@ -578,8 +697,8 @@ FileInfo::openCoveragePath(StringRef CoveragePath) {
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);
+ 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>();
@@ -593,7 +712,7 @@ void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename,
SmallVector<StringRef, 4> Filenames;
for (const auto &LI : LineInfo)
Filenames.push_back(LI.first());
- llvm::sort(Filenames.begin(), Filenames.end());
+ llvm::sort(Filenames);
for (StringRef Filename : Filenames) {
auto AllLines = LineConsumer(Filename);
@@ -628,17 +747,7 @@ void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename,
// 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
@@ -674,6 +783,7 @@ void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename,
}
}
+ const uint64_t LineCount = GCOVBlock::getLineCount(Blocks);
if (LineCount == 0)
CovOS << " #####:";
else {
diff --git a/lib/ProfileData/InstrProf.cpp b/lib/ProfileData/InstrProf.cpp
index 544a77ec20a5..aaa8000ff2f9 100644
--- a/lib/ProfileData/InstrProf.cpp
+++ b/lib/ProfileData/InstrProf.cpp
@@ -252,11 +252,12 @@ static StringRef stripDirPrefix(StringRef PathNameStr, uint32_t NumPrefix) {
// data, its original linkage must be non-internal.
std::string getPGOFuncName(const Function &F, bool InLTO, uint64_t Version) {
if (!InLTO) {
- StringRef FileName = (StaticFuncFullModulePrefix
- ? F.getParent()->getName()
- : sys::path::filename(F.getParent()->getName()));
- if (StaticFuncFullModulePrefix && StaticFuncStripDirNamePrefix != 0)
- FileName = stripDirPrefix(FileName, StaticFuncStripDirNamePrefix);
+ StringRef FileName(F.getParent()->getSourceFileName());
+ uint32_t StripLevel = StaticFuncFullModulePrefix ? 0 : (uint32_t)-1;
+ if (StripLevel < StaticFuncStripDirNamePrefix)
+ StripLevel = StaticFuncStripDirNamePrefix;
+ if (StripLevel)
+ FileName = stripDirPrefix(FileName, StripLevel);
return getPGOFuncName(F.getName(), F.getLinkage(), FileName, Version);
}
diff --git a/lib/ProfileData/InstrProfReader.cpp b/lib/ProfileData/InstrProfReader.cpp
index 3b704158a5c5..eaf0eb04bfbf 100644
--- a/lib/ProfileData/InstrProfReader.cpp
+++ b/lib/ProfileData/InstrProfReader.cpp
@@ -14,6 +14,7 @@
#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/ProfileSummary.h"
@@ -23,6 +24,7 @@
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SymbolRemappingReader.h"
#include "llvm/Support/SwapByteOrder.h"
#include <algorithm>
#include <cctype>
@@ -88,16 +90,29 @@ InstrProfReader::create(std::unique_ptr<MemoryBuffer> Buffer) {
}
Expected<std::unique_ptr<IndexedInstrProfReader>>
-IndexedInstrProfReader::create(const Twine &Path) {
+IndexedInstrProfReader::create(const Twine &Path, const Twine &RemappingPath) {
// Set up the buffer to read.
auto BufferOrError = setupMemoryBuffer(Path);
if (Error E = BufferOrError.takeError())
return std::move(E);
- return IndexedInstrProfReader::create(std::move(BufferOrError.get()));
+
+ // Set up the remapping buffer if requested.
+ std::unique_ptr<MemoryBuffer> RemappingBuffer;
+ std::string RemappingPathStr = RemappingPath.str();
+ if (!RemappingPathStr.empty()) {
+ auto RemappingBufferOrError = setupMemoryBuffer(RemappingPathStr);
+ if (Error E = RemappingBufferOrError.takeError())
+ return std::move(E);
+ RemappingBuffer = std::move(RemappingBufferOrError.get());
+ }
+
+ return IndexedInstrProfReader::create(std::move(BufferOrError.get()),
+ std::move(RemappingBuffer));
}
Expected<std::unique_ptr<IndexedInstrProfReader>>
-IndexedInstrProfReader::create(std::unique_ptr<MemoryBuffer> Buffer) {
+IndexedInstrProfReader::create(std::unique_ptr<MemoryBuffer> Buffer,
+ std::unique_ptr<MemoryBuffer> RemappingBuffer) {
// Sanity check the buffer.
if (uint64_t(Buffer->getBufferSize()) > std::numeric_limits<unsigned>::max())
return make_error<InstrProfError>(instrprof_error::too_large);
@@ -105,7 +120,8 @@ IndexedInstrProfReader::create(std::unique_ptr<MemoryBuffer> Buffer) {
// Create the reader.
if (!IndexedInstrProfReader::hasFormat(*Buffer))
return make_error<InstrProfError>(instrprof_error::bad_magic);
- auto Result = llvm::make_unique<IndexedInstrProfReader>(std::move(Buffer));
+ auto Result = llvm::make_unique<IndexedInstrProfReader>(
+ std::move(Buffer), std::move(RemappingBuffer));
// Initialize the reader and return the result.
if (Error E = initializeReader(*Result))
@@ -587,6 +603,124 @@ InstrProfReaderIndex<HashTableImpl>::InstrProfReaderIndex(
RecordIterator = HashTable->data_begin();
}
+namespace {
+/// A remapper that does not apply any remappings.
+class InstrProfReaderNullRemapper : public InstrProfReaderRemapper {
+ InstrProfReaderIndexBase &Underlying;
+
+public:
+ InstrProfReaderNullRemapper(InstrProfReaderIndexBase &Underlying)
+ : Underlying(Underlying) {}
+
+ Error getRecords(StringRef FuncName,
+ ArrayRef<NamedInstrProfRecord> &Data) override {
+ return Underlying.getRecords(FuncName, Data);
+ }
+};
+}
+
+/// A remapper that applies remappings based on a symbol remapping file.
+template <typename HashTableImpl>
+class llvm::InstrProfReaderItaniumRemapper
+ : public InstrProfReaderRemapper {
+public:
+ InstrProfReaderItaniumRemapper(
+ std::unique_ptr<MemoryBuffer> RemapBuffer,
+ InstrProfReaderIndex<HashTableImpl> &Underlying)
+ : RemapBuffer(std::move(RemapBuffer)), Underlying(Underlying) {
+ }
+
+ /// Extract the original function name from a PGO function name.
+ static StringRef extractName(StringRef Name) {
+ // We can have multiple :-separated pieces; there can be pieces both
+ // before and after the mangled name. Find the first part that starts
+ // with '_Z'; we'll assume that's the mangled name we want.
+ std::pair<StringRef, StringRef> Parts = {StringRef(), Name};
+ while (true) {
+ Parts = Parts.second.split(':');
+ if (Parts.first.startswith("_Z"))
+ return Parts.first;
+ if (Parts.second.empty())
+ return Name;
+ }
+ }
+
+ /// Given a mangled name extracted from a PGO function name, and a new
+ /// form for that mangled name, reconstitute the name.
+ static void reconstituteName(StringRef OrigName, StringRef ExtractedName,
+ StringRef Replacement,
+ SmallVectorImpl<char> &Out) {
+ Out.reserve(OrigName.size() + Replacement.size() - ExtractedName.size());
+ Out.insert(Out.end(), OrigName.begin(), ExtractedName.begin());
+ Out.insert(Out.end(), Replacement.begin(), Replacement.end());
+ Out.insert(Out.end(), ExtractedName.end(), OrigName.end());
+ }
+
+ Error populateRemappings() override {
+ if (Error E = Remappings.read(*RemapBuffer))
+ return E;
+ for (StringRef Name : Underlying.HashTable->keys()) {
+ StringRef RealName = extractName(Name);
+ if (auto Key = Remappings.insert(RealName)) {
+ // FIXME: We could theoretically map the same equivalence class to
+ // multiple names in the profile data. If that happens, we should
+ // return NamedInstrProfRecords from all of them.
+ MappedNames.insert({Key, RealName});
+ }
+ }
+ return Error::success();
+ }
+
+ Error getRecords(StringRef FuncName,
+ ArrayRef<NamedInstrProfRecord> &Data) override {
+ StringRef RealName = extractName(FuncName);
+ if (auto Key = Remappings.lookup(RealName)) {
+ StringRef Remapped = MappedNames.lookup(Key);
+ if (!Remapped.empty()) {
+ if (RealName.begin() == FuncName.begin() &&
+ RealName.end() == FuncName.end())
+ FuncName = Remapped;
+ else {
+ // Try rebuilding the name from the given remapping.
+ SmallString<256> Reconstituted;
+ reconstituteName(FuncName, RealName, Remapped, Reconstituted);
+ Error E = Underlying.getRecords(Reconstituted, Data);
+ if (!E)
+ return E;
+
+ // If we failed because the name doesn't exist, fall back to asking
+ // about the original name.
+ if (Error Unhandled = handleErrors(
+ std::move(E), [](std::unique_ptr<InstrProfError> Err) {
+ return Err->get() == instrprof_error::unknown_function
+ ? Error::success()
+ : Error(std::move(Err));
+ }))
+ return Unhandled;
+ }
+ }
+ }
+ return Underlying.getRecords(FuncName, Data);
+ }
+
+private:
+ /// The memory buffer containing the remapping configuration. Remappings
+ /// holds pointers into this buffer.
+ std::unique_ptr<MemoryBuffer> RemapBuffer;
+
+ /// The mangling remapper.
+ SymbolRemappingReader Remappings;
+
+ /// Mapping from mangled name keys to the name used for the key in the
+ /// profile data.
+ /// FIXME: Can we store a location within the on-disk hash table instead of
+ /// redoing lookup?
+ DenseMap<SymbolRemappingReader::Key, StringRef> MappedNames;
+
+ /// The real profile data reader.
+ InstrProfReaderIndex<HashTableImpl> &Underlying;
+};
+
bool IndexedInstrProfReader::hasFormat(const MemoryBuffer &DataBuffer) {
using namespace support;
@@ -683,10 +817,22 @@ Error IndexedInstrProfReader::readHeader() {
uint64_t HashOffset = endian::byte_swap<uint64_t, little>(Header->HashOffset);
// The rest of the file is an on disk hash table.
- InstrProfReaderIndexBase *IndexPtr = nullptr;
- IndexPtr = new InstrProfReaderIndex<OnDiskHashTableImplV3>(
- Start + HashOffset, Cur, Start, HashType, FormatVersion);
- Index.reset(IndexPtr);
+ auto IndexPtr =
+ llvm::make_unique<InstrProfReaderIndex<OnDiskHashTableImplV3>>(
+ Start + HashOffset, Cur, Start, HashType, FormatVersion);
+
+ // Load the remapping table now if requested.
+ if (RemappingBuffer) {
+ Remapper = llvm::make_unique<
+ InstrProfReaderItaniumRemapper<OnDiskHashTableImplV3>>(
+ std::move(RemappingBuffer), *IndexPtr);
+ if (Error E = Remapper->populateRemappings())
+ return E;
+ } else {
+ Remapper = llvm::make_unique<InstrProfReaderNullRemapper>(*IndexPtr);
+ }
+ Index = std::move(IndexPtr);
+
return success();
}
@@ -707,7 +853,7 @@ Expected<InstrProfRecord>
IndexedInstrProfReader::getInstrProfRecord(StringRef FuncName,
uint64_t FuncHash) {
ArrayRef<NamedInstrProfRecord> Data;
- Error Err = Index->getRecords(FuncName, Data);
+ Error Err = Remapper->getRecords(FuncName, Data);
if (Err)
return std::move(Err);
// Found it. Look for counters with the right hash.
diff --git a/lib/ProfileData/ProfileSummaryBuilder.cpp b/lib/ProfileData/ProfileSummaryBuilder.cpp
index 62f00d693c68..3a8462fd9b0d 100644
--- a/lib/ProfileData/ProfileSummaryBuilder.cpp
+++ b/lib/ProfileData/ProfileSummaryBuilder.cpp
@@ -58,7 +58,7 @@ void SampleProfileSummaryBuilder::addRecord(
void ProfileSummaryBuilder::computeDetailedSummary() {
if (DetailedSummaryCutoffs.empty())
return;
- llvm::sort(DetailedSummaryCutoffs.begin(), DetailedSummaryCutoffs.end());
+ llvm::sort(DetailedSummaryCutoffs);
auto Iter = CountFrequencies.begin();
const auto End = CountFrequencies.end();
diff --git a/lib/ProfileData/SampleProf.cpp b/lib/ProfileData/SampleProf.cpp
index 30438ba7962a..1a124415f179 100644
--- a/lib/ProfileData/SampleProf.cpp
+++ b/lib/ProfileData/SampleProf.cpp
@@ -26,6 +26,14 @@
using namespace llvm;
using namespace sampleprof;
+namespace llvm {
+namespace sampleprof {
+SampleProfileFormat FunctionSamples::Format;
+DenseMap<uint64_t, StringRef> FunctionSamples::GUIDToFuncNameMap;
+Module *FunctionSamples::CurrentModule;
+} // namespace sampleprof
+} // namespace llvm
+
namespace {
// FIXME: This class is only here to support the transition to llvm::Error. It
@@ -59,6 +67,8 @@ class SampleProfErrorCategoryType : public std::error_category {
return "Unimplemented feature";
case sampleprof_error::counter_overflow:
return "Counter overflow";
+ case sampleprof_error::ostream_seek_unsupported:
+ return "Ostream does not support seek";
}
llvm_unreachable("A value of sampleprof_error has no message.");
}
diff --git a/lib/ProfileData/SampleProfReader.cpp b/lib/ProfileData/SampleProfReader.cpp
index 79335e67cd98..a68d1e9d3ab0 100644
--- a/lib/ProfileData/SampleProfReader.cpp
+++ b/lib/ProfileData/SampleProfReader.cpp
@@ -30,6 +30,7 @@
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/MD5.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@@ -320,6 +321,21 @@ ErrorOr<StringRef> SampleProfileReaderBinary::readString() {
}
template <typename T>
+ErrorOr<T> SampleProfileReaderBinary::readUnencodedNumber() {
+ std::error_code EC;
+
+ if (Data + sizeof(T) > End) {
+ EC = sampleprof_error::truncated;
+ reportError(0, EC.message());
+ return EC;
+ }
+
+ using namespace support;
+ T Val = endian::readNext<T, little, unaligned>(Data);
+ return Val;
+}
+
+template <typename T>
inline ErrorOr<uint32_t> SampleProfileReaderBinary::readStringIndex(T &Table) {
std::error_code EC;
auto Idx = readNumber<uint32_t>();
@@ -423,29 +439,51 @@ SampleProfileReaderBinary::readProfile(FunctionSamples &FProfile) {
return sampleprof_error::success;
}
-std::error_code SampleProfileReaderBinary::read() {
- while (!at_eof()) {
- auto NumHeadSamples = readNumber<uint64_t>();
- if (std::error_code EC = NumHeadSamples.getError())
- return EC;
+std::error_code SampleProfileReaderBinary::readFuncProfile() {
+ auto NumHeadSamples = readNumber<uint64_t>();
+ if (std::error_code EC = NumHeadSamples.getError())
+ return EC;
- auto FName(readStringFromTable());
- if (std::error_code EC = FName.getError())
- return EC;
+ auto FName(readStringFromTable());
+ if (std::error_code EC = FName.getError())
+ return EC;
- Profiles[*FName] = FunctionSamples();
- FunctionSamples &FProfile = Profiles[*FName];
- FProfile.setName(*FName);
+ Profiles[*FName] = FunctionSamples();
+ FunctionSamples &FProfile = Profiles[*FName];
+ FProfile.setName(*FName);
- FProfile.addHeadSamples(*NumHeadSamples);
+ FProfile.addHeadSamples(*NumHeadSamples);
+
+ if (std::error_code EC = readProfile(FProfile))
+ return EC;
+ return sampleprof_error::success;
+}
- if (std::error_code EC = readProfile(FProfile))
+std::error_code SampleProfileReaderBinary::read() {
+ while (!at_eof()) {
+ if (std::error_code EC = readFuncProfile())
return EC;
}
return sampleprof_error::success;
}
+std::error_code SampleProfileReaderCompactBinary::read() {
+ for (auto Name : FuncsToUse) {
+ auto GUID = std::to_string(MD5Hash(Name));
+ auto iter = FuncOffsetTable.find(StringRef(GUID));
+ if (iter == FuncOffsetTable.end())
+ continue;
+ const uint8_t *SavedData = Data;
+ Data = reinterpret_cast<const uint8_t *>(Buffer->getBufferStart()) +
+ iter->second;
+ if (std::error_code EC = readFuncProfile())
+ return EC;
+ Data = SavedData;
+ }
+ return sampleprof_error::success;
+}
+
std::error_code SampleProfileReaderRawBinary::verifySPMagic(uint64_t Magic) {
if (Magic == SPMagic())
return sampleprof_error::success;
@@ -514,6 +552,53 @@ std::error_code SampleProfileReaderBinary::readHeader() {
return sampleprof_error::success;
}
+std::error_code SampleProfileReaderCompactBinary::readHeader() {
+ SampleProfileReaderBinary::readHeader();
+ if (std::error_code EC = readFuncOffsetTable())
+ return EC;
+ return sampleprof_error::success;
+}
+
+std::error_code SampleProfileReaderCompactBinary::readFuncOffsetTable() {
+ auto TableOffset = readUnencodedNumber<uint64_t>();
+ if (std::error_code EC = TableOffset.getError())
+ return EC;
+
+ const uint8_t *SavedData = Data;
+ const uint8_t *TableStart =
+ reinterpret_cast<const uint8_t *>(Buffer->getBufferStart()) +
+ *TableOffset;
+ Data = TableStart;
+
+ auto Size = readNumber<uint64_t>();
+ if (std::error_code EC = Size.getError())
+ return EC;
+
+ FuncOffsetTable.reserve(*Size);
+ for (uint32_t I = 0; I < *Size; ++I) {
+ auto FName(readStringFromTable());
+ if (std::error_code EC = FName.getError())
+ return EC;
+
+ auto Offset = readNumber<uint64_t>();
+ if (std::error_code EC = Offset.getError())
+ return EC;
+
+ FuncOffsetTable[*FName] = *Offset;
+ }
+ End = TableStart;
+ Data = SavedData;
+ return sampleprof_error::success;
+}
+
+void SampleProfileReaderCompactBinary::collectFuncsToUse(const Module &M) {
+ FuncsToUse.clear();
+ for (auto &F : M) {
+ StringRef Fname = F.getName().split('.').first;
+ FuncsToUse.insert(Fname);
+ }
+}
+
std::error_code SampleProfileReaderBinary::readSummaryEntry(
std::vector<ProfileSummaryEntry> &Entries) {
auto Cutoff = readNumber<uint64_t>();
@@ -827,6 +912,40 @@ bool SampleProfileReaderGCC::hasFormat(const MemoryBuffer &Buffer) {
return Magic == "adcg*704";
}
+std::error_code SampleProfileReaderItaniumRemapper::read() {
+ // If the underlying data is in compact format, we can't remap it because
+ // we don't know what the original function names were.
+ if (getFormat() == SPF_Compact_Binary) {
+ Ctx.diagnose(DiagnosticInfoSampleProfile(
+ Buffer->getBufferIdentifier(),
+ "Profile data remapping cannot be applied to profile data "
+ "in compact format (original mangled names are not available).",
+ DS_Warning));
+ return sampleprof_error::success;
+ }
+
+ if (Error E = Remappings.read(*Buffer)) {
+ handleAllErrors(
+ std::move(E), [&](const SymbolRemappingParseError &ParseError) {
+ reportError(ParseError.getLineNum(), ParseError.getMessage());
+ });
+ return sampleprof_error::malformed;
+ }
+
+ for (auto &Sample : getProfiles())
+ if (auto Key = Remappings.insert(Sample.first()))
+ SampleMap.insert({Key, &Sample.second});
+
+ return sampleprof_error::success;
+}
+
+FunctionSamples *
+SampleProfileReaderItaniumRemapper::getSamplesFor(StringRef Fname) {
+ if (auto Key = Remappings.lookup(Fname))
+ return SampleMap.lookup(Key);
+ return SampleProfileReader::getSamplesFor(Fname);
+}
+
/// Prepare a memory buffer for the contents of \p Filename.
///
/// \returns an error code indicating the status of the buffer.
@@ -859,6 +978,27 @@ SampleProfileReader::create(const Twine &Filename, LLVMContext &C) {
return create(BufferOrError.get(), C);
}
+/// Create a sample profile remapper from the given input, to remap the
+/// function names in the given profile data.
+///
+/// \param Filename The file to open.
+///
+/// \param C The LLVM context to use to emit diagnostics.
+///
+/// \param Underlying The underlying profile data reader to remap.
+///
+/// \returns an error code indicating the status of the created reader.
+ErrorOr<std::unique_ptr<SampleProfileReader>>
+SampleProfileReaderItaniumRemapper::create(
+ const Twine &Filename, LLVMContext &C,
+ std::unique_ptr<SampleProfileReader> Underlying) {
+ auto BufferOrError = setupMemoryBuffer(Filename);
+ if (std::error_code EC = BufferOrError.getError())
+ return EC;
+ return llvm::make_unique<SampleProfileReaderItaniumRemapper>(
+ std::move(BufferOrError.get()), C, std::move(Underlying));
+}
+
/// Create a sample profile reader based on the format of the input data.
///
/// \param B The memory buffer to create the reader from (assumes ownership).
@@ -880,6 +1020,7 @@ SampleProfileReader::create(std::unique_ptr<MemoryBuffer> &B, LLVMContext &C) {
else
return sampleprof_error::unrecognized_format;
+ FunctionSamples::Format = Reader->getFormat();
if (std::error_code EC = Reader->readHeader())
return EC;
diff --git a/lib/ProfileData/SampleProfWriter.cpp b/lib/ProfileData/SampleProfWriter.cpp
index b4de30118b8b..b1c669ec31c4 100644
--- a/lib/ProfileData/SampleProfWriter.cpp
+++ b/lib/ProfileData/SampleProfWriter.cpp
@@ -22,6 +22,8 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/ProfileData/ProfileCommon.h"
#include "llvm/ProfileData/SampleProf.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/EndianStream.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/LEB128.h"
@@ -64,6 +66,15 @@ SampleProfileWriter::write(const StringMap<FunctionSamples> &ProfileMap) {
return sampleprof_error::success;
}
+std::error_code SampleProfileWriterCompactBinary::write(
+ const StringMap<FunctionSamples> &ProfileMap) {
+ if (std::error_code EC = SampleProfileWriter::write(ProfileMap))
+ return EC;
+ if (std::error_code EC = writeFuncOffsetTable())
+ return EC;
+ return sampleprof_error::success;
+}
+
/// Write samples to a text file.
///
/// Note: it may be tempting to implement this in terms of
@@ -168,6 +179,30 @@ std::error_code SampleProfileWriterRawBinary::writeNameTable() {
return sampleprof_error::success;
}
+std::error_code SampleProfileWriterCompactBinary::writeFuncOffsetTable() {
+ auto &OS = *OutputStream;
+
+ // Fill the slot remembered by TableOffset with the offset of FuncOffsetTable.
+ auto &OFS = static_cast<raw_fd_ostream &>(OS);
+ uint64_t FuncOffsetTableStart = OS.tell();
+ if (OFS.seek(TableOffset) == (uint64_t)-1)
+ return sampleprof_error::ostream_seek_unsupported;
+ support::endian::Writer Writer(*OutputStream, support::little);
+ Writer.write(FuncOffsetTableStart);
+ if (OFS.seek(FuncOffsetTableStart) == (uint64_t)-1)
+ return sampleprof_error::ostream_seek_unsupported;
+
+ // Write out the table size.
+ encodeULEB128(FuncOffsetTable.size(), OS);
+
+ // Write out FuncOffsetTable.
+ for (auto entry : FuncOffsetTable) {
+ writeNameIdx(entry.first);
+ encodeULEB128(entry.second, OS);
+ }
+ return sampleprof_error::success;
+}
+
std::error_code SampleProfileWriterCompactBinary::writeNameTable() {
auto &OS = *OutputStream;
std::set<StringRef> V;
@@ -215,6 +250,19 @@ std::error_code SampleProfileWriterBinary::writeHeader(
return sampleprof_error::success;
}
+std::error_code SampleProfileWriterCompactBinary::writeHeader(
+ const StringMap<FunctionSamples> &ProfileMap) {
+ support::endian::Writer Writer(*OutputStream, support::little);
+ if (auto EC = SampleProfileWriterBinary::writeHeader(ProfileMap))
+ return EC;
+
+ // Reserve a slot for the offset of function offset table. The slot will
+ // be populated with the offset of FuncOffsetTable later.
+ TableOffset = OutputStream->tell();
+ Writer.write(static_cast<uint64_t>(-2));
+ return sampleprof_error::success;
+}
+
std::error_code SampleProfileWriterBinary::writeSummary() {
auto &OS = *OutputStream;
encodeULEB128(Summary->getTotalCount(), OS);
@@ -283,6 +331,15 @@ std::error_code SampleProfileWriterBinary::write(const FunctionSamples &S) {
return writeBody(S);
}
+std::error_code
+SampleProfileWriterCompactBinary::write(const FunctionSamples &S) {
+ uint64_t Offset = OutputStream->tell();
+ StringRef Name = S.getName();
+ FuncOffsetTable[Name] = Offset;
+ encodeULEB128(S.getHeadSamples(), *OutputStream);
+ return writeBody(S);
+}
+
/// Create a sample profile file writer based on the specified format.
///
/// \param Filename The file to create.