Skip to content

Commit 43c2348

Browse files
committed
[memprof] Extend the index prof format to include memory profiles.
This patch adds support for optional memory profile information to be included with and indexed profile. The indexed profile header adds a new field which points to the offset of the memory profile section (if present) in the indexed profile. For users who do not utilize this feature the only overhead is a 64-bit offset in the header. The memory profile section contains (1) profile metadata describing the information recorded for each entry (2) an on-disk hashtable containing the profile records indexed via llvm::md5(function_name). We chose to introduce a separate hash table instead of the existing one since the indexing for the instrumented fdo hash table is based on a CFG hash which itself is perturbed by memprof instrumentation. Differential Revision: https://reviews.llvm.org/D118653
1 parent 14cc41a commit 43c2348

File tree

19 files changed

+651
-35
lines changed

19 files changed

+651
-35
lines changed

compiler-rt/include/profile/InstrProfData.inc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
650650
/* Raw profile format version (start from 1). */
651651
#define INSTR_PROF_RAW_VERSION 8
652652
/* Indexed profile format version (start from 1). */
653-
#define INSTR_PROF_INDEX_VERSION 7
653+
#define INSTR_PROF_INDEX_VERSION 8
654654
/* Coverage mapping format version (start from 0). */
655655
#define INSTR_PROF_COVMAP_VERSION 5
656656

@@ -662,6 +662,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
662662
* The 59th bit indicates whether to use debug info to correlate profiles.
663663
* The 60th bit indicates single byte coverage instrumentation.
664664
* The 61st bit indicates function entry instrumentation only.
665+
* The 62nd bit indicates whether memory profile information is present.
665666
*/
666667
#define VARIANT_MASKS_ALL 0xff00000000000000ULL
667668
#define GET_VERSION(V) ((V) & ~VARIANT_MASKS_ALL)
@@ -671,6 +672,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
671672
#define VARIANT_MASK_DBG_CORRELATE (0x1ULL << 59)
672673
#define VARIANT_MASK_BYTE_COVERAGE (0x1ULL << 60)
673674
#define VARIANT_MASK_FUNCTION_ENTRY_ONLY (0x1ULL << 61)
675+
#define VARIANT_MASK_MEMPROF (0x1ULL << 62)
674676
#define INSTR_PROF_RAW_VERSION_VAR __llvm_profile_raw_version
675677
#define INSTR_PROF_PROFILE_RUNTIME_VAR __llvm_profile_runtime
676678
#define INSTR_PROF_PROFILE_COUNTER_BIAS_VAR __llvm_profile_counter_bias

llvm/include/llvm/ProfileData/InstrProf.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,8 @@ enum class InstrProfKind {
287287
CS = 0x8, // A context sensitive IR-level profile.
288288
SingleByteCoverage = 0x10, // Use single byte probes for coverage.
289289
FunctionEntryOnly = 0x20, // Only instrument the function entry basic block.
290-
LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/FunctionEntryOnly)
290+
MemProf = 0x40, // A memory profile collected using -fmemory-profile.
291+
LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/MemProf)
291292
};
292293

293294
const std::error_category &instrprof_category();
@@ -1011,7 +1012,9 @@ enum ProfVersion {
10111012
Version6 = 6,
10121013
// An additional counter is added around logical operators.
10131014
Version7 = 7,
1014-
// The current version is 7.
1015+
// An additional (optional) memory profile type is added.
1016+
Version8 = 8,
1017+
// The current version is 8.
10151018
CurrentVersion = INSTR_PROF_INDEX_VERSION
10161019
};
10171020
const uint64_t Version = ProfVersion::CurrentVersion;
@@ -1028,6 +1031,7 @@ struct Header {
10281031
uint64_t Unused; // Becomes unused since version 4
10291032
uint64_t HashType;
10301033
uint64_t HashOffset;
1034+
uint64_t MemProfOffset;
10311035
// New fields should only be added at the end to ensure that the size
10321036
// computation is correct. The methods below need to be updated to ensure that
10331037
// the new field is read correctly.

llvm/include/llvm/ProfileData/InstrProfData.inc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
650650
/* Raw profile format version (start from 1). */
651651
#define INSTR_PROF_RAW_VERSION 8
652652
/* Indexed profile format version (start from 1). */
653-
#define INSTR_PROF_INDEX_VERSION 7
653+
#define INSTR_PROF_INDEX_VERSION 8
654654
/* Coverage mapping format version (start from 0). */
655655
#define INSTR_PROF_COVMAP_VERSION 5
656656

@@ -662,6 +662,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
662662
* The 59th bit indicates whether to use debug info to correlate profiles.
663663
* The 60th bit indicates single byte coverage instrumentation.
664664
* The 61st bit indicates function entry instrumentation only.
665+
* The 62nd bit indicates whether memory profile information is present.
665666
*/
666667
#define VARIANT_MASKS_ALL 0xff00000000000000ULL
667668
#define GET_VERSION(V) ((V) & ~VARIANT_MASKS_ALL)
@@ -671,6 +672,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
671672
#define VARIANT_MASK_DBG_CORRELATE (0x1ULL << 59)
672673
#define VARIANT_MASK_BYTE_COVERAGE (0x1ULL << 60)
673674
#define VARIANT_MASK_FUNCTION_ENTRY_ONLY (0x1ULL << 61)
675+
#define VARIANT_MASK_MEMPROF (0x1ULL << 62)
674676
#define INSTR_PROF_RAW_VERSION_VAR __llvm_profile_raw_version
675677
#define INSTR_PROF_PROFILE_RUNTIME_VAR __llvm_profile_runtime
676678
#define INSTR_PROF_PROFILE_COUNTER_BIAS_VAR __llvm_profile_counter_bias

llvm/include/llvm/ProfileData/InstrProfReader.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "llvm/IR/ProfileSummary.h"
2020
#include "llvm/ProfileData/InstrProf.h"
2121
#include "llvm/ProfileData/InstrProfCorrelator.h"
22+
#include "llvm/ProfileData/MemProf.h"
2223
#include "llvm/Support/Endian.h"
2324
#include "llvm/Support/Error.h"
2425
#include "llvm/Support/LineIterator.h"
@@ -471,6 +472,9 @@ struct InstrProfReaderIndexBase {
471472
using OnDiskHashTableImplV3 =
472473
OnDiskIterableChainedHashTable<InstrProfLookupTrait>;
473474

475+
using MemProfHashTable =
476+
OnDiskIterableChainedHashTable<memprof::MemProfRecordLookupTrait>;
477+
474478
template <typename HashTableImpl>
475479
class InstrProfReaderItaniumRemapper;
476480

@@ -556,6 +560,11 @@ class IndexedInstrProfReader : public InstrProfReader {
556560
std::unique_ptr<ProfileSummary> Summary;
557561
/// Context sensitive profile summary data.
558562
std::unique_ptr<ProfileSummary> CS_Summary;
563+
/// MemProf profile schema (if available).
564+
memprof::MemProfSchema Schema;
565+
/// MemProf profile data on-disk indexed via llvm::md5(FunctionName).
566+
std::unique_ptr<MemProfHashTable> MemProfTable;
567+
559568
// Index to the current record in the record array.
560569
unsigned RecordIndex;
561570

@@ -609,6 +618,11 @@ class IndexedInstrProfReader : public InstrProfReader {
609618
Expected<InstrProfRecord> getInstrProfRecord(StringRef FuncName,
610619
uint64_t FuncHash);
611620

621+
/// Return the memprof records for the function identified by
622+
/// llvm::md5(Name).
623+
Expected<ArrayRef<memprof::MemProfRecord>>
624+
getMemProfRecord(uint64_t FuncNameHash);
625+
612626
/// Fill Counts with the profile data for the given function name.
613627
Error getFunctionCounts(StringRef FuncName, uint64_t FuncHash,
614628
std::vector<uint64_t> &Counts);

llvm/include/llvm/ProfileData/InstrProfWriter.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "llvm/ADT/DenseMap.h"
1818
#include "llvm/ADT/StringMap.h"
1919
#include "llvm/ProfileData/InstrProf.h"
20+
#include "llvm/ProfileData/MemProf.h"
2021
#include "llvm/Support/Endian.h"
2122
#include "llvm/Support/Error.h"
2223
#include "llvm/Support/MemoryBuffer.h"
@@ -37,6 +38,11 @@ class InstrProfWriter {
3738
private:
3839
bool Sparse;
3940
StringMap<ProfilingData> FunctionData;
41+
42+
// A map to hold memprof data per function. The lower 64 bits obtained from
43+
// the md5 hash of the function name is used to index into the map.
44+
memprof::FunctionMemProfMap MemProfData;
45+
4046
// An enum describing the attributes of the profile.
4147
InstrProfKind ProfileKind = InstrProfKind::Unknown;
4248
// Use raw pointer here for the incomplete type object.
@@ -57,6 +63,9 @@ class InstrProfWriter {
5763
addRecord(std::move(I), 1, Warn);
5864
}
5965

66+
void addRecord(const ::llvm::memprof::MemProfRecord &MR,
67+
function_ref<void(Error)> Warn);
68+
6069
/// Merge existing function counts from the given writer.
6170
void mergeRecordsFromWriter(InstrProfWriter &&IPW,
6271
function_ref<void(Error)> Warn);
@@ -112,6 +121,8 @@ class InstrProfWriter {
112121
return Error::success();
113122
}
114123

124+
InstrProfKind getProfileKind() const { return ProfileKind; }
125+
115126
// Internal interface for testing purpose only.
116127
void setValueProfDataEndianness(support::endianness Endianness);
117128
void setOutputSparse(bool Sparse);

llvm/include/llvm/ProfileData/MemProf.h

Lines changed: 179 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <string>
66
#include <vector>
77

8+
#include "llvm/ADT/DenseMap.h"
89
#include "llvm/ADT/SmallVector.h"
910
#include "llvm/ProfileData/MemProfData.inc"
1011
#include "llvm/ProfileData/ProfileCommon.h"
@@ -134,25 +135,65 @@ struct PortableMemInfoBlock {
134135
};
135136

136137
struct MemProfRecord {
137-
struct Frame {
138-
std::string Function;
138+
// Describes a call frame for a dynamic allocation context. The contents of
139+
// the frame are populated by symbolizing the stack depot call frame from the
140+
// compiler runtime.
141+
PACKED(struct Frame {
142+
// A uuid (uint64_t) identifying the function. It is obtained by
143+
// llvm::md5(FunctionName) which returns the lower 64 bits.
144+
GlobalValue::GUID Function;
145+
// The source line offset of the call from the beginning of parent function.
139146
uint32_t LineOffset;
147+
// The source column number of the call to help distinguish multiple calls
148+
// on the same line.
140149
uint32_t Column;
150+
// Whether the current frame is inlined.
141151
bool IsInlineFrame;
142152

143-
Frame(std::string Str, uint32_t Off, uint32_t Col, bool Inline)
144-
: Function(std::move(Str)), LineOffset(Off), Column(Col),
145-
IsInlineFrame(Inline) {}
146-
};
153+
Frame(uint64_t Hash, uint32_t Off, uint32_t Col, bool Inline)
154+
: Function(Hash), LineOffset(Off), Column(Col), IsInlineFrame(Inline) {}
147155

156+
bool operator==(const Frame &Other) const {
157+
return Other.Function == Function && Other.LineOffset == LineOffset &&
158+
Other.Column == Column && Other.IsInlineFrame == IsInlineFrame;
159+
}
160+
161+
bool operator!=(const Frame &Other) const { return !operator==(Other); }
162+
163+
// Write the contents of the frame to the ostream \p OS.
164+
void write(raw_ostream & OS) const {
165+
using namespace support;
166+
167+
endian::Writer LE(OS, little);
168+
169+
// If the type of the GlobalValue::GUID changes, then we need to update
170+
// the reader and the writer.
171+
static_assert(std::is_same<GlobalValue::GUID, uint64_t>::value,
172+
"Expect GUID to be uint64_t.");
173+
LE.write<uint64_t>(Function);
174+
175+
LE.write<uint32_t>(LineOffset);
176+
LE.write<uint32_t>(Column);
177+
LE.write<bool>(IsInlineFrame);
178+
}
179+
});
180+
181+
// The dynamic calling context for the allocation.
148182
std::vector<Frame> CallStack;
183+
// The statistics obtained from the runtime for the allocation.
149184
PortableMemInfoBlock Info;
150185

151186
void clear() {
152187
CallStack.clear();
153188
Info.clear();
154189
}
155190

191+
size_t serializedSize() const {
192+
return sizeof(uint64_t) + // The number of frames to serialize.
193+
sizeof(Frame) * CallStack.size() + // The contents of the frames.
194+
PortableMemInfoBlock::serializedSize(); // The size of the payload.
195+
}
196+
156197
// Prints out the contents of the memprof record in YAML.
157198
void print(llvm::raw_ostream &OS) const {
158199
OS << " Callstack:\n";
@@ -168,6 +209,138 @@ struct MemProfRecord {
168209

169210
Info.printYAML(OS);
170211
}
212+
213+
bool operator==(const MemProfRecord &Other) const {
214+
if (Other.Info != Info)
215+
return false;
216+
217+
if (Other.CallStack.size() != CallStack.size())
218+
return false;
219+
220+
for (size_t I = 0; I < Other.CallStack.size(); I++) {
221+
if (Other.CallStack[I] != CallStack[I])
222+
return false;
223+
}
224+
return true;
225+
}
226+
};
227+
228+
// Serializes the memprof records in \p Records to the ostream \p OS based on
229+
// the schema provided in \p Schema.
230+
void serializeRecords(const ArrayRef<MemProfRecord> Records,
231+
const MemProfSchema &Schema, raw_ostream &OS);
232+
233+
// Deserializes memprof records from the Buffer
234+
SmallVector<MemProfRecord, 4> deserializeRecords(const MemProfSchema &Schema,
235+
const unsigned char *Buffer);
236+
237+
// Reads a memprof schema from a buffer. All entries in the buffer are
238+
// interpreted as uint64_t. The first entry in the buffer denotes the number of
239+
// ids in the schema. Subsequent entries are integers which map to memprof::Meta
240+
// enum class entries. After successfully reading the schema, the pointer is one
241+
// byte past the schema contents.
242+
Expected<MemProfSchema> readMemProfSchema(const unsigned char *&Buffer);
243+
244+
using FunctionMemProfMap =
245+
DenseMap<uint64_t, SmallVector<memprof::MemProfRecord, 4>>;
246+
247+
/// Trait for lookups into the on-disk hash table for memprof format in the
248+
/// indexed profile.
249+
class MemProfRecordLookupTrait {
250+
public:
251+
using data_type = ArrayRef<MemProfRecord>;
252+
using internal_key_type = uint64_t;
253+
using external_key_type = uint64_t;
254+
using hash_value_type = uint64_t;
255+
using offset_type = uint64_t;
256+
257+
MemProfRecordLookupTrait() = delete;
258+
MemProfRecordLookupTrait(const MemProfSchema &S) : Schema(S) {}
259+
260+
static bool EqualKey(uint64_t A, uint64_t B) { return A == B; }
261+
static uint64_t GetInternalKey(uint64_t K) { return K; }
262+
static uint64_t GetExternalKey(uint64_t K) { return K; }
263+
264+
hash_value_type ComputeHash(uint64_t K) { return K; }
265+
266+
static std::pair<offset_type, offset_type>
267+
ReadKeyDataLength(const unsigned char *&D) {
268+
using namespace support;
269+
270+
offset_type KeyLen = endian::readNext<offset_type, little, unaligned>(D);
271+
offset_type DataLen = endian::readNext<offset_type, little, unaligned>(D);
272+
return std::make_pair(KeyLen, DataLen);
273+
}
274+
275+
uint64_t ReadKey(const unsigned char *D, offset_type /*Unused*/) {
276+
using namespace support;
277+
return endian::readNext<external_key_type, little, unaligned>(D);
278+
}
279+
280+
data_type ReadData(uint64_t K, const unsigned char *D,
281+
offset_type /*Unused*/) {
282+
Records = deserializeRecords(Schema, D);
283+
return Records;
284+
}
285+
286+
private:
287+
// Holds the memprof schema used to deserialize records.
288+
MemProfSchema Schema;
289+
// Holds the records from one function deserialized from the indexed format.
290+
llvm::SmallVector<MemProfRecord, 4> Records;
291+
};
292+
293+
class MemProfRecordWriterTrait {
294+
public:
295+
using key_type = uint64_t;
296+
using key_type_ref = uint64_t;
297+
298+
using data_type = ArrayRef<MemProfRecord>;
299+
using data_type_ref = ArrayRef<MemProfRecord>;
300+
301+
using hash_value_type = uint64_t;
302+
using offset_type = uint64_t;
303+
304+
// Pointer to the memprof schema to use for the generator. Unlike the reader
305+
// we must use a default constructor with no params for the writer trait so we
306+
// have a public member which must be initialized by the user.
307+
MemProfSchema *Schema = nullptr;
308+
309+
MemProfRecordWriterTrait() = default;
310+
311+
static hash_value_type ComputeHash(key_type_ref K) { return K; }
312+
313+
static std::pair<offset_type, offset_type>
314+
EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
315+
using namespace support;
316+
317+
endian::Writer LE(Out, little);
318+
319+
offset_type N = sizeof(K);
320+
LE.write<offset_type>(N);
321+
322+
offset_type M = 0;
323+
324+
M += sizeof(uint64_t);
325+
for (const auto &Record : V) {
326+
M += Record.serializedSize();
327+
}
328+
329+
LE.write<offset_type>(M);
330+
return std::make_pair(N, M);
331+
}
332+
333+
void EmitKey(raw_ostream &Out, key_type_ref K, offset_type /*Unused*/) {
334+
using namespace support;
335+
endian::Writer LE(Out, little);
336+
LE.write<uint64_t>(K);
337+
}
338+
339+
void EmitData(raw_ostream &Out, key_type_ref /*Unused*/, data_type_ref V,
340+
offset_type /*Unused*/) {
341+
assert(Schema != nullptr && "MemProf schema is not initialized!");
342+
serializeRecords(V, *Schema, Out);
343+
}
171344
};
172345

173346
} // namespace memprof

llvm/include/llvm/ProfileData/MemProfData.inc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
#ifndef LLVM_PROFILEDATA_MEMPROFDATA_INC
2-
#define LLVM_PROFILEDATA_MEMPROFDATA_INC
1+
#ifndef MEMPROF_DATA_INC
2+
#define MEMPROF_DATA_INC
33
/*===-- MemProfData.inc - MemProf profiling runtime structures -*- C++ -*-=== *\
44
|*
55
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.

llvm/include/llvm/ProfileData/RawMemProfReader.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ class RawMemProfReader {
6666
return Iterator(this);
6767
}
6868

69+
// The RawMemProfReader only holds memory profile information.
70+
InstrProfKind getProfileKind() const { return InstrProfKind::MemProf; }
71+
6972
// Constructor for unittests only.
7073
RawMemProfReader(std::unique_ptr<llvm::symbolize::SymbolizableModule> Sym,
7174
llvm::SmallVectorImpl<SegmentEntry> &Seg,

0 commit comments

Comments
 (0)