Skip to content

[MemProf] Add v4 which contains CalleeGuids to CallSiteInfo. #137394

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion llvm/include/llvm/ProfileData/InstrProfReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,8 @@ class IndexedMemProfReader {
unsigned RadixTreeSize = 0;

Error deserializeV2(const unsigned char *Start, const unsigned char *Ptr);
Error deserializeV3(const unsigned char *Start, const unsigned char *Ptr);
Error deserializeRadixTreeBased(const unsigned char *Start,
const unsigned char *Ptr);

public:
IndexedMemProfReader() = default;
Expand Down
4 changes: 3 additions & 1 deletion llvm/include/llvm/ProfileData/MemProf.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ enum IndexedVersion : uint64_t {
// Version 3: Added a radix tree for call stacks. Switched to linear IDs for
// frames and call stacks.
Version3 = 3,
// Version 4: Added CalleeGuids to call site info.
Version4 = 4,
};

constexpr uint64_t MinimumSupportedVersion = Version2;
constexpr uint64_t MaximumSupportedVersion = Version3;
constexpr uint64_t MaximumSupportedVersion = Version4;

// Verify that the minimum and maximum satisfy the obvious constraint.
static_assert(MinimumSupportedVersion <= MaximumSupportedVersion);
Expand Down
60 changes: 55 additions & 5 deletions llvm/include/llvm/ProfileData/MemProfYAML.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef LLVM_PROFILEDATA_MEMPROFYAML_H_
#define LLVM_PROFILEDATA_MEMPROFYAML_H_

#include "llvm/ADT/SmallVector.h"
#include "llvm/ProfileData/MemProf.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/YAMLTraits.h"
Expand Down Expand Up @@ -28,8 +29,9 @@ struct AllMemProfData {
namespace yaml {
template <> struct ScalarTraits<memprof::GUIDHex64> {
static void output(const memprof::GUIDHex64 &Val, void *, raw_ostream &Out) {
// Print GUID as a 16-digit hexadecimal number.
Out << format("0x%016" PRIx64, (uint64_t)Val);
// Print GUID as a hexadecimal number with 0x prefix, no padding to keep
// test strings compact.
Out << format("0x%" PRIx64, (uint64_t)Val);
}
static StringRef input(StringRef Scalar, void *, memprof::GUIDHex64 &Val) {
// Reject decimal GUIDs.
Expand Down Expand Up @@ -156,10 +158,43 @@ template <> struct MappingTraits<memprof::AllocationInfo> {
// treat the GUID and the fields within MemProfRecord at the same level as if
// the GUID were part of MemProfRecord.
template <> struct MappingTraits<memprof::CallSiteInfo> {
// Helper class to normalize CalleeGuids to use GUIDHex64 for YAML I/O.
class CallSiteInfoWithHex64Guids {
public:
CallSiteInfoWithHex64Guids(IO &) {}
CallSiteInfoWithHex64Guids(IO &, const memprof::CallSiteInfo &CS)
: Frames(CS.Frames) {
// Convert uint64_t GUIDs to GUIDHex64 for serialization.
CalleeGuids.reserve(CS.CalleeGuids.size());
for (uint64_t Guid : CS.CalleeGuids)
CalleeGuids.push_back(memprof::GUIDHex64(Guid));
}

memprof::CallSiteInfo denormalize(IO &) {
memprof::CallSiteInfo CS;
CS.Frames = Frames;
// Convert GUIDHex64 back to uint64_t GUIDs after deserialization.
CS.CalleeGuids.reserve(CalleeGuids.size());
for (memprof::GUIDHex64 HexGuid : CalleeGuids)
CS.CalleeGuids.push_back(HexGuid.value);
return CS;
}

// Keep Frames as is, since MappingTraits<memprof::Frame> handles its
// Function GUID.
decltype(memprof::CallSiteInfo::Frames) Frames;
// Use a vector of GUIDHex64 for CalleeGuids to leverage its ScalarTraits.
SmallVector<memprof::GUIDHex64> CalleeGuids;
};

static void mapping(IO &Io, memprof::CallSiteInfo &CS) {
Io.mapRequired("Frames", CS.Frames);
// Keep this optional to make it easier to write tests.
Io.mapOptional("CalleeGuids", CS.CalleeGuids);
// Use MappingNormalization to handle the conversion between
// memprof::CallSiteInfo and CallSiteInfoWithHex64Guids.
MappingNormalization<CallSiteInfoWithHex64Guids, memprof::CallSiteInfo>
Keys(Io, CS);
Io.mapRequired("Frames", Keys->Frames);
// Map the normalized CalleeGuids (which are now GUIDHex64).
Io.mapOptional("CalleeGuids", Keys->CalleeGuids);
}
};

Expand All @@ -176,6 +211,20 @@ template <> struct MappingTraits<memprof::AllMemProfData> {
Io.mapRequired("HeapProfileRecords", Data.HeapProfileRecords);
}
};

template <> struct SequenceTraits<SmallVector<memprof::GUIDHex64>> {
static size_t size(IO &io, SmallVector<memprof::GUIDHex64> &Seq) {
return Seq.size();
}
static memprof::GUIDHex64 &
element(IO &io, SmallVector<memprof::GUIDHex64> &Seq, size_t Index) {
if (Index >= Seq.size())
Seq.resize(Index + 1);
return Seq[Index];
}
static const bool flow = true;
};

} // namespace yaml
} // namespace llvm

Expand All @@ -184,5 +233,6 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(std::vector<memprof::Frame>)
LLVM_YAML_IS_SEQUENCE_VECTOR(memprof::AllocationInfo)
LLVM_YAML_IS_SEQUENCE_VECTOR(memprof::CallSiteInfo)
LLVM_YAML_IS_SEQUENCE_VECTOR(memprof::GUIDMemProfRecordPair)
LLVM_YAML_IS_SEQUENCE_VECTOR(memprof::GUIDHex64) // Used for CalleeGuids

#endif // LLVM_PROFILEDATA_MEMPROFYAML_H_
69 changes: 39 additions & 30 deletions llvm/lib/ProfileData/IndexedMemProfData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,23 +214,13 @@ static Error writeMemProfV2(ProfOStream &OS,
return Error::success();
}

// Write out MemProf Version3 as follows:
// uint64_t Version
// uint64_t CallStackPayloadOffset = Offset for the call stack payload
// uint64_t RecordPayloadOffset = Offset for the record payload
// uint64_t RecordTableOffset = RecordTableGenerator.Emit
// uint64_t Num schema entries
// uint64_t Schema entry 0
// uint64_t Schema entry 1
// ....
// uint64_t Schema entry N - 1
// Frames serialized one after another
// Call stacks encoded as a radix tree
// OnDiskChainedHashTable MemProfRecordData
static Error writeMemProfV3(ProfOStream &OS,
memprof::IndexedMemProfData &MemProfData,
bool MemProfFullSchema) {
OS.write(memprof::Version3);
static Error writeMemProfRadixTreeBased(
ProfOStream &OS, memprof::IndexedMemProfData &MemProfData,
memprof::IndexedVersion Version, bool MemProfFullSchema) {
assert((Version == memprof::Version3 || Version == memprof::Version4) &&
"Unsupported version for radix tree format");

OS.write(Version); // Write the specific version (V3 or V4)
uint64_t HeaderUpdatePos = OS.tell();
OS.write(0ULL); // Reserve space for the memprof call stack payload offset.
OS.write(0ULL); // Reserve space for the memprof record payload offset.
Expand Down Expand Up @@ -258,13 +248,11 @@ static Error writeMemProfV3(ProfOStream &OS,
NumElements);

uint64_t RecordPayloadOffset = OS.tell();
uint64_t RecordTableOffset =
writeMemProfRecords(OS, MemProfData.Records, &Schema, memprof::Version3,
&MemProfCallStackIndexes);
uint64_t RecordTableOffset = writeMemProfRecords(
OS, MemProfData.Records, &Schema, Version, &MemProfCallStackIndexes);

// IndexedMemProfReader::deserializeV3 computes the number of elements in the
// call stack array from the difference between CallStackPayloadOffset and
// RecordPayloadOffset. Verify that the computation works.
// Verify that the computation for the number of elements in the call stack
// array works.
assert(CallStackPayloadOffset +
NumElements * sizeof(memprof::LinearFrameId) ==
RecordPayloadOffset);
Expand All @@ -279,6 +267,22 @@ static Error writeMemProfV3(ProfOStream &OS,
return Error::success();
}

// Write out MemProf Version3
static Error writeMemProfV3(ProfOStream &OS,
memprof::IndexedMemProfData &MemProfData,
bool MemProfFullSchema) {
return writeMemProfRadixTreeBased(OS, MemProfData, memprof::Version3,
MemProfFullSchema);
}

// Write out MemProf Version4
static Error writeMemProfV4(ProfOStream &OS,
memprof::IndexedMemProfData &MemProfData,
bool MemProfFullSchema) {
return writeMemProfRadixTreeBased(OS, MemProfData, memprof::Version4,
MemProfFullSchema);
}

// Write out the MemProf data in a requested version.
Error writeMemProf(ProfOStream &OS, memprof::IndexedMemProfData &MemProfData,
memprof::IndexedVersion MemProfVersionRequested,
Expand All @@ -288,6 +292,8 @@ Error writeMemProf(ProfOStream &OS, memprof::IndexedMemProfData &MemProfData,
return writeMemProfV2(OS, MemProfData, MemProfFullSchema);
case memprof::Version3:
return writeMemProfV3(OS, MemProfData, MemProfFullSchema);
case memprof::Version4:
return writeMemProfV4(OS, MemProfData, MemProfFullSchema);
}

return make_error<InstrProfError>(
Expand Down Expand Up @@ -350,8 +356,8 @@ Error IndexedMemProfReader::deserializeV2(const unsigned char *Start,
return Error::success();
}

Error IndexedMemProfReader::deserializeV3(const unsigned char *Start,
const unsigned char *Ptr) {
Error IndexedMemProfReader::deserializeRadixTreeBased(
const unsigned char *Start, const unsigned char *Ptr) {
// The offset in the stream right before invoking
// CallStackTableGenerator.Emit.
const uint64_t CallStackPayloadOffset =
Expand Down Expand Up @@ -382,7 +388,7 @@ Error IndexedMemProfReader::deserializeV3(const unsigned char *Start,
MemProfRecordTable.reset(MemProfRecordHashTable::Create(
/*Buckets=*/Start + RecordTableOffset,
/*Payload=*/Start + RecordPayloadOffset,
/*Base=*/Start, memprof::RecordLookupTrait(memprof::Version3, Schema)));
/*Base=*/Start, memprof::RecordLookupTrait(Version, Schema)));

return Error::success();
}
Expand All @@ -395,8 +401,10 @@ Error IndexedMemProfReader::deserialize(const unsigned char *Start,
const uint64_t FirstWord =
support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);

if (FirstWord == memprof::Version2 || FirstWord == memprof::Version3) {
// Everything is good. We can proceed to deserialize the rest.
// Check if the version is supported
if (FirstWord >= memprof::MinimumSupportedVersion &&
FirstWord <= memprof::MaximumSupportedVersion) {
// Everything is good. We can proceed to deserialize the rest.
Version = static_cast<memprof::IndexedVersion>(FirstWord);
} else {
return make_error<InstrProfError>(
Expand All @@ -413,12 +421,13 @@ Error IndexedMemProfReader::deserialize(const unsigned char *Start,
return E;
break;
case memprof::Version3:
if (Error E = deserializeV3(Start, Ptr))
case memprof::Version4:
// V3 and V4 share the same high-level structure (radix tree, linear IDs).
if (Error E = deserializeRadixTreeBased(Start, Ptr))
return E;
break;
}

return Error::success();
}

} // namespace llvm
21 changes: 9 additions & 12 deletions llvm/lib/ProfileData/InstrProfReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1456,16 +1456,6 @@ getMemProfRecordV2(const memprof::IndexedMemProfRecord &IndexedRecord,
return Record;
}

static Expected<memprof::MemProfRecord>
getMemProfRecordV3(const memprof::IndexedMemProfRecord &IndexedRecord,
const unsigned char *FrameBase,
const unsigned char *CallStackBase) {
memprof::LinearFrameIdConverter FrameIdConv(FrameBase);
memprof::LinearCallStackIdConverter CSIdConv(CallStackBase, FrameIdConv);
memprof::MemProfRecord Record = IndexedRecord.toMemProfRecord(CSIdConv);
return Record;
}

Expected<memprof::MemProfRecord>
IndexedMemProfReader::getMemProfRecord(const uint64_t FuncNameHash) const {
// TODO: Add memprof specific errors.
Expand All @@ -1485,13 +1475,20 @@ IndexedMemProfReader::getMemProfRecord(const uint64_t FuncNameHash) const {
assert(MemProfCallStackTable && "MemProfCallStackTable must be available");
return getMemProfRecordV2(IndexedRecord, *MemProfFrameTable,
*MemProfCallStackTable);
// Combine V3 and V4 cases as the record conversion logic is the same.
case memprof::Version3:
case memprof::Version4:
assert(!MemProfFrameTable && "MemProfFrameTable must not be available");
assert(!MemProfCallStackTable &&
"MemProfCallStackTable must not be available");
assert(FrameBase && "FrameBase must be available");
assert(CallStackBase && "CallStackBase must be available");
return getMemProfRecordV3(IndexedRecord, FrameBase, CallStackBase);
{
memprof::LinearFrameIdConverter FrameIdConv(FrameBase);
memprof::LinearCallStackIdConverter CSIdConv(CallStackBase, FrameIdConv);
memprof::MemProfRecord Record = IndexedRecord.toMemProfRecord(CSIdConv);
return Record;
}
}

return make_error<InstrProfError>(
Expand All @@ -1505,7 +1502,7 @@ IndexedMemProfReader::getMemProfRecord(const uint64_t FuncNameHash) const {
DenseMap<uint64_t, SmallVector<memprof::CallEdgeTy, 0>>
IndexedMemProfReader::getMemProfCallerCalleePairs() const {
assert(MemProfRecordTable);
assert(Version == memprof::Version3);
assert(Version == memprof::Version3 || Version == memprof::Version4);

memprof::LinearFrameIdConverter FrameIdConv(FrameBase);
memprof::CallerCalleePairExtractor Extractor(CallStackBase, FrameIdConv,
Expand Down
Loading