mirror of
https://github.com/RPCSX/llvm.git
synced 2024-11-24 12:19:53 +00:00
cfbaf11a0d
With the support of value profiling added, the Indexed prof reader gets less efficient. The prof reader initialization used to be just reading the file header, but with VP support added, initialization needs to walk through all profile keys of ondisk hash table resulting in very poor locality and large memory increase (keys are stored together with the profile data in the mapped profile buffer). Even worse, when the reader is used by the compiler (not llvm-profdata too), the penalty becomes very high as compilation of each single module requires touching profile data buffer for the whole program. In this patch, the icall target values (MD5hash) are no longer eargerly converted back to name strings when the data is read into memory. New interface is added to to profile reader so that InstrProfSymtab can be lazily created for Indexed profile reader on-demand. Creating of the symtab is intended to be used by llvm-profdata tool for symbolic dumping of VP data. It can be used with compiler (for legacy out of tree uses) too but not recommended due to compile time and memory reasons mentioned above. Some other cleanups are also included: Function Addr to md5 map is now consolated into InstrProfSymtab. InstrProfStringtab is no longer used and eliminated. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@256114 91177308-0d34-0410-b5e6-96231b3b80d8
251 lines
7.9 KiB
C++
251 lines
7.9 KiB
C++
//=-- InstrProfWriter.cpp - Instrumented profiling writer -------------------=//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file contains support for writing profiling data for clang's
|
|
// instrumentation based PGO and coverage.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/ProfileData/InstrProfWriter.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Support/EndianStream.h"
|
|
#include "llvm/Support/OnDiskHashTable.h"
|
|
#include <tuple>
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
static support::endianness ValueProfDataEndianness = support::little;
|
|
|
|
class InstrProfRecordTrait {
|
|
public:
|
|
typedef StringRef key_type;
|
|
typedef StringRef key_type_ref;
|
|
|
|
typedef const InstrProfWriter::ProfilingData *const data_type;
|
|
typedef const InstrProfWriter::ProfilingData *const data_type_ref;
|
|
|
|
typedef uint64_t hash_value_type;
|
|
typedef uint64_t offset_type;
|
|
|
|
static hash_value_type ComputeHash(key_type_ref K) {
|
|
return IndexedInstrProf::ComputeHash(K);
|
|
}
|
|
|
|
static std::pair<offset_type, offset_type>
|
|
EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
|
|
using namespace llvm::support;
|
|
endian::Writer<little> LE(Out);
|
|
|
|
offset_type N = K.size();
|
|
LE.write<offset_type>(N);
|
|
|
|
offset_type M = 0;
|
|
for (const auto &ProfileData : *V) {
|
|
const InstrProfRecord &ProfRecord = ProfileData.second;
|
|
M += sizeof(uint64_t); // The function hash
|
|
M += sizeof(uint64_t); // The size of the Counts vector
|
|
M += ProfRecord.Counts.size() * sizeof(uint64_t);
|
|
|
|
// Value data
|
|
M += ValueProfData::getSize(ProfileData.second);
|
|
}
|
|
LE.write<offset_type>(M);
|
|
|
|
return std::make_pair(N, M);
|
|
}
|
|
|
|
static void EmitKey(raw_ostream &Out, key_type_ref K, offset_type N){
|
|
Out.write(K.data(), N);
|
|
}
|
|
|
|
static void EmitData(raw_ostream &Out, key_type_ref, data_type_ref V,
|
|
offset_type) {
|
|
using namespace llvm::support;
|
|
endian::Writer<little> LE(Out);
|
|
for (const auto &ProfileData : *V) {
|
|
const InstrProfRecord &ProfRecord = ProfileData.second;
|
|
|
|
LE.write<uint64_t>(ProfileData.first); // Function hash
|
|
LE.write<uint64_t>(ProfRecord.Counts.size());
|
|
for (uint64_t I : ProfRecord.Counts)
|
|
LE.write<uint64_t>(I);
|
|
|
|
// Write value data
|
|
std::unique_ptr<ValueProfData> VDataPtr =
|
|
ValueProfData::serializeFrom(ProfileData.second);
|
|
uint32_t S = VDataPtr->getSize();
|
|
VDataPtr->swapBytesFromHost(ValueProfDataEndianness);
|
|
Out.write((const char *)VDataPtr.get(), S);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
// Internal interface for testing purpose only.
|
|
void InstrProfWriter::setValueProfDataEndianness(
|
|
support::endianness Endianness) {
|
|
ValueProfDataEndianness = Endianness;
|
|
}
|
|
|
|
std::error_code InstrProfWriter::addRecord(InstrProfRecord &&I,
|
|
uint64_t Weight) {
|
|
auto &ProfileDataMap = FunctionData[I.Name];
|
|
|
|
bool NewFunc;
|
|
ProfilingData::iterator Where;
|
|
std::tie(Where, NewFunc) =
|
|
ProfileDataMap.insert(std::make_pair(I.Hash, InstrProfRecord()));
|
|
InstrProfRecord &Dest = Where->second;
|
|
|
|
instrprof_error Result;
|
|
if (NewFunc) {
|
|
// We've never seen a function with this name and hash, add it.
|
|
Dest = std::move(I);
|
|
Result = instrprof_error::success;
|
|
if (Weight > 1) {
|
|
for (auto &Count : Dest.Counts) {
|
|
bool Overflowed;
|
|
Count = SaturatingMultiply(Count, Weight, &Overflowed);
|
|
if (Overflowed && Result == instrprof_error::success) {
|
|
Result = instrprof_error::counter_overflow;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// We're updating a function we've seen before.
|
|
Result = Dest.merge(I, Weight);
|
|
}
|
|
|
|
// We keep track of the max function count as we go for simplicity.
|
|
// Update this statistic no matter the result of the merge.
|
|
if (Dest.Counts[0] > MaxFunctionCount)
|
|
MaxFunctionCount = Dest.Counts[0];
|
|
|
|
return Result;
|
|
}
|
|
|
|
std::pair<uint64_t, uint64_t> InstrProfWriter::writeImpl(raw_ostream &OS) {
|
|
OnDiskChainedHashTableGenerator<InstrProfRecordTrait> Generator;
|
|
|
|
// Populate the hash table generator.
|
|
for (const auto &I : FunctionData)
|
|
Generator.insert(I.getKey(), &I.getValue());
|
|
|
|
using namespace llvm::support;
|
|
endian::Writer<little> LE(OS);
|
|
|
|
// Write the header.
|
|
IndexedInstrProf::Header Header;
|
|
Header.Magic = IndexedInstrProf::Magic;
|
|
Header.Version = IndexedInstrProf::Version;
|
|
Header.MaxFunctionCount = MaxFunctionCount;
|
|
Header.HashType = static_cast<uint64_t>(IndexedInstrProf::HashType);
|
|
Header.HashOffset = 0;
|
|
int N = sizeof(IndexedInstrProf::Header) / sizeof(uint64_t);
|
|
|
|
// Only write out all the fields execpt 'HashOffset'. We need
|
|
// to remember the offset of that field to allow back patching
|
|
// later.
|
|
for (int I = 0; I < N - 1; I++)
|
|
LE.write<uint64_t>(reinterpret_cast<uint64_t *>(&Header)[I]);
|
|
|
|
// Save a space to write the hash table start location.
|
|
uint64_t HashTableStartLoc = OS.tell();
|
|
// Reserve the space for HashOffset field.
|
|
LE.write<uint64_t>(0);
|
|
// Write the hash table.
|
|
uint64_t HashTableStart = Generator.Emit(OS);
|
|
|
|
return std::make_pair(HashTableStartLoc, HashTableStart);
|
|
}
|
|
|
|
void InstrProfWriter::write(raw_fd_ostream &OS) {
|
|
// Write the hash table.
|
|
auto TableStart = writeImpl(OS);
|
|
|
|
// Go back and fill in the hash table start.
|
|
using namespace support;
|
|
OS.seek(TableStart.first);
|
|
// Now patch the HashOffset field previously reserved.
|
|
endian::Writer<little>(OS).write<uint64_t>(TableStart.second);
|
|
}
|
|
|
|
static const char *ValueProfKindStr[] = {
|
|
#define VALUE_PROF_KIND(Enumerator, Value) #Enumerator,
|
|
#include "llvm/ProfileData/InstrProfData.inc"
|
|
};
|
|
|
|
void InstrProfWriter::writeRecordInText(const InstrProfRecord &Func,
|
|
InstrProfSymtab &Symtab,
|
|
raw_fd_ostream &OS) {
|
|
OS << Func.Name << "\n";
|
|
OS << "# Func Hash:\n" << Func.Hash << "\n";
|
|
OS << "# Num Counters:\n" << Func.Counts.size() << "\n";
|
|
OS << "# Counter Values:\n";
|
|
for (uint64_t Count : Func.Counts)
|
|
OS << Count << "\n";
|
|
|
|
uint32_t NumValueKinds = Func.getNumValueKinds();
|
|
if (!NumValueKinds) {
|
|
OS << "\n";
|
|
return;
|
|
}
|
|
|
|
OS << "# Num Value Kinds:\n" << Func.getNumValueKinds() << "\n";
|
|
for (uint32_t VK = 0; VK < IPVK_Last + 1; VK++) {
|
|
uint32_t NS = Func.getNumValueSites(VK);
|
|
if (!NS)
|
|
continue;
|
|
OS << "# ValueKind = " << ValueProfKindStr[VK] << ":\n" << VK << "\n";
|
|
OS << "# NumValueSites:\n" << NS << "\n";
|
|
for (uint32_t S = 0; S < NS; S++) {
|
|
uint32_t ND = Func.getNumValueDataForSite(VK, S);
|
|
OS << ND << "\n";
|
|
std::unique_ptr<InstrProfValueData[]> VD = Func.getValueForSite(VK, S);
|
|
for (uint32_t I = 0; I < ND; I++) {
|
|
if (VK == IPVK_IndirectCallTarget)
|
|
OS << Symtab.getFuncName(VD[I].Value) << ":" << VD[I].Count << "\n";
|
|
else
|
|
OS << VD[I].Value << ":" << VD[I].Count << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
OS << "\n";
|
|
}
|
|
|
|
void InstrProfWriter::writeText(raw_fd_ostream &OS) {
|
|
InstrProfSymtab Symtab;
|
|
for (const auto &I : FunctionData)
|
|
Symtab.addFuncName(I.getKey());
|
|
Symtab.finalizeSymtab();
|
|
|
|
for (const auto &I : FunctionData)
|
|
for (const auto &Func : I.getValue())
|
|
writeRecordInText(Func.second, Symtab, OS);
|
|
}
|
|
|
|
std::unique_ptr<MemoryBuffer> InstrProfWriter::writeBuffer() {
|
|
std::string Data;
|
|
llvm::raw_string_ostream OS(Data);
|
|
// Write the hash table.
|
|
auto TableStart = writeImpl(OS);
|
|
OS.flush();
|
|
|
|
// Go back and fill in the hash table start.
|
|
using namespace support;
|
|
uint64_t Bytes = endian::byte_swap<uint64_t, little>(TableStart.second);
|
|
Data.replace(TableStart.first, sizeof(uint64_t), (const char *)&Bytes,
|
|
sizeof(uint64_t));
|
|
|
|
// Return this in an aligned memory buffer.
|
|
return MemoryBuffer::getMemBufferCopy(Data);
|
|
}
|