Initial Commit

This commit is contained in:
Sajid
2024-09-07 18:00:09 +06:00
commit 0f9a53f75a
3352 changed files with 1563708 additions and 0 deletions

View File

@@ -0,0 +1,245 @@
//===- CoverageMappingReader.h - Code coverage mapping reader ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains support for reading coverage mapping data for
// instrumentation based coverage.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_PROFILEDATA_COVERAGE_COVERAGEMAPPINGREADER_H
#define LLVM_PROFILEDATA_COVERAGE_COVERAGEMAPPINGREADER_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ProfileData/Coverage/CoverageMapping.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MemoryBuffer.h"
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <memory>
#include <vector>
namespace llvm {
namespace coverage {
class CoverageMappingReader;
/// Coverage mapping information for a single function.
struct CoverageMappingRecord {
StringRef FunctionName;
uint64_t FunctionHash;
ArrayRef<StringRef> Filenames;
ArrayRef<CounterExpression> Expressions;
ArrayRef<CounterMappingRegion> MappingRegions;
};
/// A file format agnostic iterator over coverage mapping data.
class CoverageMappingIterator {
CoverageMappingReader *Reader;
CoverageMappingRecord Record;
coveragemap_error ReadErr;
void increment();
public:
using iterator_category = std::input_iterator_tag;
using value_type = CoverageMappingRecord;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = value_type &;
CoverageMappingIterator()
: Reader(nullptr), ReadErr(coveragemap_error::success) {}
CoverageMappingIterator(CoverageMappingReader *Reader)
: Reader(Reader), ReadErr(coveragemap_error::success) {
increment();
}
~CoverageMappingIterator() {
if (ReadErr != coveragemap_error::success)
llvm_unreachable("Unexpected error in coverage mapping iterator");
}
CoverageMappingIterator &operator++() {
increment();
return *this;
}
bool operator==(const CoverageMappingIterator &RHS) const {
return Reader == RHS.Reader;
}
bool operator!=(const CoverageMappingIterator &RHS) const {
return Reader != RHS.Reader;
}
Expected<CoverageMappingRecord &> operator*() {
if (ReadErr != coveragemap_error::success) {
auto E = make_error<CoverageMapError>(ReadErr);
ReadErr = coveragemap_error::success;
return std::move(E);
}
return Record;
}
Expected<CoverageMappingRecord *> operator->() {
if (ReadErr != coveragemap_error::success) {
auto E = make_error<CoverageMapError>(ReadErr);
ReadErr = coveragemap_error::success;
return std::move(E);
}
return &Record;
}
};
class CoverageMappingReader {
public:
virtual ~CoverageMappingReader() = default;
virtual Error readNextRecord(CoverageMappingRecord &Record) = 0;
CoverageMappingIterator begin() { return CoverageMappingIterator(this); }
CoverageMappingIterator end() { return CoverageMappingIterator(); }
};
/// Base class for the raw coverage mapping and filenames data readers.
class RawCoverageReader {
protected:
StringRef Data;
RawCoverageReader(StringRef Data) : Data(Data) {}
Error readULEB128(uint64_t &Result);
Error readIntMax(uint64_t &Result, uint64_t MaxPlus1);
Error readSize(uint64_t &Result);
Error readString(StringRef &Result);
};
/// Checks if the given coverage mapping data is exported for
/// an unused function.
class RawCoverageMappingDummyChecker : public RawCoverageReader {
public:
RawCoverageMappingDummyChecker(StringRef MappingData)
: RawCoverageReader(MappingData) {}
Expected<bool> isDummy();
};
/// Reader for the raw coverage mapping data.
class RawCoverageMappingReader : public RawCoverageReader {
ArrayRef<std::string> &TranslationUnitFilenames;
std::vector<StringRef> &Filenames;
std::vector<CounterExpression> &Expressions;
std::vector<CounterMappingRegion> &MappingRegions;
public:
RawCoverageMappingReader(StringRef MappingData,
ArrayRef<std::string> &TranslationUnitFilenames,
std::vector<StringRef> &Filenames,
std::vector<CounterExpression> &Expressions,
std::vector<CounterMappingRegion> &MappingRegions)
: RawCoverageReader(MappingData),
TranslationUnitFilenames(TranslationUnitFilenames),
Filenames(Filenames), Expressions(Expressions),
MappingRegions(MappingRegions) {}
RawCoverageMappingReader(const RawCoverageMappingReader &) = delete;
RawCoverageMappingReader &
operator=(const RawCoverageMappingReader &) = delete;
Error read();
private:
Error decodeCounter(unsigned Value, Counter &C);
Error readCounter(Counter &C);
Error
readMappingRegionsSubArray(std::vector<CounterMappingRegion> &MappingRegions,
unsigned InferredFileID, size_t NumFileIDs);
};
/// Reader for the coverage mapping data that is emitted by the
/// frontend and stored in an object file.
class BinaryCoverageReader : public CoverageMappingReader {
public:
struct ProfileMappingRecord {
CovMapVersion Version;
StringRef FunctionName;
uint64_t FunctionHash;
StringRef CoverageMapping;
size_t FilenamesBegin;
size_t FilenamesSize;
ProfileMappingRecord(CovMapVersion Version, StringRef FunctionName,
uint64_t FunctionHash, StringRef CoverageMapping,
size_t FilenamesBegin, size_t FilenamesSize)
: Version(Version), FunctionName(FunctionName),
FunctionHash(FunctionHash), CoverageMapping(CoverageMapping),
FilenamesBegin(FilenamesBegin), FilenamesSize(FilenamesSize) {}
};
using FuncRecordsStorage = std::unique_ptr<MemoryBuffer>;
private:
std::vector<std::string> Filenames;
std::vector<ProfileMappingRecord> MappingRecords;
InstrProfSymtab ProfileNames;
size_t CurrentRecord = 0;
std::vector<StringRef> FunctionsFilenames;
std::vector<CounterExpression> Expressions;
std::vector<CounterMappingRegion> MappingRegions;
// Used to tie the lifetimes of coverage function records to the lifetime of
// this BinaryCoverageReader instance. Needed to support the format change in
// D69471, which can split up function records into multiple sections on ELF.
FuncRecordsStorage FuncRecords;
BinaryCoverageReader(FuncRecordsStorage &&FuncRecords)
: FuncRecords(std::move(FuncRecords)) {}
public:
BinaryCoverageReader(const BinaryCoverageReader &) = delete;
BinaryCoverageReader &operator=(const BinaryCoverageReader &) = delete;
static Expected<std::vector<std::unique_ptr<BinaryCoverageReader>>>
create(MemoryBufferRef ObjectBuffer, StringRef Arch,
SmallVectorImpl<std::unique_ptr<MemoryBuffer>> &ObjectFileBuffers,
StringRef CompilationDir = "");
static Expected<std::unique_ptr<BinaryCoverageReader>>
createCoverageReaderFromBuffer(StringRef Coverage,
FuncRecordsStorage &&FuncRecords,
InstrProfSymtab &&ProfileNames,
uint8_t BytesInAddress,
support::endianness Endian,
StringRef CompilationDir = "");
Error readNextRecord(CoverageMappingRecord &Record) override;
};
/// Reader for the raw coverage filenames.
class RawCoverageFilenamesReader : public RawCoverageReader {
std::vector<std::string> &Filenames;
StringRef CompilationDir;
// Read an uncompressed sequence of filenames.
Error readUncompressed(CovMapVersion Version, uint64_t NumFilenames);
public:
RawCoverageFilenamesReader(StringRef Data,
std::vector<std::string> &Filenames,
StringRef CompilationDir = "")
: RawCoverageReader(Data), Filenames(Filenames),
CompilationDir(CompilationDir) {}
RawCoverageFilenamesReader(const RawCoverageFilenamesReader &) = delete;
RawCoverageFilenamesReader &
operator=(const RawCoverageFilenamesReader &) = delete;
Error read(CovMapVersion Version);
};
} // end namespace coverage
} // end namespace llvm
#endif // LLVM_PROFILEDATA_COVERAGE_COVERAGEMAPPINGREADER_H

View File

@@ -0,0 +1,61 @@
//===- CoverageMappingWriter.h - Code coverage mapping writer ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains support for writing coverage mapping data for
// instrumentation based coverage.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_PROFILEDATA_COVERAGE_COVERAGEMAPPINGWRITER_H
#define LLVM_PROFILEDATA_COVERAGE_COVERAGEMAPPINGWRITER_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ProfileData/Coverage/CoverageMapping.h"
namespace llvm {
class raw_ostream;
namespace coverage {
/// Writer of the filenames section for the instrumentation
/// based code coverage.
class CoverageFilenamesSectionWriter {
ArrayRef<std::string> Filenames;
public:
CoverageFilenamesSectionWriter(ArrayRef<std::string> Filenames);
/// Write encoded filenames to the given output stream. If \p Compress is
/// true, attempt to compress the filenames.
void write(raw_ostream &OS, bool Compress = true);
};
/// Writer for instrumentation based coverage mapping data.
class CoverageMappingWriter {
ArrayRef<unsigned> VirtualFileMapping;
ArrayRef<CounterExpression> Expressions;
MutableArrayRef<CounterMappingRegion> MappingRegions;
public:
CoverageMappingWriter(ArrayRef<unsigned> VirtualFileMapping,
ArrayRef<CounterExpression> Expressions,
MutableArrayRef<CounterMappingRegion> MappingRegions)
: VirtualFileMapping(VirtualFileMapping), Expressions(Expressions),
MappingRegions(MappingRegions) {}
/// Write encoded coverage mapping data to the given output stream.
void write(raw_ostream &OS);
};
} // end namespace coverage
} // end namespace llvm
#endif // LLVM_PROFILEDATA_COVERAGE_COVERAGEMAPPINGWRITER_H

View File

@@ -0,0 +1,323 @@
//===- GCOV.h - LLVM coverage tool ------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This header provides the interface to read and write coverage files that
// use 'gcov' format.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_PROFILEDATA_GCOV_H
#define LLVM_PROFILEDATA_GCOV_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <map>
#include <memory>
#include <string>
#include <utility>
namespace llvm {
class GCOVFunction;
class GCOVBlock;
namespace GCOV {
enum GCOVVersion { V304, V407, V408, V800, V900, V1200 };
/// A struct for passing gcov options between functions.
struct Options {
Options(bool A, bool B, bool C, bool F, bool P, bool U, bool I, bool L,
bool M, bool N, bool R, bool T, bool X, std::string SourcePrefix)
: AllBlocks(A), BranchInfo(B), BranchCount(C), FuncCoverage(F),
PreservePaths(P), UncondBranch(U), Intermediate(I), LongFileNames(L),
Demangle(M), NoOutput(N), RelativeOnly(R), UseStdout(T),
HashFilenames(X), SourcePrefix(std::move(SourcePrefix)) {}
bool AllBlocks;
bool BranchInfo;
bool BranchCount;
bool FuncCoverage;
bool PreservePaths;
bool UncondBranch;
bool Intermediate;
bool LongFileNames;
bool Demangle;
bool NoOutput;
bool RelativeOnly;
bool UseStdout;
bool HashFilenames;
std::string SourcePrefix;
};
} // end namespace GCOV
/// GCOVBuffer - A wrapper around MemoryBuffer to provide GCOV specific
/// read operations.
class GCOVBuffer {
public:
GCOVBuffer(MemoryBuffer *B) : Buffer(B) {}
~GCOVBuffer() { consumeError(cursor.takeError()); }
/// readGCNOFormat - Check GCNO signature is valid at the beginning of buffer.
bool readGCNOFormat() {
StringRef buf = Buffer->getBuffer();
StringRef magic = buf.substr(0, 4);
if (magic == "gcno") {
de = DataExtractor(buf.substr(4), false, 0);
} else if (magic == "oncg") {
de = DataExtractor(buf.substr(4), true, 0);
} else {
errs() << "unexpected magic: " << magic << "\n";
return false;
}
return true;
}
/// readGCDAFormat - Check GCDA signature is valid at the beginning of buffer.
bool readGCDAFormat() {
StringRef buf = Buffer->getBuffer();
StringRef magic = buf.substr(0, 4);
if (magic == "gcda") {
de = DataExtractor(buf.substr(4), false, 0);
} else if (magic == "adcg") {
de = DataExtractor(buf.substr(4), true, 0);
} else {
return false;
}
return true;
}
/// readGCOVVersion - Read GCOV version.
bool readGCOVVersion(GCOV::GCOVVersion &version) {
std::string str(de.getBytes(cursor, 4));
if (str.size() != 4)
return false;
if (de.isLittleEndian())
std::reverse(str.begin(), str.end());
int ver = str[0] >= 'A'
? (str[0] - 'A') * 100 + (str[1] - '0') * 10 + str[2] - '0'
: (str[0] - '0') * 10 + str[2] - '0';
if (ver >= 120) {
this->version = version = GCOV::V1200;
return true;
} else if (ver >= 90) {
// PR gcov-profile/84846, r269678
this->version = version = GCOV::V900;
return true;
} else if (ver >= 80) {
// PR gcov-profile/48463
this->version = version = GCOV::V800;
return true;
} else if (ver >= 48) {
// r189778: the exit block moved from the last to the second.
this->version = version = GCOV::V408;
return true;
} else if (ver >= 47) {
// r173147: split checksum into cfg checksum and line checksum.
this->version = version = GCOV::V407;
return true;
} else if (ver >= 34) {
this->version = version = GCOV::V304;
return true;
}
errs() << "unexpected version: " << str << "\n";
return false;
}
uint32_t getWord() { return de.getU32(cursor); }
StringRef getString() {
uint32_t len;
if (!readInt(len) || len == 0)
return {};
return de.getBytes(cursor, len * 4).split('\0').first;
}
bool readInt(uint32_t &Val) {
if (cursor.tell() + 4 > de.size()) {
Val = 0;
errs() << "unexpected end of memory buffer: " << cursor.tell() << "\n";
return false;
}
Val = de.getU32(cursor);
return true;
}
bool readInt64(uint64_t &Val) {
uint32_t Lo, Hi;
if (!readInt(Lo) || !readInt(Hi))
return false;
Val = ((uint64_t)Hi << 32) | Lo;
return true;
}
bool readString(StringRef &str) {
uint32_t len;
if (!readInt(len) || len == 0)
return false;
if (version >= GCOV::V1200)
str = de.getBytes(cursor, len).drop_back();
else
str = de.getBytes(cursor, len * 4).split('\0').first;
return bool(cursor);
}
DataExtractor de{ArrayRef<uint8_t>{}, false, 0};
DataExtractor::Cursor cursor{0};
private:
MemoryBuffer *Buffer;
GCOV::GCOVVersion version{};
};
/// GCOVFile - Collects coverage information for one pair of coverage file
/// (.gcno and .gcda).
class GCOVFile {
public:
GCOVFile() = default;
bool readGCNO(GCOVBuffer &Buffer);
bool readGCDA(GCOVBuffer &Buffer);
GCOV::GCOVVersion getVersion() const { return version; }
void print(raw_ostream &OS) const;
void dump() const;
std::vector<std::string> filenames;
StringMap<unsigned> filenameToIdx;
public:
bool GCNOInitialized = false;
GCOV::GCOVVersion version{};
uint32_t checksum = 0;
StringRef cwd;
SmallVector<std::unique_ptr<GCOVFunction>, 16> functions;
std::map<uint32_t, GCOVFunction *> identToFunction;
uint32_t runCount = 0;
uint32_t programCount = 0;
using iterator = pointee_iterator<
SmallVectorImpl<std::unique_ptr<GCOVFunction>>::const_iterator>;
iterator begin() const { return iterator(functions.begin()); }
iterator end() const { return iterator(functions.end()); }
};
struct GCOVArc {
GCOVArc(GCOVBlock &src, GCOVBlock &dst, uint32_t flags)
: src(src), dst(dst), flags(flags) {}
bool onTree() const;
GCOVBlock &src;
GCOVBlock &dst;
uint32_t flags;
uint64_t count = 0;
uint64_t cycleCount = 0;
};
/// GCOVFunction - Collects function information.
class GCOVFunction {
public:
using BlockIterator = pointee_iterator<
SmallVectorImpl<std::unique_ptr<GCOVBlock>>::const_iterator>;
GCOVFunction(GCOVFile &file) : file(file) {}
StringRef getName(bool demangle) const;
StringRef getFilename() const;
uint64_t getEntryCount() const;
GCOVBlock &getExitBlock() const;
iterator_range<BlockIterator> blocksRange() const {
return make_range(blocks.begin(), blocks.end());
}
uint64_t propagateCounts(const GCOVBlock &v, GCOVArc *pred);
void print(raw_ostream &OS) const;
void dump() const;
GCOVFile &file;
uint32_t ident = 0;
uint32_t linenoChecksum;
uint32_t cfgChecksum = 0;
uint32_t startLine = 0;
uint32_t startColumn = 0;
uint32_t endLine = 0;
uint32_t endColumn = 0;
uint8_t artificial = 0;
StringRef Name;
mutable SmallString<0> demangled;
unsigned srcIdx;
SmallVector<std::unique_ptr<GCOVBlock>, 0> blocks;
SmallVector<std::unique_ptr<GCOVArc>, 0> arcs, treeArcs;
DenseSet<const GCOVBlock *> visited;
};
/// GCOVBlock - Collects block information.
class GCOVBlock {
public:
using EdgeIterator = SmallVectorImpl<GCOVArc *>::const_iterator;
using BlockVector = SmallVector<const GCOVBlock *, 1>;
using BlockVectorLists = SmallVector<BlockVector, 4>;
using Edges = SmallVector<GCOVArc *, 4>;
GCOVBlock(uint32_t N) : number(N) {}
void addLine(uint32_t N) { lines.push_back(N); }
uint32_t getLastLine() const { return lines.back(); }
uint64_t getCount() const { return count; }
void addSrcEdge(GCOVArc *Edge) { pred.push_back(Edge); }
void addDstEdge(GCOVArc *Edge) { succ.push_back(Edge); }
iterator_range<EdgeIterator> srcs() const {
return make_range(pred.begin(), pred.end());
}
iterator_range<EdgeIterator> dsts() const {
return make_range(succ.begin(), succ.end());
}
void print(raw_ostream &OS) const;
void dump() const;
static uint64_t
augmentOneCycle(GCOVBlock *src,
std::vector<std::pair<GCOVBlock *, size_t>> &stack);
static uint64_t getCyclesCount(const BlockVector &blocks);
static uint64_t getLineCount(const BlockVector &Blocks);
public:
uint32_t number;
uint64_t count = 0;
SmallVector<GCOVArc *, 2> pred;
SmallVector<GCOVArc *, 2> succ;
SmallVector<uint32_t, 4> lines;
bool traversable = false;
GCOVArc *incoming = nullptr;
};
void gcovOneInput(const GCOV::Options &options, StringRef filename,
StringRef gcno, StringRef gcda, GCOVFile &file);
} // end namespace llvm
#endif // LLVM_PROFILEDATA_GCOV_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,178 @@
//===- InstrProfCorrelator.h ------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This file defines InstrProfCorrelator used to generate PGO profiles from
// raw profile data and debug info.
//===----------------------------------------------------------------------===//
#ifndef LLVM_PROFILEDATA_INSTRPROFCORRELATOR_H
#define LLVM_PROFILEDATA_INSTRPROFCORRELATOR_H
#include "llvm/ADT/DenseSet.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MemoryBuffer.h"
#include <vector>
namespace llvm {
/// InstrProfCorrelator - A base class used to create raw instrumentation data
/// to their functions.
class InstrProfCorrelator {
public:
static llvm::Expected<std::unique_ptr<InstrProfCorrelator>>
get(StringRef DebugInfoFilename);
/// Construct a ProfileData vector used to correlate raw instrumentation data
/// to their functions.
virtual Error correlateProfileData() = 0;
/// Return the number of ProfileData elements.
llvm::Optional<size_t> getDataSize() const;
/// Return a pointer to the names string that this class constructs.
const char *getNamesPointer() const { return Names.c_str(); }
/// Return the number of bytes in the names string.
size_t getNamesSize() const { return Names.size(); }
/// Return the size of the counters section in bytes.
uint64_t getCountersSectionSize() const {
return Ctx->CountersSectionEnd - Ctx->CountersSectionStart;
}
static const char *FunctionNameAttributeName;
static const char *CFGHashAttributeName;
static const char *NumCountersAttributeName;
enum InstrProfCorrelatorKind { CK_32Bit, CK_64Bit };
InstrProfCorrelatorKind getKind() const { return Kind; }
virtual ~InstrProfCorrelator() = default;
protected:
struct Context {
static llvm::Expected<std::unique_ptr<Context>>
get(std::unique_ptr<MemoryBuffer> Buffer, const object::ObjectFile &Obj);
std::unique_ptr<MemoryBuffer> Buffer;
/// The address range of the __llvm_prf_cnts section.
uint64_t CountersSectionStart;
uint64_t CountersSectionEnd;
/// True if target and host have different endian orders.
bool ShouldSwapBytes;
};
const std::unique_ptr<InstrProfCorrelator::Context> Ctx;
InstrProfCorrelator(InstrProfCorrelatorKind K, std::unique_ptr<Context> Ctx)
: Ctx(std::move(Ctx)), Kind(K) {}
std::string Names;
std::vector<std::string> NamesVec;
private:
static llvm::Expected<std::unique_ptr<InstrProfCorrelator>>
get(std::unique_ptr<MemoryBuffer> Buffer);
const InstrProfCorrelatorKind Kind;
};
/// InstrProfCorrelatorImpl - A child of InstrProfCorrelator with a template
/// pointer type so that the ProfileData vector can be materialized.
template <class IntPtrT>
class InstrProfCorrelatorImpl : public InstrProfCorrelator {
public:
InstrProfCorrelatorImpl(std::unique_ptr<InstrProfCorrelator::Context> Ctx);
static bool classof(const InstrProfCorrelator *C);
/// Return a pointer to the underlying ProfileData vector that this class
/// constructs.
const RawInstrProf::ProfileData<IntPtrT> *getDataPointer() const {
return Data.empty() ? nullptr : Data.data();
}
/// Return the number of ProfileData elements.
size_t getDataSize() const { return Data.size(); }
static llvm::Expected<std::unique_ptr<InstrProfCorrelatorImpl<IntPtrT>>>
get(std::unique_ptr<InstrProfCorrelator::Context> Ctx,
const object::ObjectFile &Obj);
protected:
std::vector<RawInstrProf::ProfileData<IntPtrT>> Data;
Error correlateProfileData() override;
virtual void correlateProfileDataImpl() = 0;
void addProbe(StringRef FunctionName, uint64_t CFGHash, IntPtrT CounterOffset,
IntPtrT FunctionPtr, uint32_t NumCounters);
private:
InstrProfCorrelatorImpl(InstrProfCorrelatorKind Kind,
std::unique_ptr<InstrProfCorrelator::Context> Ctx)
: InstrProfCorrelator(Kind, std::move(Ctx)){};
llvm::DenseSet<IntPtrT> CounterOffsets;
// Byte-swap the value if necessary.
template <class T> T maybeSwap(T Value) const {
return Ctx->ShouldSwapBytes ? sys::getSwappedBytes(Value) : Value;
}
};
/// DwarfInstrProfCorrelator - A child of InstrProfCorrelatorImpl that takes
/// DWARF debug info as input to correlate profiles.
template <class IntPtrT>
class DwarfInstrProfCorrelator : public InstrProfCorrelatorImpl<IntPtrT> {
public:
DwarfInstrProfCorrelator(std::unique_ptr<DWARFContext> DICtx,
std::unique_ptr<InstrProfCorrelator::Context> Ctx)
: InstrProfCorrelatorImpl<IntPtrT>(std::move(Ctx)),
DICtx(std::move(DICtx)) {}
private:
std::unique_ptr<DWARFContext> DICtx;
/// Return the address of the object that the provided DIE symbolizes.
llvm::Optional<uint64_t> getLocation(const DWARFDie &Die) const;
/// Returns true if the provided DIE symbolizes an instrumentation probe
/// symbol.
static bool isDIEOfProbe(const DWARFDie &Die);
/// Iterate over DWARF DIEs to find those that symbolize instrumentation
/// probes and construct the ProfileData vector and Names string.
///
/// Here is some example DWARF for an instrumentation probe we are looking
/// for:
/// \code
/// DW_TAG_subprogram
/// DW_AT_low_pc (0x0000000000000000)
/// DW_AT_high_pc (0x0000000000000014)
/// DW_AT_name ("foo")
/// DW_TAG_variable
/// DW_AT_name ("__profc_foo")
/// DW_AT_location (DW_OP_addr 0x0)
/// DW_TAG_LLVM_annotation
/// DW_AT_name ("Function Name")
/// DW_AT_const_value ("foo")
/// DW_TAG_LLVM_annotation
/// DW_AT_name ("CFG Hash")
/// DW_AT_const_value (12345678)
/// DW_TAG_LLVM_annotation
/// DW_AT_name ("Num Counters")
/// DW_AT_const_value (2)
/// NULL
/// NULL
/// \endcode
void correlateProfileDataImpl() override;
};
} // end namespace llvm
#endif // LLVM_PROFILEDATA_INSTRPROFCORRELATOR_H

View File

@@ -0,0 +1,650 @@
//===- InstrProfReader.h - Instrumented profiling readers -------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains support for reading profiling data for instrumentation
// based PGO and coverage.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_PROFILEDATA_INSTRPROFREADER_H
#define LLVM_PROFILEDATA_INSTRPROFREADER_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/ProfileSummary.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/ProfileData/InstrProfCorrelator.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/LineIterator.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/OnDiskHashTable.h"
#include "llvm/Support/SwapByteOrder.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <memory>
#include <utility>
#include <vector>
namespace llvm {
class InstrProfReader;
/// A file format agnostic iterator over profiling data.
class InstrProfIterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = NamedInstrProfRecord;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = value_type &;
private:
InstrProfReader *Reader = nullptr;
value_type Record;
void Increment();
public:
InstrProfIterator() = default;
InstrProfIterator(InstrProfReader *Reader) : Reader(Reader) { Increment(); }
InstrProfIterator &operator++() { Increment(); return *this; }
bool operator==(const InstrProfIterator &RHS) const {
return Reader == RHS.Reader;
}
bool operator!=(const InstrProfIterator &RHS) const {
return Reader != RHS.Reader;
}
value_type &operator*() { return Record; }
value_type *operator->() { return &Record; }
};
/// Base class and interface for reading profiling data of any known instrprof
/// format. Provides an iterator over NamedInstrProfRecords.
class InstrProfReader {
instrprof_error LastError = instrprof_error::success;
std::string LastErrorMsg;
public:
InstrProfReader() = default;
virtual ~InstrProfReader() = default;
/// Read the header. Required before reading first record.
virtual Error readHeader() = 0;
/// Read a single record.
virtual Error readNextRecord(NamedInstrProfRecord &Record) = 0;
/// Print binary ids on stream OS.
virtual Error printBinaryIds(raw_ostream &OS) { return success(); };
/// Iterator over profile data.
InstrProfIterator begin() { return InstrProfIterator(this); }
InstrProfIterator end() { return InstrProfIterator(); }
virtual bool isIRLevelProfile() const = 0;
virtual bool hasCSIRLevelProfile() const = 0;
virtual bool instrEntryBBEnabled() const = 0;
/// Return true if we must provide debug info to create PGO profiles.
virtual bool useDebugInfoCorrelate() const { return false; }
/// Return true if the profile has single byte counters representing coverage.
virtual bool hasSingleByteCoverage() const = 0;
/// Return true if the profile only instruments function entries.
virtual bool functionEntryOnly() const = 0;
/// Returns a BitsetEnum describing the attributes of the profile. To check
/// individual attributes prefer using the helpers above.
virtual InstrProfKind getProfileKind() const = 0;
/// Return the PGO symtab. There are three different readers:
/// Raw, Text, and Indexed profile readers. The first two types
/// of readers are used only by llvm-profdata tool, while the indexed
/// profile reader is also used by llvm-cov tool and the compiler (
/// backend or frontend). Since creating PGO symtab can create
/// significant runtime and memory overhead (as it touches data
/// for the whole program), InstrProfSymtab for the indexed profile
/// reader should be created on demand and it is recommended to be
/// only used for dumping purpose with llvm-proftool, not with the
/// compiler.
virtual InstrProfSymtab &getSymtab() = 0;
/// Compute the sum of counts and return in Sum.
void accumulateCounts(CountSumOrPercent &Sum, bool IsCS);
protected:
std::unique_ptr<InstrProfSymtab> Symtab;
/// Set the current error and return same.
Error error(instrprof_error Err, const std::string &ErrMsg = "") {
LastError = Err;
LastErrorMsg = ErrMsg;
if (Err == instrprof_error::success)
return Error::success();
return make_error<InstrProfError>(Err, ErrMsg);
}
Error error(Error &&E) {
handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
LastError = IPE.get();
LastErrorMsg = IPE.getMessage();
});
return make_error<InstrProfError>(LastError, LastErrorMsg);
}
/// Clear the current error and return a successful one.
Error success() { return error(instrprof_error::success); }
public:
/// Return true if the reader has finished reading the profile data.
bool isEOF() { return LastError == instrprof_error::eof; }
/// Return true if the reader encountered an error reading profiling data.
bool hasError() { return LastError != instrprof_error::success && !isEOF(); }
/// Get the current error.
Error getError() {
if (hasError())
return make_error<InstrProfError>(LastError, LastErrorMsg);
return Error::success();
}
/// Factory method to create an appropriately typed reader for the given
/// instrprof file.
static Expected<std::unique_ptr<InstrProfReader>>
create(const Twine &Path, const InstrProfCorrelator *Correlator = nullptr);
static Expected<std::unique_ptr<InstrProfReader>>
create(std::unique_ptr<MemoryBuffer> Buffer,
const InstrProfCorrelator *Correlator = nullptr);
};
/// Reader for the simple text based instrprof format.
///
/// This format is a simple text format that's suitable for test data. Records
/// are separated by one or more blank lines, and record fields are separated by
/// new lines.
///
/// Each record consists of a function name, a function hash, a number of
/// counters, and then each counter value, in that order.
class TextInstrProfReader : public InstrProfReader {
private:
/// The profile data file contents.
std::unique_ptr<MemoryBuffer> DataBuffer;
/// Iterator over the profile data.
line_iterator Line;
/// The attributes of the current profile.
InstrProfKind ProfileKind = InstrProfKind::Unknown;
Error readValueProfileData(InstrProfRecord &Record);
public:
TextInstrProfReader(std::unique_ptr<MemoryBuffer> DataBuffer_)
: DataBuffer(std::move(DataBuffer_)), Line(*DataBuffer, true, '#') {}
TextInstrProfReader(const TextInstrProfReader &) = delete;
TextInstrProfReader &operator=(const TextInstrProfReader &) = delete;
/// Return true if the given buffer is in text instrprof format.
static bool hasFormat(const MemoryBuffer &Buffer);
bool isIRLevelProfile() const override {
return static_cast<bool>(ProfileKind & InstrProfKind::IR);
}
bool hasCSIRLevelProfile() const override {
return static_cast<bool>(ProfileKind & InstrProfKind::CS);
}
bool instrEntryBBEnabled() const override {
return static_cast<bool>(ProfileKind & InstrProfKind::BB);
}
bool hasSingleByteCoverage() const override {
return static_cast<bool>(ProfileKind & InstrProfKind::SingleByteCoverage);
}
bool functionEntryOnly() const override {
return static_cast<bool>(ProfileKind & InstrProfKind::FunctionEntryOnly);
}
InstrProfKind getProfileKind() const override { return ProfileKind; }
/// Read the header.
Error readHeader() override;
/// Read a single record.
Error readNextRecord(NamedInstrProfRecord &Record) override;
InstrProfSymtab &getSymtab() override {
assert(Symtab.get());
return *Symtab.get();
}
};
/// Reader for the raw instrprof binary format from runtime.
///
/// This format is a raw memory dump of the instrumentation-based profiling data
/// from the runtime. It has no index.
///
/// Templated on the unsigned type whose size matches pointers on the platform
/// that wrote the profile.
template <class IntPtrT>
class RawInstrProfReader : public InstrProfReader {
private:
/// The profile data file contents.
std::unique_ptr<MemoryBuffer> DataBuffer;
/// If available, this hold the ProfileData array used to correlate raw
/// instrumentation data to their functions.
const InstrProfCorrelatorImpl<IntPtrT> *Correlator;
bool ShouldSwapBytes;
// The value of the version field of the raw profile data header. The lower 56
// bits specifies the format version and the most significant 8 bits specify
// the variant types of the profile.
uint64_t Version;
uint64_t CountersDelta;
uint64_t NamesDelta;
const RawInstrProf::ProfileData<IntPtrT> *Data;
const RawInstrProf::ProfileData<IntPtrT> *DataEnd;
const char *CountersStart;
const char *CountersEnd;
const char *NamesStart;
const char *NamesEnd;
// After value profile is all read, this pointer points to
// the header of next profile data (if exists)
const uint8_t *ValueDataStart;
uint32_t ValueKindLast;
uint32_t CurValueDataSize;
uint64_t BinaryIdsSize;
const uint8_t *BinaryIdsStart;
public:
RawInstrProfReader(std::unique_ptr<MemoryBuffer> DataBuffer,
const InstrProfCorrelator *Correlator)
: DataBuffer(std::move(DataBuffer)),
Correlator(dyn_cast_or_null<const InstrProfCorrelatorImpl<IntPtrT>>(
Correlator)) {}
RawInstrProfReader(const RawInstrProfReader &) = delete;
RawInstrProfReader &operator=(const RawInstrProfReader &) = delete;
static bool hasFormat(const MemoryBuffer &DataBuffer);
Error readHeader() override;
Error readNextRecord(NamedInstrProfRecord &Record) override;
Error printBinaryIds(raw_ostream &OS) override;
bool isIRLevelProfile() const override {
return (Version & VARIANT_MASK_IR_PROF) != 0;
}
bool hasCSIRLevelProfile() const override {
return (Version & VARIANT_MASK_CSIR_PROF) != 0;
}
bool instrEntryBBEnabled() const override {
return (Version & VARIANT_MASK_INSTR_ENTRY) != 0;
}
bool useDebugInfoCorrelate() const override {
return (Version & VARIANT_MASK_DBG_CORRELATE) != 0;
}
bool hasSingleByteCoverage() const override {
return (Version & VARIANT_MASK_BYTE_COVERAGE) != 0;
}
bool functionEntryOnly() const override {
return (Version & VARIANT_MASK_FUNCTION_ENTRY_ONLY) != 0;
}
/// Returns a BitsetEnum describing the attributes of the raw instr profile.
InstrProfKind getProfileKind() const override;
InstrProfSymtab &getSymtab() override {
assert(Symtab.get());
return *Symtab.get();
}
private:
Error createSymtab(InstrProfSymtab &Symtab);
Error readNextHeader(const char *CurrentPos);
Error readHeader(const RawInstrProf::Header &Header);
template <class IntT> IntT swap(IntT Int) const {
return ShouldSwapBytes ? sys::getSwappedBytes(Int) : Int;
}
support::endianness getDataEndianness() const {
support::endianness HostEndian = getHostEndianness();
if (!ShouldSwapBytes)
return HostEndian;
if (HostEndian == support::little)
return support::big;
else
return support::little;
}
inline uint8_t getNumPaddingBytes(uint64_t SizeInBytes) {
return 7 & (sizeof(uint64_t) - SizeInBytes % sizeof(uint64_t));
}
Error readName(NamedInstrProfRecord &Record);
Error readFuncHash(NamedInstrProfRecord &Record);
Error readRawCounts(InstrProfRecord &Record);
Error readValueProfilingData(InstrProfRecord &Record);
bool atEnd() const { return Data == DataEnd; }
void advanceData() {
// `CountersDelta` is a constant zero when using debug info correlation.
if (!Correlator) {
// The initial CountersDelta is the in-memory address difference between
// the data and counts sections:
// start(__llvm_prf_cnts) - start(__llvm_prf_data)
// As we advance to the next record, we maintain the correct CountersDelta
// with respect to the next record.
CountersDelta -= sizeof(*Data);
}
Data++;
ValueDataStart += CurValueDataSize;
}
const char *getNextHeaderPos() const {
assert(atEnd());
return (const char *)ValueDataStart;
}
StringRef getName(uint64_t NameRef) const {
return Symtab->getFuncName(swap(NameRef));
}
int getCounterTypeSize() const {
return hasSingleByteCoverage() ? sizeof(uint8_t) : sizeof(uint64_t);
}
};
using RawInstrProfReader32 = RawInstrProfReader<uint32_t>;
using RawInstrProfReader64 = RawInstrProfReader<uint64_t>;
namespace IndexedInstrProf {
enum class HashT : uint32_t;
} // end namespace IndexedInstrProf
/// Trait for lookups into the on-disk hash table for the binary instrprof
/// format.
class InstrProfLookupTrait {
std::vector<NamedInstrProfRecord> DataBuffer;
IndexedInstrProf::HashT HashType;
unsigned FormatVersion;
// Endianness of the input value profile data.
// It should be LE by default, but can be changed
// for testing purpose.
support::endianness ValueProfDataEndianness = support::little;
public:
InstrProfLookupTrait(IndexedInstrProf::HashT HashType, unsigned FormatVersion)
: HashType(HashType), FormatVersion(FormatVersion) {}
using data_type = ArrayRef<NamedInstrProfRecord>;
using internal_key_type = StringRef;
using external_key_type = StringRef;
using hash_value_type = uint64_t;
using offset_type = uint64_t;
static bool EqualKey(StringRef A, StringRef B) { return A == B; }
static StringRef GetInternalKey(StringRef K) { return K; }
static StringRef GetExternalKey(StringRef K) { return K; }
hash_value_type ComputeHash(StringRef K);
static std::pair<offset_type, offset_type>
ReadKeyDataLength(const unsigned char *&D) {
using namespace support;
offset_type KeyLen = endian::readNext<offset_type, little, unaligned>(D);
offset_type DataLen = endian::readNext<offset_type, little, unaligned>(D);
return std::make_pair(KeyLen, DataLen);
}
StringRef ReadKey(const unsigned char *D, offset_type N) {
return StringRef((const char *)D, N);
}
bool readValueProfilingData(const unsigned char *&D,
const unsigned char *const End);
data_type ReadData(StringRef K, const unsigned char *D, offset_type N);
// Used for testing purpose only.
void setValueProfDataEndianness(support::endianness Endianness) {
ValueProfDataEndianness = Endianness;
}
};
struct InstrProfReaderIndexBase {
virtual ~InstrProfReaderIndexBase() = default;
// Read all the profile records with the same key pointed to the current
// iterator.
virtual Error getRecords(ArrayRef<NamedInstrProfRecord> &Data) = 0;
// Read all the profile records with the key equal to FuncName
virtual Error getRecords(StringRef FuncName,
ArrayRef<NamedInstrProfRecord> &Data) = 0;
virtual void advanceToNextKey() = 0;
virtual bool atEnd() const = 0;
virtual void setValueProfDataEndianness(support::endianness Endianness) = 0;
virtual uint64_t getVersion() const = 0;
virtual bool isIRLevelProfile() const = 0;
virtual bool hasCSIRLevelProfile() const = 0;
virtual bool instrEntryBBEnabled() const = 0;
virtual bool hasSingleByteCoverage() const = 0;
virtual bool functionEntryOnly() const = 0;
virtual InstrProfKind getProfileKind() const = 0;
virtual Error populateSymtab(InstrProfSymtab &) = 0;
};
using OnDiskHashTableImplV3 =
OnDiskIterableChainedHashTable<InstrProfLookupTrait>;
template <typename HashTableImpl>
class InstrProfReaderItaniumRemapper;
template <typename HashTableImpl>
class InstrProfReaderIndex : public InstrProfReaderIndexBase {
private:
std::unique_ptr<HashTableImpl> HashTable;
typename HashTableImpl::data_iterator RecordIterator;
uint64_t FormatVersion;
friend class InstrProfReaderItaniumRemapper<HashTableImpl>;
public:
InstrProfReaderIndex(const unsigned char *Buckets,
const unsigned char *const Payload,
const unsigned char *const Base,
IndexedInstrProf::HashT HashType, uint64_t Version);
~InstrProfReaderIndex() override = default;
Error getRecords(ArrayRef<NamedInstrProfRecord> &Data) override;
Error getRecords(StringRef FuncName,
ArrayRef<NamedInstrProfRecord> &Data) override;
void advanceToNextKey() override { RecordIterator++; }
bool atEnd() const override {
return RecordIterator == HashTable->data_end();
}
void setValueProfDataEndianness(support::endianness Endianness) override {
HashTable->getInfoObj().setValueProfDataEndianness(Endianness);
}
uint64_t getVersion() const override { return GET_VERSION(FormatVersion); }
bool isIRLevelProfile() const override {
return (FormatVersion & VARIANT_MASK_IR_PROF) != 0;
}
bool hasCSIRLevelProfile() const override {
return (FormatVersion & VARIANT_MASK_CSIR_PROF) != 0;
}
bool instrEntryBBEnabled() const override {
return (FormatVersion & VARIANT_MASK_INSTR_ENTRY) != 0;
}
bool hasSingleByteCoverage() const override {
return (FormatVersion & VARIANT_MASK_BYTE_COVERAGE) != 0;
}
bool functionEntryOnly() const override {
return (FormatVersion & VARIANT_MASK_FUNCTION_ENTRY_ONLY) != 0;
}
InstrProfKind getProfileKind() const override;
Error populateSymtab(InstrProfSymtab &Symtab) override {
return Symtab.create(HashTable->keys());
}
};
/// Name matcher supporting fuzzy matching of symbol names to names in profiles.
class InstrProfReaderRemapper {
public:
virtual ~InstrProfReaderRemapper() = default;
virtual Error populateRemappings() { return Error::success(); }
virtual Error getRecords(StringRef FuncName,
ArrayRef<NamedInstrProfRecord> &Data) = 0;
};
/// Reader for the indexed binary instrprof format.
class IndexedInstrProfReader : public InstrProfReader {
private:
/// The profile data file contents.
std::unique_ptr<MemoryBuffer> DataBuffer;
/// The profile remapping file contents.
std::unique_ptr<MemoryBuffer> RemappingBuffer;
/// The index into the profile data.
std::unique_ptr<InstrProfReaderIndexBase> Index;
/// The profile remapping file contents.
std::unique_ptr<InstrProfReaderRemapper> Remapper;
/// Profile summary data.
std::unique_ptr<ProfileSummary> Summary;
/// Context sensitive profile summary data.
std::unique_ptr<ProfileSummary> CS_Summary;
// Index to the current record in the record array.
unsigned RecordIndex;
// Read the profile summary. Return a pointer pointing to one byte past the
// end of the summary data if it exists or the input \c Cur.
// \c UseCS indicates whether to use the context-sensitive profile summary.
const unsigned char *readSummary(IndexedInstrProf::ProfVersion Version,
const unsigned char *Cur, bool UseCS);
public:
IndexedInstrProfReader(
std::unique_ptr<MemoryBuffer> DataBuffer,
std::unique_ptr<MemoryBuffer> RemappingBuffer = nullptr)
: DataBuffer(std::move(DataBuffer)),
RemappingBuffer(std::move(RemappingBuffer)), RecordIndex(0) {}
IndexedInstrProfReader(const IndexedInstrProfReader &) = delete;
IndexedInstrProfReader &operator=(const IndexedInstrProfReader &) = delete;
/// Return the profile version.
uint64_t getVersion() const { return Index->getVersion(); }
bool isIRLevelProfile() const override { return Index->isIRLevelProfile(); }
bool hasCSIRLevelProfile() const override {
return Index->hasCSIRLevelProfile();
}
bool instrEntryBBEnabled() const override {
return Index->instrEntryBBEnabled();
}
bool hasSingleByteCoverage() const override {
return Index->hasSingleByteCoverage();
}
bool functionEntryOnly() const override { return Index->functionEntryOnly(); }
/// Returns a BitsetEnum describing the attributes of the indexed instr
/// profile.
InstrProfKind getProfileKind() const override {
return Index->getProfileKind();
}
/// Return true if the given buffer is in an indexed instrprof format.
static bool hasFormat(const MemoryBuffer &DataBuffer);
/// Read the file header.
Error readHeader() override;
/// Read a single record.
Error readNextRecord(NamedInstrProfRecord &Record) override;
/// Return the NamedInstrProfRecord associated with FuncName and FuncHash
Expected<InstrProfRecord> getInstrProfRecord(StringRef FuncName,
uint64_t FuncHash);
/// Fill Counts with the profile data for the given function name.
Error getFunctionCounts(StringRef FuncName, uint64_t FuncHash,
std::vector<uint64_t> &Counts);
/// Return the maximum of all known function counts.
/// \c UseCS indicates whether to use the context-sensitive count.
uint64_t getMaximumFunctionCount(bool UseCS) {
if (UseCS) {
assert(CS_Summary && "No context sensitive profile summary");
return CS_Summary->getMaxFunctionCount();
} else {
assert(Summary && "No profile summary");
return Summary->getMaxFunctionCount();
}
}
/// Factory method to create an indexed reader.
static Expected<std::unique_ptr<IndexedInstrProfReader>>
create(const Twine &Path, const Twine &RemappingPath = "");
static Expected<std::unique_ptr<IndexedInstrProfReader>>
create(std::unique_ptr<MemoryBuffer> Buffer,
std::unique_ptr<MemoryBuffer> RemappingBuffer = nullptr);
// Used for testing purpose only.
void setValueProfDataEndianness(support::endianness Endianness) {
Index->setValueProfDataEndianness(Endianness);
}
// See description in the base class. This interface is designed
// to be used by llvm-profdata (for dumping). Avoid using this when
// the client is the compiler.
InstrProfSymtab &getSymtab() override;
/// Return the profile summary.
/// \c UseCS indicates whether to use the context-sensitive summary.
ProfileSummary &getSummary(bool UseCS) {
if (UseCS) {
assert(CS_Summary && "No context sensitive summary");
return *(CS_Summary.get());
} else {
assert(Summary && "No profile summary");
return *(Summary.get());
}
}
};
} // end namespace llvm
#endif // LLVM_PROFILEDATA_INSTRPROFREADER_H

View File

@@ -0,0 +1,134 @@
//===- InstrProfWriter.h - Instrumented profiling writer --------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains support for writing profiling data for instrumentation
// based PGO and coverage.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_PROFILEDATA_INSTRPROFWRITER_H
#define LLVM_PROFILEDATA_INSTRPROFWRITER_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MemoryBuffer.h"
#include <cstdint>
#include <memory>
namespace llvm {
/// Writer for instrumentation based profile data.
class InstrProfRecordWriterTrait;
class ProfOStream;
class raw_fd_ostream;
class InstrProfWriter {
public:
using ProfilingData = SmallDenseMap<uint64_t, InstrProfRecord>;
private:
bool Sparse;
StringMap<ProfilingData> FunctionData;
// An enum describing the attributes of the profile.
InstrProfKind ProfileKind = InstrProfKind::Unknown;
// Use raw pointer here for the incomplete type object.
InstrProfRecordWriterTrait *InfoObj;
public:
InstrProfWriter(bool Sparse = false);
~InstrProfWriter();
StringMap<ProfilingData> &getProfileData() { return FunctionData; }
/// Add function counts for the given function. If there are already counts
/// for this function and the hash and number of counts match, each counter is
/// summed. Optionally scale counts by \p Weight.
void addRecord(NamedInstrProfRecord &&I, uint64_t Weight,
function_ref<void(Error)> Warn);
void addRecord(NamedInstrProfRecord &&I, function_ref<void(Error)> Warn) {
addRecord(std::move(I), 1, Warn);
}
/// Merge existing function counts from the given writer.
void mergeRecordsFromWriter(InstrProfWriter &&IPW,
function_ref<void(Error)> Warn);
/// Write the profile to \c OS
Error write(raw_fd_ostream &OS);
/// Write the profile in text format to \c OS
Error writeText(raw_fd_ostream &OS);
Error validateRecord(const InstrProfRecord &Func);
/// Write \c Record in text format to \c OS
static void writeRecordInText(StringRef Name, uint64_t Hash,
const InstrProfRecord &Counters,
InstrProfSymtab &Symtab, raw_fd_ostream &OS);
/// Write the profile, returning the raw data. For testing.
std::unique_ptr<MemoryBuffer> writeBuffer();
/// Update the attributes of the current profile from the attributes
/// specified. An error is returned if IR and FE profiles are mixed.
Error mergeProfileKind(const InstrProfKind Other) {
// If the kind is unset, this is the first profile we are merging so just
// set it to the given type.
if (ProfileKind == InstrProfKind::Unknown) {
ProfileKind = Other;
return Error::success();
}
// Returns true if merging should fail assuming A and B are incompatible.
auto testIncompatible = [&](InstrProfKind A, InstrProfKind B) {
return (static_cast<bool>(ProfileKind & A) &&
static_cast<bool>(Other & B)) ||
(static_cast<bool>(ProfileKind & B) &&
static_cast<bool>(Other & A));
};
// Check if the profiles are in-compatible. Clang frontend profiles can't be
// merged with other profile types.
if (static_cast<bool>((ProfileKind & InstrProfKind::FE) ^
(Other & InstrProfKind::FE))) {
return make_error<InstrProfError>(instrprof_error::unsupported_version);
}
if (testIncompatible(InstrProfKind::FunctionEntryOnly, InstrProfKind::BB)) {
return make_error<InstrProfError>(
instrprof_error::unsupported_version,
"cannot merge FunctionEntryOnly profiles and BB profiles together");
}
// Now we update the profile type with the bits that are set.
ProfileKind |= Other;
return Error::success();
}
// Internal interface for testing purpose only.
void setValueProfDataEndianness(support::endianness Endianness);
void setOutputSparse(bool Sparse);
// Compute the overlap b/w this object and Other. Program level result is
// stored in Overlap and function level result is stored in FuncLevelOverlap.
void overlapRecord(NamedInstrProfRecord &&Other, OverlapStats &Overlap,
OverlapStats &FuncLevelOverlap,
const OverlapFuncFilters &FuncFilter);
private:
void addRecord(StringRef Name, uint64_t Hash, InstrProfRecord &&I,
uint64_t Weight, function_ref<void(Error)> Warn);
bool shouldEncodeData(const ProfilingData &PD);
Error writeImpl(ProfOStream &OS);
};
} // end namespace llvm
#endif // LLVM_PROFILEDATA_INSTRPROFWRITER_H

View File

@@ -0,0 +1,111 @@
//===- ProfileCommon.h - Common profiling APIs. -----------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains data structures and functions common to both instrumented
// and sample profiling.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_PROFILEDATA_PROFILECOMMON_H
#define LLVM_PROFILEDATA_PROFILECOMMON_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/IR/ProfileSummary.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/ProfileData/SampleProf.h"
#include "llvm/Support/Error.h"
#include <algorithm>
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
#include <vector>
namespace llvm {
namespace sampleprof {
class FunctionSamples;
} // end namespace sampleprof
inline const char *getHotSectionPrefix() { return "hot"; }
inline const char *getUnlikelySectionPrefix() { return "unlikely"; }
class ProfileSummaryBuilder {
private:
/// We keep track of the number of times a count (block count or samples)
/// appears in the profile. The map is kept sorted in the descending order of
/// counts.
std::map<uint64_t, uint32_t, std::greater<uint64_t>> CountFrequencies;
std::vector<uint32_t> DetailedSummaryCutoffs;
protected:
SummaryEntryVector DetailedSummary;
uint64_t TotalCount = 0;
uint64_t MaxCount = 0;
uint64_t MaxFunctionCount = 0;
uint32_t NumCounts = 0;
uint32_t NumFunctions = 0;
ProfileSummaryBuilder(std::vector<uint32_t> Cutoffs)
: DetailedSummaryCutoffs(std::move(Cutoffs)) {}
~ProfileSummaryBuilder() = default;
inline void addCount(uint64_t Count);
void computeDetailedSummary();
public:
/// A vector of useful cutoff values for detailed summary.
static const ArrayRef<uint32_t> DefaultCutoffs;
/// Find the summary entry for a desired percentile of counts.
static const ProfileSummaryEntry &
getEntryForPercentile(const SummaryEntryVector &DS, uint64_t Percentile);
static uint64_t getHotCountThreshold(const SummaryEntryVector &DS);
static uint64_t getColdCountThreshold(const SummaryEntryVector &DS);
};
class InstrProfSummaryBuilder final : public ProfileSummaryBuilder {
uint64_t MaxInternalBlockCount = 0;
inline void addEntryCount(uint64_t Count);
inline void addInternalCount(uint64_t Count);
public:
InstrProfSummaryBuilder(std::vector<uint32_t> Cutoffs)
: ProfileSummaryBuilder(std::move(Cutoffs)) {}
void addRecord(const InstrProfRecord &);
std::unique_ptr<ProfileSummary> getSummary();
};
class SampleProfileSummaryBuilder final : public ProfileSummaryBuilder {
public:
SampleProfileSummaryBuilder(std::vector<uint32_t> Cutoffs)
: ProfileSummaryBuilder(std::move(Cutoffs)) {}
void addRecord(const sampleprof::FunctionSamples &FS,
bool isCallsiteSample = false);
std::unique_ptr<ProfileSummary>
computeSummaryForProfiles(const sampleprof::SampleProfileMap &Profiles);
std::unique_ptr<ProfileSummary> getSummary();
};
/// This is called when a count is seen in the profile.
void ProfileSummaryBuilder::addCount(uint64_t Count) {
TotalCount += Count;
if (Count > MaxCount)
MaxCount = Count;
NumCounts++;
CountFrequencies[Count]++;
}
} // end namespace llvm
#endif // LLVM_PROFILEDATA_PROFILECOMMON_H

View File

@@ -0,0 +1,43 @@
#ifndef LLVM_PROFILEDATA_RAWMEMPROFREADER_H_
#define LLVM_PROFILEDATA_RAWMEMPROFREADER_H_
//===- MemProfReader.h - Instrumented memory profiling reader ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains support for reading MemProf profiling data.
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/Error.h"
#include "llvm/Support/MemoryBuffer.h"
namespace llvm {
namespace memprof {
class RawMemProfReader {
public:
RawMemProfReader(std::unique_ptr<MemoryBuffer> DataBuffer)
: DataBuffer(std::move(DataBuffer)) {}
// Prints aggregate counts for each raw profile parsed from the DataBuffer.
void printSummaries(raw_ostream &OS) const;
// Return true if the \p DataBuffer starts with magic bytes indicating it is
// a raw binary memprof profile.
static bool hasFormat(const MemoryBuffer &DataBuffer);
// Create a RawMemProfReader after sanity checking the contents of the file at
// \p Path.
static Expected<std::unique_ptr<RawMemProfReader>> create(const Twine &Path);
private:
std::unique_ptr<MemoryBuffer> DataBuffer;
};
} // namespace memprof
} // namespace llvm
#endif // LLVM_PROFILEDATA_RAWMEMPROFREADER_H_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,903 @@
//===- SampleProfReader.h - Read LLVM sample profile data -------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains definitions needed for reading sample profiles.
//
// NOTE: If you are making changes to this file format, please remember
// to document them in the Clang documentation at
// tools/clang/docs/UsersManual.rst.
//
// Text format
// -----------
//
// Sample profiles are written as ASCII text. The file is divided into
// sections, which correspond to each of the functions executed at runtime.
// Each section has the following format
//
// function1:total_samples:total_head_samples
// offset1[.discriminator]: number_of_samples [fn1:num fn2:num ... ]
// offset2[.discriminator]: number_of_samples [fn3:num fn4:num ... ]
// ...
// offsetN[.discriminator]: number_of_samples [fn5:num fn6:num ... ]
// offsetA[.discriminator]: fnA:num_of_total_samples
// offsetA1[.discriminator]: number_of_samples [fn7:num fn8:num ... ]
// ...
// !CFGChecksum: num
// !Attribute: flags
//
// This is a nested tree in which the indentation represents the nesting level
// of the inline stack. There are no blank lines in the file. And the spacing
// within a single line is fixed. Additional spaces will result in an error
// while reading the file.
//
// Any line starting with the '#' character is completely ignored.
//
// Inlined calls are represented with indentation. The Inline stack is a
// stack of source locations in which the top of the stack represents the
// leaf function, and the bottom of the stack represents the actual
// symbol to which the instruction belongs.
//
// Function names must be mangled in order for the profile loader to
// match them in the current translation unit. The two numbers in the
// function header specify how many total samples were accumulated in the
// function (first number), and the total number of samples accumulated
// in the prologue of the function (second number). This head sample
// count provides an indicator of how frequently the function is invoked.
//
// There are three types of lines in the function body.
//
// * Sampled line represents the profile information of a source location.
// * Callsite line represents the profile information of a callsite.
// * Metadata line represents extra metadata of the function.
//
// Each sampled line may contain several items. Some are optional (marked
// below):
//
// a. Source line offset. This number represents the line number
// in the function where the sample was collected. The line number is
// always relative to the line where symbol of the function is
// defined. So, if the function has its header at line 280, the offset
// 13 is at line 293 in the file.
//
// Note that this offset should never be a negative number. This could
// happen in cases like macros. The debug machinery will register the
// line number at the point of macro expansion. So, if the macro was
// expanded in a line before the start of the function, the profile
// converter should emit a 0 as the offset (this means that the optimizers
// will not be able to associate a meaningful weight to the instructions
// in the macro).
//
// b. [OPTIONAL] Discriminator. This is used if the sampled program
// was compiled with DWARF discriminator support
// (http://wiki.dwarfstd.org/index.php?title=Path_Discriminators).
// DWARF discriminators are unsigned integer values that allow the
// compiler to distinguish between multiple execution paths on the
// same source line location.
//
// For example, consider the line of code ``if (cond) foo(); else bar();``.
// If the predicate ``cond`` is true 80% of the time, then the edge
// into function ``foo`` should be considered to be taken most of the
// time. But both calls to ``foo`` and ``bar`` are at the same source
// line, so a sample count at that line is not sufficient. The
// compiler needs to know which part of that line is taken more
// frequently.
//
// This is what discriminators provide. In this case, the calls to
// ``foo`` and ``bar`` will be at the same line, but will have
// different discriminator values. This allows the compiler to correctly
// set edge weights into ``foo`` and ``bar``.
//
// c. Number of samples. This is an integer quantity representing the
// number of samples collected by the profiler at this source
// location.
//
// d. [OPTIONAL] Potential call targets and samples. If present, this
// line contains a call instruction. This models both direct and
// number of samples. For example,
//
// 130: 7 foo:3 bar:2 baz:7
//
// The above means that at relative line offset 130 there is a call
// instruction that calls one of ``foo()``, ``bar()`` and ``baz()``,
// with ``baz()`` being the relatively more frequently called target.
//
// Each callsite line may contain several items. Some are optional.
//
// a. Source line offset. This number represents the line number of the
// callsite that is inlined in the profiled binary.
//
// b. [OPTIONAL] Discriminator. Same as the discriminator for sampled line.
//
// c. Number of samples. This is an integer quantity representing the
// total number of samples collected for the inlined instance at this
// callsite
//
// Metadata line can occur in lines with one indent only, containing extra
// information for the top-level function. Furthermore, metadata can only
// occur after all the body samples and callsite samples.
// Each metadata line may contain a particular type of metadata, marked by
// the starting characters annotated with !. We process each metadata line
// independently, hence each metadata line has to form an independent piece
// of information that does not require cross-line reference.
// We support the following types of metadata:
//
// a. CFG Checksum (a.k.a. function hash):
// !CFGChecksum: 12345
// b. CFG Checksum (see ContextAttributeMask):
// !Attribute: 1
//
//
// Binary format
// -------------
//
// This is a more compact encoding. Numbers are encoded as ULEB128 values
// and all strings are encoded in a name table. The file is organized in
// the following sections:
//
// MAGIC (uint64_t)
// File identifier computed by function SPMagic() (0x5350524f463432ff)
//
// VERSION (uint32_t)
// File format version number computed by SPVersion()
//
// SUMMARY
// TOTAL_COUNT (uint64_t)
// Total number of samples in the profile.
// MAX_COUNT (uint64_t)
// Maximum value of samples on a line.
// MAX_FUNCTION_COUNT (uint64_t)
// Maximum number of samples at function entry (head samples).
// NUM_COUNTS (uint64_t)
// Number of lines with samples.
// NUM_FUNCTIONS (uint64_t)
// Number of functions with samples.
// NUM_DETAILED_SUMMARY_ENTRIES (size_t)
// Number of entries in detailed summary
// DETAILED_SUMMARY
// A list of detailed summary entry. Each entry consists of
// CUTOFF (uint32_t)
// Required percentile of total sample count expressed as a fraction
// multiplied by 1000000.
// MIN_COUNT (uint64_t)
// The minimum number of samples required to reach the target
// CUTOFF.
// NUM_COUNTS (uint64_t)
// Number of samples to get to the desired percentile.
//
// NAME TABLE
// SIZE (uint32_t)
// Number of entries in the name table.
// NAMES
// A NUL-separated list of SIZE strings.
//
// FUNCTION BODY (one for each uninlined function body present in the profile)
// HEAD_SAMPLES (uint64_t) [only for top-level functions]
// Total number of samples collected at the head (prologue) of the
// function.
// NOTE: This field should only be present for top-level functions
// (i.e., not inlined into any caller). Inlined function calls
// have no prologue, so they don't need this.
// NAME_IDX (uint32_t)
// Index into the name table indicating the function name.
// SAMPLES (uint64_t)
// Total number of samples collected in this function.
// NRECS (uint32_t)
// Total number of sampling records this function's profile.
// BODY RECORDS
// A list of NRECS entries. Each entry contains:
// OFFSET (uint32_t)
// Line offset from the start of the function.
// DISCRIMINATOR (uint32_t)
// Discriminator value (see description of discriminators
// in the text format documentation above).
// SAMPLES (uint64_t)
// Number of samples collected at this location.
// NUM_CALLS (uint32_t)
// Number of non-inlined function calls made at this location. In the
// case of direct calls, this number will always be 1. For indirect
// calls (virtual functions and function pointers) this will
// represent all the actual functions called at runtime.
// CALL_TARGETS
// A list of NUM_CALLS entries for each called function:
// NAME_IDX (uint32_t)
// Index into the name table with the callee name.
// SAMPLES (uint64_t)
// Number of samples collected at the call site.
// NUM_INLINED_FUNCTIONS (uint32_t)
// Number of callees inlined into this function.
// INLINED FUNCTION RECORDS
// A list of NUM_INLINED_FUNCTIONS entries describing each of the inlined
// callees.
// OFFSET (uint32_t)
// Line offset from the start of the function.
// DISCRIMINATOR (uint32_t)
// Discriminator value (see description of discriminators
// in the text format documentation above).
// FUNCTION BODY
// A FUNCTION BODY entry describing the inlined function.
//===----------------------------------------------------------------------===//
#ifndef LLVM_PROFILEDATA_SAMPLEPROFREADER_H
#define LLVM_PROFILEDATA_SAMPLEPROFREADER_H
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/ProfileSummary.h"
#include "llvm/ProfileData/GCOV.h"
#include "llvm/ProfileData/SampleProf.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Discriminator.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SymbolRemappingReader.h"
#include <algorithm>
#include <cstdint>
#include <list>
#include <memory>
#include <string>
#include <system_error>
#include <unordered_set>
#include <vector>
namespace llvm {
class raw_ostream;
class Twine;
namespace sampleprof {
class SampleProfileReader;
/// SampleProfileReaderItaniumRemapper remaps the profile data from a
/// sample profile data reader, by applying a provided set of equivalences
/// between components of the symbol names in the profile.
class SampleProfileReaderItaniumRemapper {
public:
SampleProfileReaderItaniumRemapper(std::unique_ptr<MemoryBuffer> B,
std::unique_ptr<SymbolRemappingReader> SRR,
SampleProfileReader &R)
: Buffer(std::move(B)), Remappings(std::move(SRR)), Reader(R) {
assert(Remappings && "Remappings cannot be nullptr");
}
/// Create a remapper from the given remapping file. The remapper will
/// be used for profile read in by Reader.
static ErrorOr<std::unique_ptr<SampleProfileReaderItaniumRemapper>>
create(const std::string Filename, SampleProfileReader &Reader,
LLVMContext &C);
/// Create a remapper from the given Buffer. The remapper will
/// be used for profile read in by Reader.
static ErrorOr<std::unique_ptr<SampleProfileReaderItaniumRemapper>>
create(std::unique_ptr<MemoryBuffer> &B, SampleProfileReader &Reader,
LLVMContext &C);
/// Apply remappings to the profile read by Reader.
void applyRemapping(LLVMContext &Ctx);
bool hasApplied() { return RemappingApplied; }
/// Insert function name into remapper.
void insert(StringRef FunctionName) { Remappings->insert(FunctionName); }
/// Query whether there is equivalent in the remapper which has been
/// inserted.
bool exist(StringRef FunctionName) {
return Remappings->lookup(FunctionName);
}
/// Return the equivalent name in the profile for \p FunctionName if
/// it exists.
Optional<StringRef> lookUpNameInProfile(StringRef FunctionName);
private:
// The buffer holding the content read from remapping file.
std::unique_ptr<MemoryBuffer> Buffer;
std::unique_ptr<SymbolRemappingReader> Remappings;
// Map remapping key to the name in the profile. By looking up the
// key in the remapper, a given new name can be mapped to the
// canonical name using the NameMap.
DenseMap<SymbolRemappingReader::Key, StringRef> NameMap;
// The Reader the remapper is servicing.
SampleProfileReader &Reader;
// Indicate whether remapping has been applied to the profile read
// by Reader -- by calling applyRemapping.
bool RemappingApplied = false;
};
/// Sample-based profile reader.
///
/// Each profile contains sample counts for all the functions
/// executed. Inside each function, statements are annotated with the
/// collected samples on all the instructions associated with that
/// statement.
///
/// For this to produce meaningful data, the program needs to be
/// compiled with some debug information (at minimum, line numbers:
/// -gline-tables-only). Otherwise, it will be impossible to match IR
/// instructions to the line numbers collected by the profiler.
///
/// From the profile file, we are interested in collecting the
/// following information:
///
/// * A list of functions included in the profile (mangled names).
///
/// * For each function F:
/// 1. The total number of samples collected in F.
///
/// 2. The samples collected at each line in F. To provide some
/// protection against source code shuffling, line numbers should
/// be relative to the start of the function.
///
/// The reader supports two file formats: text and binary. The text format
/// is useful for debugging and testing, while the binary format is more
/// compact and I/O efficient. They can both be used interchangeably.
class SampleProfileReader {
public:
SampleProfileReader(std::unique_ptr<MemoryBuffer> B, LLVMContext &C,
SampleProfileFormat Format = SPF_None)
: Profiles(0), Ctx(C), Buffer(std::move(B)), Format(Format) {}
virtual ~SampleProfileReader() = default;
/// Read and validate the file header.
virtual std::error_code readHeader() = 0;
/// Set the bits for FS discriminators. Parameter Pass specify the sequence
/// number, Pass == i is for the i-th round of adding FS discriminators.
/// Pass == 0 is for using base discriminators.
void setDiscriminatorMaskedBitFrom(FSDiscriminatorPass P) {
MaskedBitFrom = getFSPassBitEnd(P);
}
/// Get the bitmask the discriminators: For FS profiles, return the bit
/// mask for this pass. For non FS profiles, return (unsigned) -1.
uint32_t getDiscriminatorMask() const {
if (!ProfileIsFS)
return 0xFFFFFFFF;
assert((MaskedBitFrom != 0) && "MaskedBitFrom is not set properly");
return getN1Bits(MaskedBitFrom);
}
/// The interface to read sample profiles from the associated file.
std::error_code read() {
if (std::error_code EC = readImpl())
return EC;
if (Remapper)
Remapper->applyRemapping(Ctx);
FunctionSamples::UseMD5 = useMD5();
return sampleprof_error::success;
}
/// The implementation to read sample profiles from the associated file.
virtual std::error_code readImpl() = 0;
/// Print the profile for \p FContext on stream \p OS.
void dumpFunctionProfile(SampleContext FContext, raw_ostream &OS = dbgs());
/// Collect functions with definitions in Module M. For reader which
/// support loading function profiles on demand, return true when the
/// reader has been given a module. Always return false for reader
/// which doesn't support loading function profiles on demand.
virtual bool collectFuncsFromModule() { return false; }
/// Print all the profiles on stream \p OS.
void dump(raw_ostream &OS = dbgs());
/// Return the samples collected for function \p F.
FunctionSamples *getSamplesFor(const Function &F) {
// The function name may have been updated by adding suffix. Call
// a helper to (optionally) strip off suffixes so that we can
// match against the original function name in the profile.
StringRef CanonName = FunctionSamples::getCanonicalFnName(F);
return getSamplesFor(CanonName);
}
/// Return the samples collected for function \p F, create empty
/// FunctionSamples if it doesn't exist.
FunctionSamples *getOrCreateSamplesFor(const Function &F) {
std::string FGUID;
StringRef CanonName = FunctionSamples::getCanonicalFnName(F);
CanonName = getRepInFormat(CanonName, useMD5(), FGUID);
auto It = Profiles.find(CanonName);
if (It != Profiles.end())
return &It->second;
if (!FGUID.empty()) {
assert(useMD5() && "New name should only be generated for md5 profile");
CanonName = *MD5NameBuffer.insert(FGUID).first;
}
return &Profiles[CanonName];
}
/// Return the samples collected for function \p F.
virtual FunctionSamples *getSamplesFor(StringRef Fname) {
std::string FGUID;
Fname = getRepInFormat(Fname, useMD5(), FGUID);
auto It = Profiles.find(Fname);
if (It != Profiles.end())
return &It->second;
if (Remapper) {
if (auto NameInProfile = Remapper->lookUpNameInProfile(Fname)) {
auto It = Profiles.find(*NameInProfile);
if (It != Profiles.end())
return &It->second;
}
}
return nullptr;
}
/// Return all the profiles.
SampleProfileMap &getProfiles() { return Profiles; }
/// Report a parse error message.
void reportError(int64_t LineNumber, const Twine &Msg) const {
Ctx.diagnose(DiagnosticInfoSampleProfile(Buffer->getBufferIdentifier(),
LineNumber, Msg));
}
/// Create a sample profile reader appropriate to the file format.
/// Create a remapper underlying if RemapFilename is not empty.
/// Parameter P specifies the FSDiscriminatorPass.
static ErrorOr<std::unique_ptr<SampleProfileReader>>
create(const std::string Filename, LLVMContext &C,
FSDiscriminatorPass P = FSDiscriminatorPass::Base,
const std::string RemapFilename = "");
/// Create a sample profile reader from the supplied memory buffer.
/// Create a remapper underlying if RemapFilename is not empty.
/// Parameter P specifies the FSDiscriminatorPass.
static ErrorOr<std::unique_ptr<SampleProfileReader>>
create(std::unique_ptr<MemoryBuffer> &B, LLVMContext &C,
FSDiscriminatorPass P = FSDiscriminatorPass::Base,
const std::string RemapFilename = "");
/// Return the profile summary.
ProfileSummary &getSummary() const { return *(Summary.get()); }
MemoryBuffer *getBuffer() const { return Buffer.get(); }
/// \brief Return the profile format.
SampleProfileFormat getFormat() const { return Format; }
/// Whether input profile is based on pseudo probes.
bool profileIsProbeBased() const { return ProfileIsProbeBased; }
/// Whether input profile is fully context-sensitive and flat.
bool profileIsCSFlat() const { return ProfileIsCSFlat; }
/// Whether input profile is fully context-sensitive and nested.
bool profileIsCSNested() const { return ProfileIsCSNested; }
virtual std::unique_ptr<ProfileSymbolList> getProfileSymbolList() {
return nullptr;
};
/// It includes all the names that have samples either in outline instance
/// or inline instance.
virtual std::vector<StringRef> *getNameTable() { return nullptr; }
virtual bool dumpSectionInfo(raw_ostream &OS = dbgs()) { return false; };
/// Return whether names in the profile are all MD5 numbers.
virtual bool useMD5() { return false; }
/// Don't read profile without context if the flag is set. This is only meaningful
/// for ExtBinary format.
virtual void setSkipFlatProf(bool Skip) {}
/// Return whether any name in the profile contains ".__uniq." suffix.
virtual bool hasUniqSuffix() { return false; }
SampleProfileReaderItaniumRemapper *getRemapper() { return Remapper.get(); }
void setModule(const Module *Mod) { M = Mod; }
protected:
/// Map every function to its associated profile.
///
/// The profile of every function executed at runtime is collected
/// in the structure FunctionSamples. This maps function objects
/// to their corresponding profiles.
SampleProfileMap Profiles;
/// LLVM context used to emit diagnostics.
LLVMContext &Ctx;
/// Memory buffer holding the profile file.
std::unique_ptr<MemoryBuffer> Buffer;
/// Extra name buffer holding names created on demand.
/// This should only be needed for md5 profiles.
std::unordered_set<std::string> MD5NameBuffer;
/// Profile summary information.
std::unique_ptr<ProfileSummary> Summary;
/// Take ownership of the summary of this reader.
static std::unique_ptr<ProfileSummary>
takeSummary(SampleProfileReader &Reader) {
return std::move(Reader.Summary);
}
/// Compute summary for this profile.
void computeSummary();
std::unique_ptr<SampleProfileReaderItaniumRemapper> Remapper;
/// \brief Whether samples are collected based on pseudo probes.
bool ProfileIsProbeBased = false;
/// Whether function profiles are context-sensitive flat profiles.
bool ProfileIsCSFlat = false;
/// Whether function profiles are context-sensitive nested profiles.
bool ProfileIsCSNested = false;
/// Number of context-sensitive profiles.
uint32_t CSProfileCount = 0;
/// Whether the function profiles use FS discriminators.
bool ProfileIsFS = false;
/// \brief The format of sample.
SampleProfileFormat Format = SPF_None;
/// \brief The current module being compiled if SampleProfileReader
/// is used by compiler. If SampleProfileReader is used by other
/// tools which are not compiler, M is usually nullptr.
const Module *M = nullptr;
/// Zero out the discriminator bits higher than bit MaskedBitFrom (0 based).
/// The default is to keep all the bits.
uint32_t MaskedBitFrom = 31;
};
class SampleProfileReaderText : public SampleProfileReader {
public:
SampleProfileReaderText(std::unique_ptr<MemoryBuffer> B, LLVMContext &C)
: SampleProfileReader(std::move(B), C, SPF_Text) {}
/// Read and validate the file header.
std::error_code readHeader() override { return sampleprof_error::success; }
/// Read sample profiles from the associated file.
std::error_code readImpl() override;
/// Return true if \p Buffer is in the format supported by this class.
static bool hasFormat(const MemoryBuffer &Buffer);
private:
/// CSNameTable is used to save full context vectors. This serves as an
/// underlying immutable buffer for all clients.
std::list<SampleContextFrameVector> CSNameTable;
};
class SampleProfileReaderBinary : public SampleProfileReader {
public:
SampleProfileReaderBinary(std::unique_ptr<MemoryBuffer> B, LLVMContext &C,
SampleProfileFormat Format = SPF_None)
: SampleProfileReader(std::move(B), C, Format) {}
/// Read and validate the file header.
virtual std::error_code readHeader() override;
/// Read sample profiles from the associated file.
std::error_code readImpl() override;
/// It includes all the names that have samples either in outline instance
/// or inline instance.
virtual std::vector<StringRef> *getNameTable() override { return &NameTable; }
protected:
/// Read a numeric value of type T from the profile.
///
/// If an error occurs during decoding, a diagnostic message is emitted and
/// EC is set.
///
/// \returns the read value.
template <typename T> ErrorOr<T> readNumber();
/// Read a numeric value of type T from the profile. The value is saved
/// without encoded.
template <typename T> ErrorOr<T> readUnencodedNumber();
/// Read a string from the profile.
///
/// If an error occurs during decoding, a diagnostic message is emitted and
/// EC is set.
///
/// \returns the read value.
ErrorOr<StringRef> readString();
/// Read the string index and check whether it overflows the table.
template <typename T> inline ErrorOr<uint32_t> readStringIndex(T &Table);
/// Return true if we've reached the end of file.
bool at_eof() const { return Data >= End; }
/// Read the next function profile instance.
std::error_code readFuncProfile(const uint8_t *Start);
/// Read the contents of the given profile instance.
std::error_code readProfile(FunctionSamples &FProfile);
/// Read the contents of Magic number and Version number.
std::error_code readMagicIdent();
/// Read profile summary.
std::error_code readSummary();
/// Read the whole name table.
virtual std::error_code readNameTable();
/// Points to the current location in the buffer.
const uint8_t *Data = nullptr;
/// Points to the end of the buffer.
const uint8_t *End = nullptr;
/// Function name table.
std::vector<StringRef> NameTable;
/// Read a string indirectly via the name table.
virtual ErrorOr<StringRef> readStringFromTable();
virtual ErrorOr<SampleContext> readSampleContextFromTable();
private:
std::error_code readSummaryEntry(std::vector<ProfileSummaryEntry> &Entries);
virtual std::error_code verifySPMagic(uint64_t Magic) = 0;
};
class SampleProfileReaderRawBinary : public SampleProfileReaderBinary {
private:
virtual std::error_code verifySPMagic(uint64_t Magic) override;
public:
SampleProfileReaderRawBinary(std::unique_ptr<MemoryBuffer> B, LLVMContext &C,
SampleProfileFormat Format = SPF_Binary)
: SampleProfileReaderBinary(std::move(B), C, Format) {}
/// \brief Return true if \p Buffer is in the format supported by this class.
static bool hasFormat(const MemoryBuffer &Buffer);
};
/// SampleProfileReaderExtBinaryBase/SampleProfileWriterExtBinaryBase defines
/// the basic structure of the extensible binary format.
/// The format is organized in sections except the magic and version number
/// at the beginning. There is a section table before all the sections, and
/// each entry in the table describes the entry type, start, size and
/// attributes. The format in each section is defined by the section itself.
///
/// It is easy to add a new section while maintaining the backward
/// compatibility of the profile. Nothing extra needs to be done. If we want
/// to extend an existing section, like add cache misses information in
/// addition to the sample count in the profile body, we can add a new section
/// with the extension and retire the existing section, and we could choose
/// to keep the parser of the old section if we want the reader to be able
/// to read both new and old format profile.
///
/// SampleProfileReaderExtBinary/SampleProfileWriterExtBinary define the
/// commonly used sections of a profile in extensible binary format. It is
/// possible to define other types of profile inherited from
/// SampleProfileReaderExtBinaryBase/SampleProfileWriterExtBinaryBase.
class SampleProfileReaderExtBinaryBase : public SampleProfileReaderBinary {
private:
std::error_code decompressSection(const uint8_t *SecStart,
const uint64_t SecSize,
const uint8_t *&DecompressBuf,
uint64_t &DecompressBufSize);
BumpPtrAllocator Allocator;
protected:
std::vector<SecHdrTableEntry> SecHdrTable;
std::error_code readSecHdrTableEntry(uint32_t Idx);
std::error_code readSecHdrTable();
std::error_code readFuncMetadata(bool ProfileHasAttribute);
std::error_code readFuncMetadata(bool ProfileHasAttribute,
FunctionSamples *FProfile);
std::error_code readFuncOffsetTable();
std::error_code readFuncProfiles();
std::error_code readMD5NameTable();
std::error_code readNameTableSec(bool IsMD5);
std::error_code readCSNameTableSec();
std::error_code readProfileSymbolList();
virtual std::error_code readHeader() override;
virtual std::error_code verifySPMagic(uint64_t Magic) override = 0;
virtual std::error_code readOneSection(const uint8_t *Start, uint64_t Size,
const SecHdrTableEntry &Entry);
// placeholder for subclasses to dispatch their own section readers.
virtual std::error_code readCustomSection(const SecHdrTableEntry &Entry) = 0;
virtual ErrorOr<StringRef> readStringFromTable() override;
virtual ErrorOr<SampleContext> readSampleContextFromTable() override;
ErrorOr<SampleContextFrames> readContextFromTable();
std::unique_ptr<ProfileSymbolList> ProfSymList;
/// The table mapping from function context to the offset of its
/// FunctionSample towards file start.
DenseMap<SampleContext, uint64_t> FuncOffsetTable;
/// Function offset mapping ordered by contexts.
std::unique_ptr<std::vector<std::pair<SampleContext, uint64_t>>>
OrderedFuncOffsets;
/// The set containing the functions to use when compiling a module.
DenseSet<StringRef> FuncsToUse;
/// Use fixed length MD5 instead of ULEB128 encoding so NameTable doesn't
/// need to be read in up front and can be directly accessed using index.
bool FixedLengthMD5 = false;
/// The starting address of NameTable containing fixed length MD5.
const uint8_t *MD5NameMemStart = nullptr;
/// If MD5 is used in NameTable section, the section saves uint64_t data.
/// The uint64_t data has to be converted to a string and then the string
/// will be used to initialize StringRef in NameTable.
/// Note NameTable contains StringRef so it needs another buffer to own
/// the string data. MD5StringBuf serves as the string buffer that is
/// referenced by NameTable (vector of StringRef). We make sure
/// the lifetime of MD5StringBuf is not shorter than that of NameTable.
std::unique_ptr<std::vector<std::string>> MD5StringBuf;
/// CSNameTable is used to save full context vectors. This serves as an
/// underlying immutable buffer for all clients.
std::unique_ptr<const std::vector<SampleContextFrameVector>> CSNameTable;
/// If SkipFlatProf is true, skip the sections with
/// SecFlagFlat flag.
bool SkipFlatProf = false;
bool FuncOffsetsOrdered = false;
public:
SampleProfileReaderExtBinaryBase(std::unique_ptr<MemoryBuffer> B,
LLVMContext &C, SampleProfileFormat Format)
: SampleProfileReaderBinary(std::move(B), C, Format) {}
/// Read sample profiles in extensible format from the associated file.
std::error_code readImpl() override;
/// Get the total size of all \p Type sections.
uint64_t getSectionSize(SecType Type);
/// Get the total size of header and all sections.
uint64_t getFileSize();
virtual bool dumpSectionInfo(raw_ostream &OS = dbgs()) override;
/// Collect functions with definitions in Module M. Return true if
/// the reader has been given a module.
bool collectFuncsFromModule() override;
/// Return whether names in the profile are all MD5 numbers.
virtual bool useMD5() override { return MD5StringBuf.get(); }
virtual std::unique_ptr<ProfileSymbolList> getProfileSymbolList() override {
return std::move(ProfSymList);
};
virtual void setSkipFlatProf(bool Skip) override { SkipFlatProf = Skip; }
};
class SampleProfileReaderExtBinary : public SampleProfileReaderExtBinaryBase {
private:
virtual std::error_code verifySPMagic(uint64_t Magic) override;
virtual std::error_code
readCustomSection(const SecHdrTableEntry &Entry) override {
// Update the data reader pointer to the end of the section.
Data = End;
return sampleprof_error::success;
};
public:
SampleProfileReaderExtBinary(std::unique_ptr<MemoryBuffer> B, LLVMContext &C,
SampleProfileFormat Format = SPF_Ext_Binary)
: SampleProfileReaderExtBinaryBase(std::move(B), C, Format) {}
/// \brief Return true if \p Buffer is in the format supported by this class.
static bool hasFormat(const MemoryBuffer &Buffer);
};
class SampleProfileReaderCompactBinary : public SampleProfileReaderBinary {
private:
/// Function name table.
std::vector<std::string> NameTable;
/// The table mapping from function name to the offset of its FunctionSample
/// towards file start.
DenseMap<StringRef, uint64_t> FuncOffsetTable;
/// The set containing the functions to use when compiling a module.
DenseSet<StringRef> FuncsToUse;
virtual std::error_code verifySPMagic(uint64_t Magic) override;
virtual std::error_code readNameTable() override;
/// Read a string indirectly via the name table.
virtual ErrorOr<StringRef> readStringFromTable() override;
virtual std::error_code readHeader() override;
std::error_code readFuncOffsetTable();
public:
SampleProfileReaderCompactBinary(std::unique_ptr<MemoryBuffer> B,
LLVMContext &C)
: SampleProfileReaderBinary(std::move(B), C, SPF_Compact_Binary) {}
/// \brief Return true if \p Buffer is in the format supported by this class.
static bool hasFormat(const MemoryBuffer &Buffer);
/// Read samples only for functions to use.
std::error_code readImpl() override;
/// Collect functions with definitions in Module M. Return true if
/// the reader has been given a module.
bool collectFuncsFromModule() override;
/// Return whether names in the profile are all MD5 numbers.
virtual bool useMD5() override { return true; }
};
using InlineCallStack = SmallVector<FunctionSamples *, 10>;
// Supported histogram types in GCC. Currently, we only need support for
// call target histograms.
enum HistType {
HIST_TYPE_INTERVAL,
HIST_TYPE_POW2,
HIST_TYPE_SINGLE_VALUE,
HIST_TYPE_CONST_DELTA,
HIST_TYPE_INDIR_CALL,
HIST_TYPE_AVERAGE,
HIST_TYPE_IOR,
HIST_TYPE_INDIR_CALL_TOPN
};
class SampleProfileReaderGCC : public SampleProfileReader {
public:
SampleProfileReaderGCC(std::unique_ptr<MemoryBuffer> B, LLVMContext &C)
: SampleProfileReader(std::move(B), C, SPF_GCC),
GcovBuffer(Buffer.get()) {}
/// Read and validate the file header.
std::error_code readHeader() override;
/// Read sample profiles from the associated file.
std::error_code readImpl() override;
/// Return true if \p Buffer is in the format supported by this class.
static bool hasFormat(const MemoryBuffer &Buffer);
protected:
std::error_code readNameTable();
std::error_code readOneFunctionProfile(const InlineCallStack &InlineStack,
bool Update, uint32_t Offset);
std::error_code readFunctionProfiles();
std::error_code skipNextWord();
template <typename T> ErrorOr<T> readNumber();
ErrorOr<StringRef> readString();
/// Read the section tag and check that it's the same as \p Expected.
std::error_code readSectionTag(uint32_t Expected);
/// GCOV buffer containing the profile.
GCOVBuffer GcovBuffer;
/// Function names in this profile.
std::vector<std::string> Names;
/// GCOV tags used to separate sections in the profile file.
static const uint32_t GCOVTagAFDOFileNames = 0xaa000000;
static const uint32_t GCOVTagAFDOFunction = 0xac000000;
};
} // end namespace sampleprof
} // end namespace llvm
#endif // LLVM_PROFILEDATA_SAMPLEPROFREADER_H

View File

@@ -0,0 +1,407 @@
//===- SampleProfWriter.h - Write LLVM sample profile data ------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains definitions needed for writing sample profiles.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_PROFILEDATA_SAMPLEPROFWRITER_H
#define LLVM_PROFILEDATA_SAMPLEPROFWRITER_H
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/IR/ProfileSummary.h"
#include "llvm/ProfileData/SampleProf.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstdint>
#include <memory>
#include <set>
#include <system_error>
#include <unordered_set>
namespace llvm {
namespace sampleprof {
enum SectionLayout {
DefaultLayout,
// The layout splits profile with context information from profile without
// context information. When Thinlto is enabled, ThinLTO postlink phase only
// has to load profile with context information and can skip the other part.
CtxSplitLayout,
NumOfLayout,
};
/// Sample-based profile writer. Base class.
class SampleProfileWriter {
public:
virtual ~SampleProfileWriter() = default;
/// Write sample profiles in \p S.
///
/// \returns status code of the file update operation.
virtual std::error_code writeSample(const FunctionSamples &S) = 0;
/// Write all the sample profiles in the given map of samples.
///
/// \returns status code of the file update operation.
virtual std::error_code write(const SampleProfileMap &ProfileMap);
raw_ostream &getOutputStream() { return *OutputStream; }
/// Profile writer factory.
///
/// Create a new file writer based on the value of \p Format.
static ErrorOr<std::unique_ptr<SampleProfileWriter>>
create(StringRef Filename, SampleProfileFormat Format);
/// Create a new stream writer based on the value of \p Format.
/// For testing.
static ErrorOr<std::unique_ptr<SampleProfileWriter>>
create(std::unique_ptr<raw_ostream> &OS, SampleProfileFormat Format);
virtual void setProfileSymbolList(ProfileSymbolList *PSL) {}
virtual void setToCompressAllSections() {}
virtual void setUseMD5() {}
virtual void setPartialProfile() {}
virtual void resetSecLayout(SectionLayout SL) {}
protected:
SampleProfileWriter(std::unique_ptr<raw_ostream> &OS)
: OutputStream(std::move(OS)) {}
/// Write a file header for the profile file.
virtual std::error_code writeHeader(const SampleProfileMap &ProfileMap) = 0;
// Write function profiles to the profile file.
virtual std::error_code writeFuncProfiles(const SampleProfileMap &ProfileMap);
/// Output stream where to emit the profile to.
std::unique_ptr<raw_ostream> OutputStream;
/// Profile summary.
std::unique_ptr<ProfileSummary> Summary;
/// Compute summary for this profile.
void computeSummary(const SampleProfileMap &ProfileMap);
/// Profile format.
SampleProfileFormat Format = SPF_None;
};
/// Sample-based profile writer (text format).
class SampleProfileWriterText : public SampleProfileWriter {
public:
std::error_code writeSample(const FunctionSamples &S) override;
protected:
SampleProfileWriterText(std::unique_ptr<raw_ostream> &OS)
: SampleProfileWriter(OS), Indent(0) {}
std::error_code writeHeader(const SampleProfileMap &ProfileMap) override {
return sampleprof_error::success;
}
private:
/// Indent level to use when writing.
///
/// This is used when printing inlined callees.
unsigned Indent;
friend ErrorOr<std::unique_ptr<SampleProfileWriter>>
SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
SampleProfileFormat Format);
};
/// Sample-based profile writer (binary format).
class SampleProfileWriterBinary : public SampleProfileWriter {
public:
SampleProfileWriterBinary(std::unique_ptr<raw_ostream> &OS)
: SampleProfileWriter(OS) {}
virtual std::error_code writeSample(const FunctionSamples &S) override;
protected:
virtual MapVector<StringRef, uint32_t> &getNameTable() { return NameTable; }
virtual std::error_code writeMagicIdent(SampleProfileFormat Format);
virtual std::error_code writeNameTable();
virtual std::error_code
writeHeader(const SampleProfileMap &ProfileMap) override;
std::error_code writeSummary();
virtual std::error_code writeContextIdx(const SampleContext &Context);
std::error_code writeNameIdx(StringRef FName);
std::error_code writeBody(const FunctionSamples &S);
inline void stablizeNameTable(MapVector<StringRef, uint32_t> &NameTable,
std::set<StringRef> &V);
MapVector<StringRef, uint32_t> NameTable;
void addName(StringRef FName);
virtual void addContext(const SampleContext &Context);
void addNames(const FunctionSamples &S);
private:
friend ErrorOr<std::unique_ptr<SampleProfileWriter>>
SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
SampleProfileFormat Format);
};
class SampleProfileWriterRawBinary : public SampleProfileWriterBinary {
using SampleProfileWriterBinary::SampleProfileWriterBinary;
};
const std::array<SmallVector<SecHdrTableEntry, 8>, NumOfLayout>
ExtBinaryHdrLayoutTable = {
// Note that SecFuncOffsetTable section is written after SecLBRProfile
// in the profile, but is put before SecLBRProfile in SectionHdrLayout.
// This is because sample reader follows the order in SectionHdrLayout
// to read each section. To read function profiles on demand, sample
// reader need to get the offset of each function profile first.
//
// DefaultLayout
SmallVector<SecHdrTableEntry, 8>({{SecProfSummary, 0, 0, 0, 0},
{SecNameTable, 0, 0, 0, 0},
{SecCSNameTable, 0, 0, 0, 0},
{SecFuncOffsetTable, 0, 0, 0, 0},
{SecLBRProfile, 0, 0, 0, 0},
{SecProfileSymbolList, 0, 0, 0, 0},
{SecFuncMetadata, 0, 0, 0, 0}}),
// CtxSplitLayout
SmallVector<SecHdrTableEntry, 8>({{SecProfSummary, 0, 0, 0, 0},
{SecNameTable, 0, 0, 0, 0},
// profile with context
// for next two sections
{SecFuncOffsetTable, 0, 0, 0, 0},
{SecLBRProfile, 0, 0, 0, 0},
// profile without context
// for next two sections
{SecFuncOffsetTable, 0, 0, 0, 0},
{SecLBRProfile, 0, 0, 0, 0},
{SecProfileSymbolList, 0, 0, 0, 0},
{SecFuncMetadata, 0, 0, 0, 0}}),
};
class SampleProfileWriterExtBinaryBase : public SampleProfileWriterBinary {
using SampleProfileWriterBinary::SampleProfileWriterBinary;
public:
virtual std::error_code write(const SampleProfileMap &ProfileMap) override;
virtual void setToCompressAllSections() override;
void setToCompressSection(SecType Type);
virtual std::error_code writeSample(const FunctionSamples &S) override;
// Set to use MD5 to represent string in NameTable.
virtual void setUseMD5() override {
UseMD5 = true;
addSectionFlag(SecNameTable, SecNameTableFlags::SecFlagMD5Name);
// MD5 will be stored as plain uint64_t instead of variable-length
// quantity format in NameTable section.
addSectionFlag(SecNameTable, SecNameTableFlags::SecFlagFixedLengthMD5);
}
// Set the profile to be partial. It means the profile is for
// common/shared code. The common profile is usually merged from
// profiles collected from running other targets.
virtual void setPartialProfile() override {
addSectionFlag(SecProfSummary, SecProfSummaryFlags::SecFlagPartial);
}
virtual void setProfileSymbolList(ProfileSymbolList *PSL) override {
ProfSymList = PSL;
};
virtual void resetSecLayout(SectionLayout SL) override {
verifySecLayout(SL);
#ifndef NDEBUG
// Make sure resetSecLayout is called before any flag setting.
for (auto &Entry : SectionHdrLayout) {
assert(Entry.Flags == 0 &&
"resetSecLayout has to be called before any flag setting");
}
#endif
SecLayout = SL;
SectionHdrLayout = ExtBinaryHdrLayoutTable[SL];
}
protected:
uint64_t markSectionStart(SecType Type, uint32_t LayoutIdx);
std::error_code addNewSection(SecType Sec, uint32_t LayoutIdx,
uint64_t SectionStart);
template <class SecFlagType>
void addSectionFlag(SecType Type, SecFlagType Flag) {
for (auto &Entry : SectionHdrLayout) {
if (Entry.Type == Type)
addSecFlag(Entry, Flag);
}
}
template <class SecFlagType>
void addSectionFlag(uint32_t SectionIdx, SecFlagType Flag) {
addSecFlag(SectionHdrLayout[SectionIdx], Flag);
}
virtual void addContext(const SampleContext &Context) override;
// placeholder for subclasses to dispatch their own section writers.
virtual std::error_code writeCustomSection(SecType Type) = 0;
// Verify the SecLayout is supported by the format.
virtual void verifySecLayout(SectionLayout SL) = 0;
// specify the order to write sections.
virtual std::error_code writeSections(const SampleProfileMap &ProfileMap) = 0;
// Dispatch section writer for each section. \p LayoutIdx is the sequence
// number indicating where the section is located in SectionHdrLayout.
virtual std::error_code writeOneSection(SecType Type, uint32_t LayoutIdx,
const SampleProfileMap &ProfileMap);
// Helper function to write name table.
virtual std::error_code writeNameTable() override;
virtual std::error_code
writeContextIdx(const SampleContext &Context) override;
std::error_code writeCSNameIdx(const SampleContext &Context);
std::error_code writeCSNameTableSection();
std::error_code writeFuncMetadata(const SampleProfileMap &Profiles);
std::error_code writeFuncMetadata(const FunctionSamples &Profile);
// Functions to write various kinds of sections.
std::error_code writeNameTableSection(const SampleProfileMap &ProfileMap);
std::error_code writeFuncOffsetTable();
std::error_code writeProfileSymbolListSection();
SectionLayout SecLayout = DefaultLayout;
// Specify the order of sections in section header table. Note
// the order of sections in SecHdrTable may be different that the
// order in SectionHdrLayout. sample Reader will follow the order
// in SectionHdrLayout to read each section.
SmallVector<SecHdrTableEntry, 8> SectionHdrLayout =
ExtBinaryHdrLayoutTable[DefaultLayout];
// Save the start of SecLBRProfile so we can compute the offset to the
// start of SecLBRProfile for each Function's Profile and will keep it
// in FuncOffsetTable.
uint64_t SecLBRProfileStart = 0;
private:
void allocSecHdrTable();
std::error_code writeSecHdrTable();
virtual std::error_code
writeHeader(const SampleProfileMap &ProfileMap) override;
std::error_code compressAndOutput();
// We will swap the raw_ostream held by LocalBufStream and that
// held by OutputStream if we try to add a section which needs
// compression. After the swap, all the data written to output
// will be temporarily buffered into the underlying raw_string_ostream
// originally held by LocalBufStream. After the data writing for the
// section is completed, compress the data in the local buffer,
// swap the raw_ostream back and write the compressed data to the
// real output.
std::unique_ptr<raw_ostream> LocalBufStream;
// The location where the output stream starts.
uint64_t FileStart;
// The location in the output stream where the SecHdrTable should be
// written to.
uint64_t SecHdrTableOffset;
// The table contains SecHdrTableEntry entries in order of how they are
// populated in the writer. It may be different from the order in
// SectionHdrLayout which specifies the sequence in which sections will
// be read.
std::vector<SecHdrTableEntry> SecHdrTable;
// FuncOffsetTable maps function context to its profile offset in
// SecLBRProfile section. It is used to load function profile on demand.
MapVector<SampleContext, uint64_t> FuncOffsetTable;
// Whether to use MD5 to represent string.
bool UseMD5 = false;
/// CSNameTable maps function context to its offset in SecCSNameTable section.
/// The offset will be used everywhere where the context is referenced.
MapVector<SampleContext, uint32_t> CSNameTable;
ProfileSymbolList *ProfSymList = nullptr;
};
class SampleProfileWriterExtBinary : public SampleProfileWriterExtBinaryBase {
public:
SampleProfileWriterExtBinary(std::unique_ptr<raw_ostream> &OS)
: SampleProfileWriterExtBinaryBase(OS) {}
private:
std::error_code writeDefaultLayout(const SampleProfileMap &ProfileMap);
std::error_code writeCtxSplitLayout(const SampleProfileMap &ProfileMap);
virtual std::error_code
writeSections(const SampleProfileMap &ProfileMap) override;
virtual std::error_code writeCustomSection(SecType Type) override {
return sampleprof_error::success;
};
virtual void verifySecLayout(SectionLayout SL) override {
assert((SL == DefaultLayout || SL == CtxSplitLayout) &&
"Unsupported layout");
}
};
// CompactBinary is a compact format of binary profile which both reduces
// the profile size and the load time needed when compiling. It has two
// major difference with Binary format.
// 1. It represents all the strings in name table using md5 hash.
// 2. It saves a function offset table which maps function name index to
// the offset of its function profile to the start of the binary profile,
// so by using the function offset table, for those function profiles which
// will not be needed when compiling a module, the profile reader does't
// have to read them and it saves compile time if the profile size is huge.
// The layout of the compact format is shown as follows:
//
// Part1: Profile header, the same as binary format, containing magic
// number, version, summary, name table...
// Part2: Function Offset Table Offset, which saves the position of
// Part4.
// Part3: Function profile collection
// function1 profile start
// ....
// function2 profile start
// ....
// function3 profile start
// ....
// ......
// Part4: Function Offset Table
// function1 name index --> function1 profile start
// function2 name index --> function2 profile start
// function3 name index --> function3 profile start
//
// We need Part2 because profile reader can use it to find out and read
// function offset table without reading Part3 first.
class SampleProfileWriterCompactBinary : public SampleProfileWriterBinary {
using SampleProfileWriterBinary::SampleProfileWriterBinary;
public:
virtual std::error_code writeSample(const FunctionSamples &S) override;
virtual std::error_code write(const SampleProfileMap &ProfileMap) override;
protected:
/// The table mapping from function name to the offset of its FunctionSample
/// towards profile start.
MapVector<StringRef, uint64_t> FuncOffsetTable;
/// The offset of the slot to be filled with the offset of FuncOffsetTable
/// towards profile start.
uint64_t TableOffset;
virtual std::error_code writeNameTable() override;
virtual std::error_code
writeHeader(const SampleProfileMap &ProfileMap) override;
std::error_code writeFuncOffsetTable();
};
} // end namespace sampleprof
} // end namespace llvm
#endif // LLVM_PROFILEDATA_SAMPLEPROFWRITER_H