Skip to content

Commit fd2a5c4

Browse files
[memprof] Introduce writeMemProf (NFC) (llvm#87698)
This patch refactors the serialization of MemProf data to a switch statement style: switch (Version) { case Version0: return ...; case Version1: return ...; } just like IndexedMemProfRecord::serialize. A reasonable amount of code is shared and factored out to helper functions between writeMemProfV0 and writeMemProfV1 to the extent that doens't hamper readability.
1 parent 74373c1 commit fd2a5c4

File tree

1 file changed

+142
-76
lines changed

1 file changed

+142
-76
lines changed

llvm/lib/ProfileData/InstrProfWriter.cpp

Lines changed: 142 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,144 @@ static void setSummary(IndexedInstrProf::Summary *TheSummary,
414414
TheSummary->setEntry(I, Res[I]);
415415
}
416416

417+
// Serialize Schema.
418+
static void writeMemProfSchema(ProfOStream &OS,
419+
const memprof::MemProfSchema &Schema) {
420+
OS.write(static_cast<uint64_t>(Schema.size()));
421+
for (const auto Id : Schema)
422+
OS.write(static_cast<uint64_t>(Id));
423+
}
424+
425+
// Serialize MemProfRecordData. Return RecordTableOffset.
426+
static uint64_t writeMemProfRecords(
427+
ProfOStream &OS,
428+
llvm::MapVector<GlobalValue::GUID, memprof::IndexedMemProfRecord>
429+
&MemProfRecordData,
430+
memprof::MemProfSchema *Schema) {
431+
auto RecordWriter =
432+
std::make_unique<memprof::RecordWriterTrait>(memprof::Version1);
433+
RecordWriter->Schema = Schema;
434+
OnDiskChainedHashTableGenerator<memprof::RecordWriterTrait>
435+
RecordTableGenerator;
436+
for (auto &I : MemProfRecordData) {
437+
// Insert the key (func hash) and value (memprof record).
438+
RecordTableGenerator.insert(I.first, I.second, *RecordWriter.get());
439+
}
440+
// Release the memory of this MapVector as it is no longer needed.
441+
MemProfRecordData.clear();
442+
443+
// The call to Emit invokes RecordWriterTrait::EmitData which destructs
444+
// the memprof record copies owned by the RecordTableGenerator. This works
445+
// because the RecordTableGenerator is not used after this point.
446+
return RecordTableGenerator.Emit(OS.OS, *RecordWriter);
447+
}
448+
449+
// Serialize MemProfFrameData. Return FrameTableOffset.
450+
static uint64_t writeMemProfFrames(
451+
ProfOStream &OS,
452+
llvm::MapVector<memprof::FrameId, memprof::Frame> &MemProfFrameData) {
453+
auto FrameWriter = std::make_unique<memprof::FrameWriterTrait>();
454+
OnDiskChainedHashTableGenerator<memprof::FrameWriterTrait>
455+
FrameTableGenerator;
456+
for (auto &I : MemProfFrameData) {
457+
// Insert the key (frame id) and value (frame contents).
458+
FrameTableGenerator.insert(I.first, I.second);
459+
}
460+
// Release the memory of this MapVector as it is no longer needed.
461+
MemProfFrameData.clear();
462+
463+
return FrameTableGenerator.Emit(OS.OS, *FrameWriter);
464+
}
465+
466+
static Error writeMemProfV0(
467+
ProfOStream &OS,
468+
llvm::MapVector<GlobalValue::GUID, memprof::IndexedMemProfRecord>
469+
&MemProfRecordData,
470+
llvm::MapVector<memprof::FrameId, memprof::Frame> &MemProfFrameData) {
471+
uint64_t HeaderUpdatePos = OS.tell();
472+
OS.write(0ULL); // Reserve space for the memprof record table offset.
473+
OS.write(0ULL); // Reserve space for the memprof frame payload offset.
474+
OS.write(0ULL); // Reserve space for the memprof frame table offset.
475+
476+
auto Schema = memprof::PortableMemInfoBlock::getSchema();
477+
writeMemProfSchema(OS, Schema);
478+
479+
uint64_t RecordTableOffset =
480+
writeMemProfRecords(OS, MemProfRecordData, &Schema);
481+
482+
uint64_t FramePayloadOffset = OS.tell();
483+
uint64_t FrameTableOffset = writeMemProfFrames(OS, MemProfFrameData);
484+
485+
uint64_t Header[] = {RecordTableOffset, FramePayloadOffset, FrameTableOffset};
486+
OS.patch({{HeaderUpdatePos, Header, std::size(Header)}});
487+
488+
return Error::success();
489+
}
490+
491+
static Error writeMemProfV1(
492+
ProfOStream &OS,
493+
llvm::MapVector<GlobalValue::GUID, memprof::IndexedMemProfRecord>
494+
&MemProfRecordData,
495+
llvm::MapVector<memprof::FrameId, memprof::Frame> &MemProfFrameData) {
496+
OS.write(memprof::Version0);
497+
uint64_t HeaderUpdatePos = OS.tell();
498+
OS.write(0ULL); // Reserve space for the memprof record table offset.
499+
OS.write(0ULL); // Reserve space for the memprof frame payload offset.
500+
OS.write(0ULL); // Reserve space for the memprof frame table offset.
501+
502+
auto Schema = memprof::PortableMemInfoBlock::getSchema();
503+
writeMemProfSchema(OS, Schema);
504+
505+
uint64_t RecordTableOffset =
506+
writeMemProfRecords(OS, MemProfRecordData, &Schema);
507+
508+
uint64_t FramePayloadOffset = OS.tell();
509+
uint64_t FrameTableOffset = writeMemProfFrames(OS, MemProfFrameData);
510+
511+
uint64_t Header[] = {RecordTableOffset, FramePayloadOffset, FrameTableOffset};
512+
OS.patch({{HeaderUpdatePos, Header, std::size(Header)}});
513+
514+
return Error::success();
515+
}
516+
517+
// The MemProf profile data includes a simple schema
518+
// with the format described below followed by the hashtable:
519+
// uint64_t Version
520+
// uint64_t RecordTableOffset = RecordTableGenerator.Emit
521+
// uint64_t FramePayloadOffset = Stream offset before emitting the frame table
522+
// uint64_t FrameTableOffset = FrameTableGenerator.Emit
523+
// uint64_t Num schema entries
524+
// uint64_t Schema entry 0
525+
// uint64_t Schema entry 1
526+
// ....
527+
// uint64_t Schema entry N - 1
528+
// OnDiskChainedHashTable MemProfRecordData
529+
// OnDiskChainedHashTable MemProfFrameData
530+
static Error writeMemProf(
531+
ProfOStream &OS,
532+
llvm::MapVector<GlobalValue::GUID, memprof::IndexedMemProfRecord>
533+
&MemProfRecordData,
534+
llvm::MapVector<memprof::FrameId, memprof::Frame> &MemProfFrameData,
535+
memprof::IndexedVersion MemProfVersionRequested) {
536+
537+
switch (MemProfVersionRequested) {
538+
case memprof::Version0:
539+
return writeMemProfV0(OS, MemProfRecordData, MemProfFrameData);
540+
case memprof::Version1:
541+
return writeMemProfV1(OS, MemProfRecordData, MemProfFrameData);
542+
case memprof::Version2:
543+
// TODO: Implement. Fall through to the error handling below for now.
544+
break;
545+
}
546+
547+
return make_error<InstrProfError>(
548+
instrprof_error::unsupported_version,
549+
formatv("MemProf version {} not supported; "
550+
"requires version between {} and {}, inclusive",
551+
MemProfVersionRequested, memprof::MinimumSupportedVersion,
552+
memprof::MaximumSupportedVersion));
553+
}
554+
417555
Error InstrProfWriter::writeImpl(ProfOStream &OS) {
418556
using namespace IndexedInstrProf;
419557
using namespace support;
@@ -517,85 +655,13 @@ Error InstrProfWriter::writeImpl(ProfOStream &OS) {
517655
// Write the hash table.
518656
uint64_t HashTableStart = Generator.Emit(OS.OS, *InfoObj);
519657

520-
// Write the MemProf profile data if we have it. This includes a simple schema
521-
// with the format described below followed by the hashtable:
522-
// uint64_t Version
523-
// uint64_t RecordTableOffset = RecordTableGenerator.Emit
524-
// uint64_t FramePayloadOffset = Stream offset before emitting the frame table
525-
// uint64_t FrameTableOffset = FrameTableGenerator.Emit
526-
// uint64_t Num schema entries
527-
// uint64_t Schema entry 0
528-
// uint64_t Schema entry 1
529-
// ....
530-
// uint64_t Schema entry N - 1
531-
// OnDiskChainedHashTable MemProfRecordData
532-
// OnDiskChainedHashTable MemProfFrameData
658+
// Write the MemProf profile data if we have it.
533659
uint64_t MemProfSectionStart = 0;
534660
if (static_cast<bool>(ProfileKind & InstrProfKind::MemProf)) {
535-
if (MemProfVersionRequested < memprof::MinimumSupportedVersion ||
536-
MemProfVersionRequested > memprof::MaximumSupportedVersion) {
537-
return make_error<InstrProfError>(
538-
instrprof_error::unsupported_version,
539-
formatv("MemProf version {} not supported; "
540-
"requires version between {} and {}, inclusive",
541-
MemProfVersionRequested, memprof::MinimumSupportedVersion,
542-
memprof::MaximumSupportedVersion));
543-
}
544-
545661
MemProfSectionStart = OS.tell();
546-
547-
if (MemProfVersionRequested >= memprof::Version1)
548-
OS.write(MemProfVersionRequested);
549-
550-
OS.write(0ULL); // Reserve space for the memprof record table offset.
551-
OS.write(0ULL); // Reserve space for the memprof frame payload offset.
552-
OS.write(0ULL); // Reserve space for the memprof frame table offset.
553-
554-
auto Schema = memprof::PortableMemInfoBlock::getSchema();
555-
OS.write(static_cast<uint64_t>(Schema.size()));
556-
for (const auto Id : Schema) {
557-
OS.write(static_cast<uint64_t>(Id));
558-
}
559-
560-
auto RecordWriter =
561-
std::make_unique<memprof::RecordWriterTrait>(memprof::Version1);
562-
RecordWriter->Schema = &Schema;
563-
OnDiskChainedHashTableGenerator<memprof::RecordWriterTrait>
564-
RecordTableGenerator;
565-
for (auto &I : MemProfRecordData) {
566-
// Insert the key (func hash) and value (memprof record).
567-
RecordTableGenerator.insert(I.first, I.second, *RecordWriter.get());
568-
}
569-
// Release the memory of this MapVector as it is no longer needed.
570-
MemProfRecordData.clear();
571-
572-
// The call to Emit invokes RecordWriterTrait::EmitData which destructs
573-
// the memprof record copies owned by the RecordTableGenerator. This works
574-
// because the RecordTableGenerator is not used after this point.
575-
uint64_t RecordTableOffset =
576-
RecordTableGenerator.Emit(OS.OS, *RecordWriter);
577-
578-
uint64_t FramePayloadOffset = OS.tell();
579-
580-
auto FrameWriter = std::make_unique<memprof::FrameWriterTrait>();
581-
OnDiskChainedHashTableGenerator<memprof::FrameWriterTrait>
582-
FrameTableGenerator;
583-
for (auto &I : MemProfFrameData) {
584-
// Insert the key (frame id) and value (frame contents).
585-
FrameTableGenerator.insert(I.first, I.second);
586-
}
587-
// Release the memory of this MapVector as it is no longer needed.
588-
MemProfFrameData.clear();
589-
590-
uint64_t FrameTableOffset = FrameTableGenerator.Emit(OS.OS, *FrameWriter);
591-
592-
uint64_t Header[] = {RecordTableOffset, FramePayloadOffset,
593-
FrameTableOffset};
594-
uint64_t HeaderUpdatePos = MemProfSectionStart;
595-
if (MemProfVersionRequested >= memprof::Version1)
596-
// The updates go just after the version field.
597-
HeaderUpdatePos += sizeof(uint64_t);
598-
OS.patch({{HeaderUpdatePos, Header, std::size(Header)}});
662+
if (auto E = writeMemProf(OS, MemProfRecordData, MemProfFrameData,
663+
MemProfVersionRequested))
664+
return E;
599665
}
600666

601667
// BinaryIdSection has two parts:

0 commit comments

Comments
 (0)