unsupported_writing_format,
truncated_name_table,
not_implemented,
- counter_overflow
+ counter_overflow,
+ ostream_seek_unsupported
};
inline std::error_code make_error_code(sampleprof_error E) {
/// Print the profile for \p FName on stream \p OS.
void dumpFunctionProfile(StringRef FName, raw_ostream &OS = dbgs());
+ virtual void collectFuncsToUse(const Module &M) {}
+
/// Print all the profiles on stream \p OS.
void dump(raw_ostream &OS = dbgs());
: SampleProfileReader(std::move(B), C, Format) {}
/// Read and validate the file header.
- std::error_code readHeader() override;
+ virtual std::error_code readHeader() override;
/// Read sample profiles from the associated file.
std::error_code read() override;
/// \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
/// 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();
+
/// Read the contents of the given profile instance.
std::error_code readProfile(FunctionSamples &FProfile);
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,
/// \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 read() override;
+
+ /// Collect functions to be used when compiling Module \p M.
+ void collectFuncsToUse(const Module &M) override;
};
using InlineCallStack = SmallVector<FunctionSamples *, 10>;
/// Write all the sample profiles in the given map of samples.
///
/// \returns status code of the file update operation.
- std::error_code write(const StringMap<FunctionSamples> &ProfileMap);
+ virtual std::error_code write(const StringMap<FunctionSamples> &ProfileMap);
raw_ostream &getOutputStream() { return *OutputStream; }
/// Sample-based profile writer (binary format).
class SampleProfileWriterBinary : public SampleProfileWriter {
public:
- std::error_code write(const FunctionSamples &S) override;
+ virtual std::error_code write(const FunctionSamples &S) override;
SampleProfileWriterBinary(std::unique_ptr<raw_ostream> &OS)
: SampleProfileWriter(OS) {}
protected:
virtual std::error_code writeNameTable() = 0;
virtual std::error_code writeMagicIdent() = 0;
- std::error_code writeHeader(const StringMap<FunctionSamples> &ProfileMap) override;
+ virtual std::error_code
+ writeHeader(const StringMap<FunctionSamples> &ProfileMap) override;
std::error_code writeSummary();
std::error_code writeNameIdx(StringRef FName);
std::error_code writeBody(const FunctionSamples &S);
virtual std::error_code writeMagicIdent() override;
};
+// 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 write(const FunctionSamples &S) override;
+ virtual std::error_code
+ write(const StringMap<FunctionSamples> &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 writeMagicIdent() override;
+ virtual std::error_code
+ writeHeader(const StringMap<FunctionSamples> &ProfileMap) override;
+ std::error_code writeFuncOffsetTable();
};
} // end namespace sampleprof
return "Unimplemented feature";
case sampleprof_error::counter_overflow:
return "Counter overflow";
+ case sampleprof_error::ostream_seek_unsupported:
+ return "Ostream does not support seek";
}
llvm_unreachable("A value of sampleprof_error has no message.");
}
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/MD5.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
}
template <typename T>
+ErrorOr<T> SampleProfileReaderBinary::readUnencodedNumber() {
+ std::error_code EC;
+
+ if (Data + sizeof(T) > End) {
+ EC = sampleprof_error::truncated;
+ reportError(0, EC.message());
+ return EC;
+ }
+
+ using namespace support;
+ T Val = endian::readNext<T, little, unaligned>(Data);
+ return Val;
+}
+
+template <typename T>
inline ErrorOr<uint32_t> SampleProfileReaderBinary::readStringIndex(T &Table) {
std::error_code EC;
auto Idx = readNumber<uint32_t>();
return sampleprof_error::success;
}
-std::error_code SampleProfileReaderBinary::read() {
- while (!at_eof()) {
- auto NumHeadSamples = readNumber<uint64_t>();
- if (std::error_code EC = NumHeadSamples.getError())
- return EC;
+std::error_code SampleProfileReaderBinary::readFuncProfile() {
+ auto NumHeadSamples = readNumber<uint64_t>();
+ if (std::error_code EC = NumHeadSamples.getError())
+ return EC;
- auto FName(readStringFromTable());
- if (std::error_code EC = FName.getError())
- return EC;
+ auto FName(readStringFromTable());
+ if (std::error_code EC = FName.getError())
+ return EC;
- Profiles[*FName] = FunctionSamples();
- FunctionSamples &FProfile = Profiles[*FName];
- FProfile.setName(*FName);
+ Profiles[*FName] = FunctionSamples();
+ FunctionSamples &FProfile = Profiles[*FName];
+ FProfile.setName(*FName);
- FProfile.addHeadSamples(*NumHeadSamples);
+ FProfile.addHeadSamples(*NumHeadSamples);
+
+ if (std::error_code EC = readProfile(FProfile))
+ return EC;
+ return sampleprof_error::success;
+}
- if (std::error_code EC = readProfile(FProfile))
+std::error_code SampleProfileReaderBinary::read() {
+ while (!at_eof()) {
+ if (std::error_code EC = readFuncProfile())
return EC;
}
return sampleprof_error::success;
}
+std::error_code SampleProfileReaderCompactBinary::read() {
+ for (auto Name : FuncsToUse) {
+ auto GUID = std::to_string(MD5Hash(Name));
+ auto iter = FuncOffsetTable.find(StringRef(GUID));
+ if (iter == FuncOffsetTable.end())
+ continue;
+ const uint8_t *SavedData = Data;
+ Data = reinterpret_cast<const uint8_t *>(Buffer->getBufferStart()) +
+ iter->second;
+ if (std::error_code EC = readFuncProfile())
+ return EC;
+ Data = SavedData;
+ }
+ return sampleprof_error::success;
+}
+
std::error_code SampleProfileReaderRawBinary::verifySPMagic(uint64_t Magic) {
if (Magic == SPMagic())
return sampleprof_error::success;
return sampleprof_error::success;
}
+std::error_code SampleProfileReaderCompactBinary::readHeader() {
+ SampleProfileReaderBinary::readHeader();
+ if (std::error_code EC = readFuncOffsetTable())
+ return EC;
+ return sampleprof_error::success;
+}
+
+std::error_code SampleProfileReaderCompactBinary::readFuncOffsetTable() {
+ auto TableOffset = readUnencodedNumber<uint64_t>();
+ if (std::error_code EC = TableOffset.getError())
+ return EC;
+
+ const uint8_t *SavedData = Data;
+ const uint8_t *TableStart =
+ reinterpret_cast<const uint8_t *>(Buffer->getBufferStart()) +
+ *TableOffset;
+ Data = TableStart;
+
+ auto Size = readNumber<uint64_t>();
+ if (std::error_code EC = Size.getError())
+ return EC;
+
+ FuncOffsetTable.reserve(*Size);
+ for (uint32_t I = 0; I < *Size; ++I) {
+ auto FName(readStringFromTable());
+ if (std::error_code EC = FName.getError())
+ return EC;
+
+ auto Offset = readNumber<uint64_t>();
+ if (std::error_code EC = Offset.getError())
+ return EC;
+
+ FuncOffsetTable[*FName] = *Offset;
+ }
+ End = TableStart;
+ Data = SavedData;
+ return sampleprof_error::success;
+}
+
+void SampleProfileReaderCompactBinary::collectFuncsToUse(const Module &M) {
+ FuncsToUse.clear();
+ for (auto &F : M) {
+ StringRef Fname = F.getName().split('.').first;
+ FuncsToUse.insert(Fname);
+ }
+}
+
std::error_code SampleProfileReaderBinary::readSummaryEntry(
std::vector<ProfileSummaryEntry> &Entries) {
auto Cutoff = readNumber<uint64_t>();
#include "llvm/ADT/StringRef.h"
#include "llvm/ProfileData/ProfileCommon.h"
#include "llvm/ProfileData/SampleProf.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/EndianStream.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/LEB128.h"
return sampleprof_error::success;
}
+std::error_code SampleProfileWriterCompactBinary::write(
+ const StringMap<FunctionSamples> &ProfileMap) {
+ if (std::error_code EC = SampleProfileWriter::write(ProfileMap))
+ return EC;
+ if (std::error_code EC = writeFuncOffsetTable())
+ return EC;
+ return sampleprof_error::success;
+}
+
/// Write samples to a text file.
///
/// Note: it may be tempting to implement this in terms of
return sampleprof_error::success;
}
+std::error_code SampleProfileWriterCompactBinary::writeFuncOffsetTable() {
+ auto &OS = *OutputStream;
+
+ // Fill the slot remembered by TableOffset with the offset of FuncOffsetTable.
+ auto &OFS = static_cast<raw_fd_ostream &>(OS);
+ uint64_t FuncOffsetTableStart = OS.tell();
+ if (OFS.seek(TableOffset) == (uint64_t)-1)
+ return sampleprof_error::ostream_seek_unsupported;
+ support::endian::Writer Writer(*OutputStream, support::little);
+ Writer.write(FuncOffsetTableStart);
+ if (OFS.seek(FuncOffsetTableStart) == (uint64_t)-1)
+ return sampleprof_error::ostream_seek_unsupported;
+
+ // Write out the table size.
+ encodeULEB128(FuncOffsetTable.size(), OS);
+
+ // Write out FuncOffsetTable.
+ for (auto entry : FuncOffsetTable) {
+ writeNameIdx(entry.first);
+ encodeULEB128(entry.second, OS);
+ }
+ return sampleprof_error::success;
+}
+
std::error_code SampleProfileWriterCompactBinary::writeNameTable() {
auto &OS = *OutputStream;
std::set<StringRef> V;
return sampleprof_error::success;
}
+std::error_code SampleProfileWriterCompactBinary::writeHeader(
+ const StringMap<FunctionSamples> &ProfileMap) {
+ support::endian::Writer Writer(*OutputStream, support::little);
+ if (auto EC = SampleProfileWriterBinary::writeHeader(ProfileMap))
+ return EC;
+
+ // Reserve a slot for the offset of function offset table. The slot will
+ // be populated with the offset of FuncOffsetTable later.
+ TableOffset = OutputStream->tell();
+ Writer.write(static_cast<uint64_t>(-2));
+ return sampleprof_error::success;
+}
+
std::error_code SampleProfileWriterBinary::writeSummary() {
auto &OS = *OutputStream;
encodeULEB128(Summary->getTotalCount(), OS);
return writeBody(S);
}
+std::error_code
+SampleProfileWriterCompactBinary::write(const FunctionSamples &S) {
+ uint64_t Offset = OutputStream->tell();
+ StringRef Name = S.getName();
+ FuncOffsetTable[Name] = Offset;
+ encodeULEB128(S.getHeadSamples(), *OutputStream);
+ return writeBody(S);
+}
+
/// Create a sample profile file writer based on the specified format.
///
/// \param Filename The file to create.
return false;
}
Reader = std::move(ReaderOrErr.get());
+ Reader->collectFuncsToUse(M);
ProfileIsValid = (Reader->read() == sampleprof_error::success);
return true;
}
namespace {
struct SampleProfTest : ::testing::Test {
- std::string Data;
LLVMContext Context;
+ std::string Profile;
std::unique_ptr<raw_ostream> OS;
std::unique_ptr<SampleProfileWriter> Writer;
std::unique_ptr<SampleProfileReader> Reader;
+ std::error_code EC;
SampleProfTest()
- : Data(), OS(new raw_string_ostream(Data)), Writer(), Reader() {}
+ : Profile("profile"),
+ OS(new raw_fd_ostream(Profile, EC, sys::fs::F_None)), Writer(),
+ Reader() {}
void createWriter(SampleProfileFormat Format) {
auto WriterOrErr = SampleProfileWriter::create(OS, Format);
Writer = std::move(WriterOrErr.get());
}
- void readProfile(std::unique_ptr<MemoryBuffer> &Profile) {
+ void readProfile(const Module &M) {
auto ReaderOrErr = SampleProfileReader::create(Profile, Context);
ASSERT_TRUE(NoError(ReaderOrErr.getError()));
Reader = std::move(ReaderOrErr.get());
+ Reader->collectFuncsToUse(M);
}
void testRoundTrip(SampleProfileFormat Format) {
BarSamples.addCalledTargetSamples(1, 0, MconstructName, 1000);
BarSamples.addCalledTargetSamples(1, 0, StringviewName, 437);
+ Module M("my_module", Context);
+ FunctionType *fn_type =
+ FunctionType::get(Type::getVoidTy(Context), {}, false);
+ M.getOrInsertFunction(FooName, fn_type);
+ M.getOrInsertFunction(BarName, fn_type);
+
StringMap<FunctionSamples> Profiles;
Profiles[FooName] = std::move(FooSamples);
Profiles[BarName] = std::move(BarSamples);
Writer->getOutputStream().flush();
- auto Profile = MemoryBuffer::getMemBufferCopy(Data);
- readProfile(Profile);
+ readProfile(M);
EC = Reader->read();
ASSERT_TRUE(NoError(EC));
delete PS;
// Test that summary can be attached to and read back from module.
- Module M("my_module", Context);
M.setProfileSummary(MD);
MD = M.getProfileSummary();
ASSERT_TRUE(MD);