Skip to content

Commit 99b9ab4

Browse files
[memprof] Reorder MemProf sections in profile (#93640)
This patch teaches the V3 format to serialize Frames, call stacks, and IndexedMemProfRecords, in that order. I'm planning to use linear IDs for Frames. That is, Frames will be numbered 0, 1, 2, and so on in the order we serialize them. In turn, we will seialize the call stacks in terms of those linear IDs. Likewise, I'm planning to use linear IDs for call stacks and then serialize IndexedMemProfRecords in terms of those linear IDs for call stacks. With the new order, we can successively free data structures as we serialize them. That is, once we serialize Frames, we can free the Frames' data proper and just retain mappings from FrameIds to linear IDs. A similar story applies to call stacks.
1 parent b3bbb2d commit 99b9ab4

File tree

3 files changed

+111
-40
lines changed

3 files changed

+111
-40
lines changed

llvm/include/llvm/ProfileData/InstrProfReader.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,11 @@ class IndexedMemProfReader {
660660
/// MemProf call stack data on-disk indexed via call stack id.
661661
std::unique_ptr<MemProfCallStackHashTable> MemProfCallStackTable;
662662

663+
Error deserializeV012(const unsigned char *Start, const unsigned char *Ptr,
664+
uint64_t FirstWord, memprof::IndexedVersion Version);
665+
Error deserializeV3(const unsigned char *Start, const unsigned char *Ptr,
666+
memprof::IndexedVersion Version);
667+
663668
public:
664669
IndexedMemProfReader() = default;
665670

llvm/lib/ProfileData/InstrProfReader.cpp

Lines changed: 95 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,35 +1202,10 @@ IndexedInstrProfReader::readSummary(IndexedInstrProf::ProfVersion Version,
12021202
}
12031203
}
12041204

1205-
Error IndexedMemProfReader::deserialize(const unsigned char *Start,
1206-
uint64_t MemProfOffset) {
1207-
const unsigned char *Ptr = Start + MemProfOffset;
1208-
1209-
// Read the first 64-bit word, which may be RecordTableOffset in
1210-
// memprof::MemProfVersion0 or the MemProf version number in
1211-
// memprof::MemProfVersion1 and above.
1212-
const uint64_t FirstWord =
1213-
support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
1214-
1215-
if (FirstWord == memprof::Version1 || FirstWord == memprof::Version2 ||
1216-
FirstWord == memprof::Version3) {
1217-
// Everything is good. We can proceed to deserialize the rest.
1218-
Version = static_cast<memprof::IndexedVersion>(FirstWord);
1219-
} else if (FirstWord >= 24) {
1220-
// This is a heuristic/hack to detect memprof::MemProfVersion0,
1221-
// which does not have a version field in the header.
1222-
// In memprof::MemProfVersion0, FirstWord will be RecordTableOffset,
1223-
// which should be at least 24 because of the MemProf header size.
1224-
Version = memprof::Version0;
1225-
} else {
1226-
return make_error<InstrProfError>(
1227-
instrprof_error::unsupported_version,
1228-
formatv("MemProf version {} not supported; "
1229-
"requires version between {} and {}, inclusive",
1230-
FirstWord, memprof::MinimumSupportedVersion,
1231-
memprof::MaximumSupportedVersion));
1232-
}
1233-
1205+
Error IndexedMemProfReader::deserializeV012(const unsigned char *Start,
1206+
const unsigned char *Ptr,
1207+
uint64_t FirstWord,
1208+
memprof::IndexedVersion Version) {
12341209
// The value returned from RecordTableGenerator.Emit.
12351210
const uint64_t RecordTableOffset =
12361211
Version == memprof::Version0
@@ -1280,6 +1255,97 @@ Error IndexedMemProfReader::deserialize(const unsigned char *Start,
12801255
/*Payload=*/Start + CallStackPayloadOffset,
12811256
/*Base=*/Start));
12821257

1258+
return Error::success();
1259+
}
1260+
1261+
Error IndexedMemProfReader::deserializeV3(const unsigned char *Start,
1262+
const unsigned char *Ptr,
1263+
memprof::IndexedVersion Version) {
1264+
// The value returned from FrameTableGenerator.Emit.
1265+
const uint64_t FrameTableOffset =
1266+
support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
1267+
// The offset in the stream right before invoking
1268+
// CallStackTableGenerator.Emit.
1269+
const uint64_t CallStackPayloadOffset =
1270+
support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
1271+
// The value returned from CallStackTableGenerator.Emit.
1272+
const uint64_t CallStackTableOffset =
1273+
support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
1274+
// The offset in the stream right before invoking RecordTableGenerator.Emit.
1275+
const uint64_t RecordPayloadOffset =
1276+
support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
1277+
// The value returned from RecordTableGenerator.Emit.
1278+
const uint64_t RecordTableOffset =
1279+
support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
1280+
1281+
// Read the schema.
1282+
auto SchemaOr = memprof::readMemProfSchema(Ptr);
1283+
if (!SchemaOr)
1284+
return SchemaOr.takeError();
1285+
Schema = SchemaOr.get();
1286+
1287+
// Initialize the frame table reader with the payload and bucket offsets.
1288+
MemProfFrameTable.reset(MemProfFrameHashTable::Create(
1289+
/*Buckets=*/Start + FrameTableOffset,
1290+
/*Payload=*/Ptr,
1291+
/*Base=*/Start));
1292+
1293+
MemProfCallStackTable.reset(MemProfCallStackHashTable::Create(
1294+
/*Buckets=*/Start + CallStackTableOffset,
1295+
/*Payload=*/Start + CallStackPayloadOffset,
1296+
/*Base=*/Start));
1297+
1298+
// Now initialize the table reader with a pointer into data buffer.
1299+
MemProfRecordTable.reset(MemProfRecordHashTable::Create(
1300+
/*Buckets=*/Start + RecordTableOffset,
1301+
/*Payload=*/Start + RecordPayloadOffset,
1302+
/*Base=*/Start, memprof::RecordLookupTrait(Version, Schema)));
1303+
1304+
return Error::success();
1305+
}
1306+
1307+
Error IndexedMemProfReader::deserialize(const unsigned char *Start,
1308+
uint64_t MemProfOffset) {
1309+
const unsigned char *Ptr = Start + MemProfOffset;
1310+
1311+
// Read the first 64-bit word, which may be RecordTableOffset in
1312+
// memprof::MemProfVersion0 or the MemProf version number in
1313+
// memprof::MemProfVersion1 and above.
1314+
const uint64_t FirstWord =
1315+
support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
1316+
1317+
if (FirstWord == memprof::Version1 || FirstWord == memprof::Version2 ||
1318+
FirstWord == memprof::Version3) {
1319+
// Everything is good. We can proceed to deserialize the rest.
1320+
Version = static_cast<memprof::IndexedVersion>(FirstWord);
1321+
} else if (FirstWord >= 24) {
1322+
// This is a heuristic/hack to detect memprof::MemProfVersion0,
1323+
// which does not have a version field in the header.
1324+
// In memprof::MemProfVersion0, FirstWord will be RecordTableOffset,
1325+
// which should be at least 24 because of the MemProf header size.
1326+
Version = memprof::Version0;
1327+
} else {
1328+
return make_error<InstrProfError>(
1329+
instrprof_error::unsupported_version,
1330+
formatv("MemProf version {} not supported; "
1331+
"requires version between {} and {}, inclusive",
1332+
FirstWord, memprof::MinimumSupportedVersion,
1333+
memprof::MaximumSupportedVersion));
1334+
}
1335+
1336+
switch (Version) {
1337+
case memprof::Version0:
1338+
case memprof::Version1:
1339+
case memprof::Version2:
1340+
if (Error E = deserializeV012(Start, Ptr, FirstWord, Version))
1341+
return E;
1342+
break;
1343+
case memprof::Version3:
1344+
if (Error E = deserializeV3(Start, Ptr, Version))
1345+
return E;
1346+
break;
1347+
}
1348+
12831349
#ifdef EXPENSIVE_CHECKS
12841350
// Go through all the records and verify that CSId has been correctly
12851351
// populated. Do this only under EXPENSIVE_CHECKS. Otherwise, we

llvm/lib/ProfileData/InstrProfWriter.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -619,48 +619,48 @@ static Error writeMemProfV2(ProfOStream &OS,
619619

620620
// Write out MemProf Version3 as follows:
621621
// uint64_t Version
622-
// uint64_t RecordTableOffset = RecordTableGenerator.Emit
623-
// uint64_t FramePayloadOffset = Offset for the frame payload
624622
// uint64_t FrameTableOffset = FrameTableGenerator.Emit
625623
// uint64_t CallStackPayloadOffset = Offset for the call stack payload
626624
// uint64_t CallStackTableOffset = CallStackTableGenerator.Emit
625+
// uint64_t RecordPayloadOffset = Offset for the record payload
626+
// uint64_t RecordTableOffset = RecordTableGenerator.Emit
627627
// uint64_t Num schema entries
628628
// uint64_t Schema entry 0
629629
// uint64_t Schema entry 1
630630
// ....
631631
// uint64_t Schema entry N - 1
632-
// OnDiskChainedHashTable MemProfRecordData
633632
// OnDiskChainedHashTable MemProfFrameData
634633
// OnDiskChainedHashTable MemProfCallStackData
634+
// OnDiskChainedHashTable MemProfRecordData
635635
static Error writeMemProfV3(ProfOStream &OS,
636636
memprof::IndexedMemProfData &MemProfData,
637637
bool MemProfFullSchema) {
638638
OS.write(memprof::Version3);
639639
uint64_t HeaderUpdatePos = OS.tell();
640-
OS.write(0ULL); // Reserve space for the memprof record table offset.
641-
OS.write(0ULL); // Reserve space for the memprof frame payload offset.
642640
OS.write(0ULL); // Reserve space for the memprof frame table offset.
643641
OS.write(0ULL); // Reserve space for the memprof call stack payload offset.
644642
OS.write(0ULL); // Reserve space for the memprof call stack table offset.
643+
OS.write(0ULL); // Reserve space for the memprof record payload offset.
644+
OS.write(0ULL); // Reserve space for the memprof record table offset.
645645

646646
auto Schema = memprof::getHotColdSchema();
647647
if (MemProfFullSchema)
648648
Schema = memprof::getFullSchema();
649649
writeMemProfSchema(OS, Schema);
650650

651-
uint64_t RecordTableOffset = writeMemProfRecords(OS, MemProfData.RecordData,
652-
&Schema, memprof::Version3);
653-
654-
uint64_t FramePayloadOffset = OS.tell();
655651
uint64_t FrameTableOffset = writeMemProfFrames(OS, MemProfData.FrameData);
656652

657653
uint64_t CallStackPayloadOffset = OS.tell();
658654
uint64_t CallStackTableOffset =
659655
writeMemProfCallStacks(OS, MemProfData.CallStackData);
660656

657+
uint64_t RecordPayloadOffset = OS.tell();
658+
uint64_t RecordTableOffset = writeMemProfRecords(OS, MemProfData.RecordData,
659+
&Schema, memprof::Version3);
660+
661661
uint64_t Header[] = {
662-
RecordTableOffset, FramePayloadOffset, FrameTableOffset,
663-
CallStackPayloadOffset, CallStackTableOffset,
662+
FrameTableOffset, CallStackPayloadOffset, CallStackTableOffset,
663+
RecordPayloadOffset, RecordTableOffset,
664664
};
665665
OS.patch({{HeaderUpdatePos, Header, std::size(Header)}});
666666

0 commit comments

Comments
 (0)