Skip to content

Commit 27a4f25

Browse files
author
Snehasish Kumar
committed
Reland "[memprof] Store callsite metadata with memprof records."
This reverts commit f4b7944. Reland with underlying msan issue fixed in D122260.
1 parent 61c75eb commit 27a4f25

File tree

12 files changed

+637
-305
lines changed

12 files changed

+637
-305
lines changed

llvm/include/llvm/ProfileData/InstrProfWriter.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
#define LLVM_PROFILEDATA_INSTRPROFWRITER_H
1616

1717
#include "llvm/ADT/DenseMap.h"
18+
#include "llvm/ADT/MapVector.h"
1819
#include "llvm/ADT/StringMap.h"
20+
#include "llvm/IR/GlobalValue.h"
1921
#include "llvm/ProfileData/InstrProf.h"
2022
#include "llvm/ProfileData/MemProf.h"
2123
#include "llvm/Support/Endian.h"
@@ -41,7 +43,7 @@ class InstrProfWriter {
4143

4244
// A map to hold memprof data per function. The lower 64 bits obtained from
4345
// the md5 hash of the function name is used to index into the map.
44-
memprof::FunctionMemProfMap MemProfData;
46+
llvm::MapVector<GlobalValue::GUID, memprof::MemProfRecord> MemProfData;
4547

4648
// An enum describing the attributes of the profile.
4749
InstrProfKind ProfileKind = InstrProfKind::Unknown;
@@ -63,7 +65,8 @@ class InstrProfWriter {
6365
addRecord(std::move(I), 1, Warn);
6466
}
6567

66-
void addRecord(const ::llvm::memprof::MemProfRecord &MR,
68+
void addRecord(const GlobalValue::GUID Id,
69+
const memprof::MemProfRecord &Record,
6770
function_ref<void(Error)> Warn);
6871

6972
/// Merge existing function counts from the given writer.

llvm/include/llvm/ProfileData/MemProf.h

Lines changed: 131 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ struct PortableMemInfoBlock {
8282

8383
// Print out the contents of the MemInfoBlock in YAML format.
8484
void printYAML(raw_ostream &OS) const {
85-
OS << " MemInfoBlock:\n";
85+
OS << " MemInfoBlock:\n";
8686
#define MIBEntryDef(NameTag, Name, Type) \
87-
OS << " " << #Name << ": " << Name << "\n";
87+
OS << " " << #Name << ": " << Name << "\n";
8888
#include "llvm/ProfileData/MIBEntryDef.inc"
8989
#undef MIBEntryDef
9090
}
@@ -133,6 +133,7 @@ struct PortableMemInfoBlock {
133133
#undef MIBEntryDef
134134
};
135135

136+
// Holds the memprof profile information for a function.
136137
struct MemProfRecord {
137138
// Describes a call frame for a dynamic allocation context. The contents of
138139
// the frame are populated by symbolizing the stack depot call frame from the
@@ -193,64 +194,152 @@ struct MemProfRecord {
193194
return sizeof(Frame::Function) + sizeof(Frame::LineOffset) +
194195
sizeof(Frame::Column) + sizeof(Frame::IsInlineFrame);
195196
}
197+
198+
// Print the frame information in YAML format.
199+
void printYAML(raw_ostream &OS) const {
200+
OS << " -\n"
201+
<< " Function: " << Function << "\n"
202+
<< " LineOffset: " << LineOffset << "\n"
203+
<< " Column: " << Column << "\n"
204+
<< " Inline: " << IsInlineFrame << "\n";
205+
}
196206
};
197207

198-
// The dynamic calling context for the allocation.
199-
llvm::SmallVector<Frame> CallStack;
200-
// The statistics obtained from the runtime for the allocation.
201-
PortableMemInfoBlock Info;
208+
struct AllocationInfo {
209+
// The dynamic calling context for the allocation.
210+
llvm::SmallVector<Frame> CallStack;
211+
// The statistics obtained from the runtime for the allocation.
212+
PortableMemInfoBlock Info;
213+
214+
AllocationInfo() = default;
215+
AllocationInfo(ArrayRef<Frame> CS, const MemInfoBlock &MB)
216+
: CallStack(CS.begin(), CS.end()), Info(MB) {}
217+
218+
void printYAML(raw_ostream &OS) const {
219+
OS << " -\n";
220+
OS << " Callstack:\n";
221+
// TODO: Print out the frame on one line with to make it easier for deep
222+
// callstacks once we have a test to check valid YAML is generated.
223+
for (const auto &Frame : CallStack)
224+
Frame.printYAML(OS);
225+
Info.printYAML(OS);
226+
}
227+
228+
size_t serializedSize() const {
229+
return sizeof(uint64_t) + // The number of frames to serialize.
230+
Frame::serializedSize() *
231+
CallStack.size() + // The contents of the frames.
232+
PortableMemInfoBlock::serializedSize(); // The size of the payload.
233+
}
234+
235+
bool operator==(const AllocationInfo &Other) const {
236+
if (Other.Info != Info)
237+
return false;
238+
239+
if (Other.CallStack.size() != CallStack.size())
240+
return false;
241+
242+
for (size_t J = 0; J < Other.CallStack.size(); J++) {
243+
if (Other.CallStack[J] != CallStack[J])
244+
return false;
245+
}
246+
return true;
247+
}
248+
249+
bool operator!=(const AllocationInfo &Other) const {
250+
return !operator==(Other);
251+
}
252+
};
253+
254+
// Memory allocation sites in this function for which we have memory profiling
255+
// data.
256+
llvm::SmallVector<AllocationInfo> AllocSites;
257+
// Holds call sites in this function which are part of some memory allocation
258+
// context. We store this as a list of locations, each with its list of
259+
// inline locations in bottom-up order i.e. from leaf to root. The inline
260+
// location list may include additional entries, users should pick the last
261+
// entry in the list with the same function GUID.
262+
llvm::SmallVector<llvm::SmallVector<Frame>> CallSites;
202263

203264
void clear() {
204-
CallStack.clear();
205-
Info.clear();
265+
AllocSites.clear();
266+
CallSites.clear();
267+
}
268+
269+
void merge(const MemProfRecord &Other) {
270+
// TODO: Filter out duplicates which may occur if multiple memprof profiles
271+
// are merged together using llvm-profdata.
272+
AllocSites.append(Other.AllocSites);
273+
CallSites.append(Other.CallSites);
206274
}
207275

208276
size_t serializedSize() const {
209-
return sizeof(uint64_t) + // The number of frames to serialize.
210-
Frame::serializedSize() *
211-
CallStack.size() + // The contents of the frames.
212-
PortableMemInfoBlock::serializedSize(); // The size of the payload.
277+
size_t Result = sizeof(GlobalValue::GUID);
278+
for (const AllocationInfo &N : AllocSites)
279+
Result += N.serializedSize();
280+
281+
// The number of callsites we have information for.
282+
Result += sizeof(uint64_t);
283+
for (const auto &Frames : CallSites) {
284+
// The number of frames to serialize.
285+
Result += sizeof(uint64_t);
286+
for (const Frame &F : Frames)
287+
Result += F.serializedSize();
288+
}
289+
return Result;
213290
}
214291

215292
// Prints out the contents of the memprof record in YAML.
216293
void print(llvm::raw_ostream &OS) const {
217-
OS << " Callstack:\n";
218-
// TODO: Print out the frame on one line with to make it easier for deep
219-
// callstacks once we have a test to check valid YAML is generated.
220-
for (const auto &Frame : CallStack) {
221-
OS << " -\n"
222-
<< " Function: " << Frame.Function << "\n"
223-
<< " LineOffset: " << Frame.LineOffset << "\n"
224-
<< " Column: " << Frame.Column << "\n"
225-
<< " Inline: " << Frame.IsInlineFrame << "\n";
294+
if (!AllocSites.empty()) {
295+
OS << " AllocSites:\n";
296+
for (const AllocationInfo &N : AllocSites)
297+
N.printYAML(OS);
226298
}
227299

228-
Info.printYAML(OS);
300+
if (!CallSites.empty()) {
301+
OS << " CallSites:\n";
302+
for (const auto &Frames : CallSites) {
303+
for (const auto &F : Frames) {
304+
OS << " -\n";
305+
F.printYAML(OS);
306+
}
307+
}
308+
}
229309
}
230310

231311
bool operator==(const MemProfRecord &Other) const {
232-
if (Other.Info != Info)
312+
if (Other.AllocSites.size() != AllocSites.size())
233313
return false;
234314

235-
if (Other.CallStack.size() != CallStack.size())
315+
if (Other.CallSites.size() != CallSites.size())
236316
return false;
237317

238-
for (size_t I = 0; I < Other.CallStack.size(); I++) {
239-
if (Other.CallStack[I] != CallStack[I])
318+
for (size_t I = 0; I < AllocSites.size(); I++) {
319+
if (AllocSites[I] != Other.AllocSites[I])
320+
return false;
321+
}
322+
323+
for (size_t I = 0; I < CallSites.size(); I++) {
324+
if (CallSites[I] != Other.CallSites[I])
240325
return false;
241326
}
242327
return true;
243328
}
244-
};
245329

246-
// Serializes the memprof records in \p Records to the ostream \p OS based on
247-
// the schema provided in \p Schema.
248-
void serializeRecords(const ArrayRef<MemProfRecord> Records,
249-
const MemProfSchema &Schema, raw_ostream &OS);
330+
// Serializes the memprof records in \p Records to the ostream \p OS based on
331+
// the schema provided in \p Schema.
332+
void serialize(const MemProfSchema &Schema, raw_ostream &OS);
250333

251-
// Deserializes memprof records from the Buffer
252-
SmallVector<MemProfRecord, 4> deserializeRecords(const MemProfSchema &Schema,
253-
const unsigned char *Buffer);
334+
// Deserializes memprof records from the Buffer.
335+
static MemProfRecord deserialize(const MemProfSchema &Schema,
336+
const unsigned char *Buffer);
337+
338+
// Returns the GUID for the function name after canonicalization. For memprof,
339+
// we remove any .llvm suffix added by LTO. MemProfRecords are mapped to
340+
// functions using this GUID.
341+
static GlobalValue::GUID getGUID(const StringRef FunctionName);
342+
};
254343

255344
// Reads a memprof schema from a buffer. All entries in the buffer are
256345
// interpreted as uint64_t. The first entry in the buffer denotes the number of
@@ -259,14 +348,11 @@ SmallVector<MemProfRecord, 4> deserializeRecords(const MemProfSchema &Schema,
259348
// byte past the schema contents.
260349
Expected<MemProfSchema> readMemProfSchema(const unsigned char *&Buffer);
261350

262-
using FunctionMemProfMap =
263-
DenseMap<uint64_t, SmallVector<memprof::MemProfRecord, 4>>;
264-
265351
/// Trait for lookups into the on-disk hash table for memprof format in the
266352
/// indexed profile.
267353
class MemProfRecordLookupTrait {
268354
public:
269-
using data_type = ArrayRef<MemProfRecord>;
355+
using data_type = const MemProfRecord &;
270356
using internal_key_type = uint64_t;
271357
using external_key_type = uint64_t;
272358
using hash_value_type = uint64_t;
@@ -297,24 +383,24 @@ class MemProfRecordLookupTrait {
297383

298384
data_type ReadData(uint64_t K, const unsigned char *D,
299385
offset_type /*Unused*/) {
300-
Records = deserializeRecords(Schema, D);
301-
return Records;
386+
Record = MemProfRecord::deserialize(Schema, D);
387+
return Record;
302388
}
303389

304390
private:
305391
// Holds the memprof schema used to deserialize records.
306392
MemProfSchema Schema;
307393
// Holds the records from one function deserialized from the indexed format.
308-
llvm::SmallVector<MemProfRecord, 4> Records;
394+
MemProfRecord Record;
309395
};
310396

311397
class MemProfRecordWriterTrait {
312398
public:
313399
using key_type = uint64_t;
314400
using key_type_ref = uint64_t;
315401

316-
using data_type = ArrayRef<MemProfRecord>;
317-
using data_type_ref = ArrayRef<MemProfRecord>;
402+
using data_type = MemProfRecord;
403+
using data_type_ref = MemProfRecord &;
318404

319405
using hash_value_type = uint64_t;
320406
using offset_type = uint64_t;
@@ -333,17 +419,9 @@ class MemProfRecordWriterTrait {
333419
using namespace support;
334420

335421
endian::Writer LE(Out, little);
336-
337422
offset_type N = sizeof(K);
338423
LE.write<offset_type>(N);
339-
340-
offset_type M = 0;
341-
342-
M += sizeof(uint64_t);
343-
for (const auto &Record : V) {
344-
M += Record.serializedSize();
345-
}
346-
424+
offset_type M = V.serializedSize();
347425
LE.write<offset_type>(M);
348426
return std::make_pair(N, M);
349427
}
@@ -357,7 +435,7 @@ class MemProfRecordWriterTrait {
357435
void EmitData(raw_ostream &Out, key_type_ref /*Unused*/, data_type_ref V,
358436
offset_type /*Unused*/) {
359437
assert(Schema != nullptr && "MemProf schema is not initialized!");
360-
serializeRecords(V, *Schema, Out);
438+
V.serialize(*Schema, Out);
361439
}
362440
};
363441

llvm/include/llvm/ProfileData/RawMemProfReader.h

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414

1515
#include "llvm/ADT/DenseMap.h"
1616
#include "llvm/ADT/MapVector.h"
17+
#include "llvm/ADT/SetVector.h"
1718
#include "llvm/ADT/StringRef.h"
1819
#include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
1920
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
21+
#include "llvm/IR/GlobalValue.h"
2022
#include "llvm/Object/Binary.h"
2123
#include "llvm/Object/ObjectFile.h"
2224
#include "llvm/ProfileData/InstrProfReader.h"
@@ -57,15 +59,16 @@ class RawMemProfReader {
5759
static Expected<std::unique_ptr<RawMemProfReader>>
5860
create(const Twine &Path, const StringRef ProfiledBinary);
5961

60-
Error readNextRecord(MemProfRecord &Record);
61-
62-
using Iterator = InstrProfIterator<MemProfRecord, RawMemProfReader>;
62+
using GuidMemProfRecordPair = std::pair<GlobalValue::GUID, MemProfRecord>;
63+
using Iterator = InstrProfIterator<GuidMemProfRecordPair, RawMemProfReader>;
6364
Iterator end() { return Iterator(); }
6465
Iterator begin() {
65-
Iter = ProfileData.begin();
66+
Iter = FunctionProfileData.begin();
6667
return Iterator(this);
6768
}
6869

70+
Error readNextRecord(GuidMemProfRecordPair &GuidRecord);
71+
6972
// The RawMemProfReader only holds memory profile information.
7073
InstrProfKind getProfileKind() const { return InstrProfKind::MemProf; }
7174

@@ -75,14 +78,16 @@ class RawMemProfReader {
7578
llvm::MapVector<uint64_t, MemInfoBlock> &Prof,
7679
CallStackMap &SM)
7780
: Symbolizer(std::move(Sym)), SegmentInfo(Seg.begin(), Seg.end()),
78-
ProfileData(Prof), StackMap(SM) {
81+
CallstackProfileData(Prof), StackMap(SM) {
7982
// We don't call initialize here since there is no raw profile to read. The
8083
// test should pass in the raw profile as structured data.
8184

8285
// If there is an error here then the mock symbolizer has not been
8386
// initialized properly.
8487
if (Error E = symbolizeAndFilterStackFrames())
8588
report_fatal_error(std::move(E));
89+
if (Error E = mapRawProfileToRecords())
90+
report_fatal_error(std::move(E));
8691
}
8792

8893
private:
@@ -96,10 +101,12 @@ class RawMemProfReader {
96101
// symbolize or those that belong to the runtime. For profile entries where
97102
// the entire callstack is pruned, we drop the entry from the profile.
98103
Error symbolizeAndFilterStackFrames();
104+
// Construct memprof records for each function and store it in the
105+
// `FunctionProfileData` map. A function may have allocation profile data or
106+
// callsite data or both.
107+
Error mapRawProfileToRecords();
99108

100109
object::SectionedAddress getModuleOffset(uint64_t VirtualAddress);
101-
Error fillRecord(const uint64_t Id, const MemInfoBlock &MIB,
102-
MemProfRecord &Record);
103110
// Prints aggregate counts for each raw profile parsed from the DataBuffer in
104111
// YAML format.
105112
void printSummaries(raw_ostream &OS) const;
@@ -112,15 +119,15 @@ class RawMemProfReader {
112119
llvm::SmallVector<SegmentEntry, 16> SegmentInfo;
113120
// A map from callstack id (same as key in CallStackMap below) to the heap
114121
// information recorded for that allocation context.
115-
llvm::MapVector<uint64_t, MemInfoBlock> ProfileData;
122+
llvm::MapVector<uint64_t, MemInfoBlock> CallstackProfileData;
116123
CallStackMap StackMap;
117124

118125
// Cached symbolization from PC to Frame.
119126
llvm::DenseMap<uint64_t, llvm::SmallVector<MemProfRecord::Frame>>
120127
SymbolizedFrame;
121128

122-
// Iterator to read from the ProfileData MapVector.
123-
llvm::MapVector<uint64_t, MemInfoBlock>::iterator Iter = ProfileData.end();
129+
llvm::MapVector<GlobalValue::GUID, MemProfRecord> FunctionProfileData;
130+
llvm::MapVector<GlobalValue::GUID, MemProfRecord>::iterator Iter;
124131
};
125132

126133
} // namespace memprof

0 commit comments

Comments
 (0)