Skip to content

Commit 44253a9

Browse files
[memprof] Add MemProf version (llvm#86414)
This patch adds a version field to the MemProf section of the indexed profile format, calling the new version "version 1". The existing version is called "version 0". The writer supports both versions via a command-line option: llvm-profdata merge --memprof-version=1 ... The reader supports both versions by automatically detecting the version from the header.
1 parent 17c3f10 commit 44253a9

File tree

5 files changed

+91
-18
lines changed

5 files changed

+91
-18
lines changed

llvm/include/llvm/ProfileData/InstrProfWriter.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,14 @@ class InstrProfWriter {
7575
// deployment of newer versions of llvm-profdata.
7676
bool WritePrevVersion = false;
7777

78+
// The MemProf version we should write.
79+
memprof::IndexedVersion MemProfVersionRequested;
80+
7881
public:
79-
InstrProfWriter(bool Sparse = false,
80-
uint64_t TemporalProfTraceReservoirSize = 0,
81-
uint64_t MaxTemporalProfTraceLength = 0,
82-
bool WritePrevVersion = false);
82+
InstrProfWriter(
83+
bool Sparse = false, uint64_t TemporalProfTraceReservoirSize = 0,
84+
uint64_t MaxTemporalProfTraceLength = 0, bool WritePrevVersion = false,
85+
memprof::IndexedVersion MemProfVersionRequested = memprof::Version0);
8386
~InstrProfWriter();
8487

8588
StringMap<ProfilingData> &getProfileData() { return FunctionData; }

llvm/include/llvm/ProfileData/MemProf.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,20 @@
1616
namespace llvm {
1717
namespace memprof {
1818

19+
// The versions of the indexed MemProf format
20+
enum IndexedVersion : uint64_t {
21+
// Version 0: This version didn't have a version field.
22+
Version0 = 0,
23+
// Version 1: Added a version field to the header.
24+
Version1 = 1,
25+
};
26+
27+
constexpr uint64_t MinimumSupportedVersion = Version0;
28+
constexpr uint64_t MaximumSupportedVersion = Version1;
29+
30+
// Verify that the minimum and maximum satisfy the obvious constraint.
31+
static_assert(MinimumSupportedVersion <= MaximumSupportedVersion);
32+
1933
enum class Meta : uint64_t {
2034
Start = 0,
2135
#define MIBEntryDef(NameTag, Name, Type) NameTag,

llvm/lib/ProfileData/InstrProfReader.cpp

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "llvm/Support/Endian.h"
2525
#include "llvm/Support/Error.h"
2626
#include "llvm/Support/ErrorOr.h"
27+
#include "llvm/Support/FormatVariadic.h"
2728
#include "llvm/Support/MemoryBuffer.h"
2829
#include "llvm/Support/SwapByteOrder.h"
2930
#include "llvm/Support/VirtualFileSystem.h"
@@ -1230,10 +1231,39 @@ Error IndexedInstrProfReader::readHeader() {
12301231
Header->MemProfOffset);
12311232

12321233
const unsigned char *Ptr = Start + MemProfOffset;
1233-
// The value returned from RecordTableGenerator.Emit.
1234-
const uint64_t RecordTableOffset =
1234+
1235+
// Read the first 64-bit word, which may be RecordTableOffset in
1236+
// memprof::MemProfVersion0 or the MemProf version number in
1237+
// memprof::MemProfVersion1.
1238+
const uint64_t FirstWord =
12351239
support::endian::readNext<uint64_t, llvm::endianness::little,
12361240
unaligned>(Ptr);
1241+
1242+
memprof::IndexedVersion Version = memprof::Version0;
1243+
if (FirstWord == memprof::Version1) {
1244+
// Everything is good. We can proceed to deserialize the rest.
1245+
Version = memprof::Version1;
1246+
} else if (FirstWord >= 24) {
1247+
// This is a heuristic/hack to detect memprof::MemProfVersion0,
1248+
// which does not have a version field in the header.
1249+
// In memprof::MemProfVersion0, FirstWord will be RecordTableOffset,
1250+
// which should be at least 24 because of the MemProf header size.
1251+
Version = memprof::Version0;
1252+
} else {
1253+
return make_error<InstrProfError>(
1254+
instrprof_error::unsupported_version,
1255+
formatv("MemProf version {} not supported; "
1256+
"requires version between {} and {}, inclusive",
1257+
FirstWord, memprof::MinimumSupportedVersion,
1258+
memprof::MaximumSupportedVersion));
1259+
}
1260+
1261+
// The value returned from RecordTableGenerator.Emit.
1262+
const uint64_t RecordTableOffset =
1263+
Version == memprof::Version0
1264+
? FirstWord
1265+
: support::endian::readNext<uint64_t, llvm::endianness::little,
1266+
unaligned>(Ptr);
12371267
// The offset in the stream right before invoking
12381268
// FrameTableGenerator.Emit.
12391269
const uint64_t FramePayloadOffset =

llvm/lib/ProfileData/InstrProfWriter.cpp

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "llvm/Support/Endian.h"
2323
#include "llvm/Support/EndianStream.h"
2424
#include "llvm/Support/Error.h"
25+
#include "llvm/Support/FormatVariadic.h"
2526
#include "llvm/Support/MemoryBuffer.h"
2627
#include "llvm/Support/OnDiskHashTable.h"
2728
#include "llvm/Support/raw_ostream.h"
@@ -179,14 +180,15 @@ class InstrProfRecordWriterTrait {
179180

180181
} // end namespace llvm
181182

182-
InstrProfWriter::InstrProfWriter(bool Sparse,
183-
uint64_t TemporalProfTraceReservoirSize,
184-
uint64_t MaxTemporalProfTraceLength,
185-
bool WritePrevVersion)
183+
InstrProfWriter::InstrProfWriter(
184+
bool Sparse, uint64_t TemporalProfTraceReservoirSize,
185+
uint64_t MaxTemporalProfTraceLength, bool WritePrevVersion,
186+
memprof::IndexedVersion MemProfVersionRequested)
186187
: Sparse(Sparse), MaxTemporalProfTraceLength(MaxTemporalProfTraceLength),
187188
TemporalProfTraceReservoirSize(TemporalProfTraceReservoirSize),
188189
InfoObj(new InstrProfRecordWriterTrait()),
189-
WritePrevVersion(WritePrevVersion) {}
190+
WritePrevVersion(WritePrevVersion),
191+
MemProfVersionRequested(MemProfVersionRequested) {}
190192

191193
InstrProfWriter::~InstrProfWriter() { delete InfoObj; }
192194

@@ -516,6 +518,7 @@ Error InstrProfWriter::writeImpl(ProfOStream &OS) {
516518

517519
// Write the MemProf profile data if we have it. This includes a simple schema
518520
// with the format described below followed by the hashtable:
521+
// uint64_t Version
519522
// uint64_t RecordTableOffset = RecordTableGenerator.Emit
520523
// uint64_t FramePayloadOffset = Stream offset before emitting the frame table
521524
// uint64_t FrameTableOffset = FrameTableGenerator.Emit
@@ -528,7 +531,21 @@ Error InstrProfWriter::writeImpl(ProfOStream &OS) {
528531
// OnDiskChainedHashTable MemProfFrameData
529532
uint64_t MemProfSectionStart = 0;
530533
if (static_cast<bool>(ProfileKind & InstrProfKind::MemProf)) {
534+
if (MemProfVersionRequested < memprof::MinimumSupportedVersion ||
535+
MemProfVersionRequested > memprof::MaximumSupportedVersion) {
536+
return make_error<InstrProfError>(
537+
instrprof_error::unsupported_version,
538+
formatv("MemProf version {} not supported; "
539+
"requires version between {} and {}, inclusive",
540+
MemProfVersionRequested, memprof::MinimumSupportedVersion,
541+
memprof::MaximumSupportedVersion));
542+
}
543+
531544
MemProfSectionStart = OS.tell();
545+
546+
if (MemProfVersionRequested >= memprof::Version1)
547+
OS.write(MemProfVersionRequested);
548+
532549
OS.write(0ULL); // Reserve space for the memprof record table offset.
533550
OS.write(0ULL); // Reserve space for the memprof frame payload offset.
534551
OS.write(0ULL); // Reserve space for the memprof frame table offset.
@@ -570,12 +587,13 @@ Error InstrProfWriter::writeImpl(ProfOStream &OS) {
570587

571588
uint64_t FrameTableOffset = FrameTableGenerator.Emit(OS.OS, *FrameWriter);
572589

573-
PatchItem PatchItems[] = {
574-
{MemProfSectionStart, &RecordTableOffset, 1},
575-
{MemProfSectionStart + sizeof(uint64_t), &FramePayloadOffset, 1},
576-
{MemProfSectionStart + 2 * sizeof(uint64_t), &FrameTableOffset, 1},
577-
};
578-
OS.patch(PatchItems);
590+
uint64_t Header[] = {RecordTableOffset, FramePayloadOffset,
591+
FrameTableOffset};
592+
uint64_t HeaderUpdatePos = MemProfSectionStart;
593+
if (MemProfVersionRequested >= memprof::Version1)
594+
// The updates go just after the version field.
595+
HeaderUpdatePos += sizeof(uint64_t);
596+
OS.patch({{HeaderUpdatePos, Header, std::size(Header)}});
579597
}
580598

581599
// BinaryIdSection has two parts:

llvm/tools/llvm-profdata/llvm-profdata.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,13 @@ cl::opt<bool> DoWritePrevVersion(
300300
cl::desc("Write the previous version of indexed format, to enable "
301301
"some forward compatibility."));
302302

303+
cl::opt<memprof::IndexedVersion> MemProfVersionRequested(
304+
"memprof-version", cl::Hidden, cl::sub(MergeSubcommand),
305+
cl::desc("Specify the version of the memprof format to use"),
306+
cl::init(memprof::Version0),
307+
cl::values(clEnumValN(memprof::Version0, "0", "version 0"),
308+
clEnumValN(memprof::Version1, "1", "version 1")));
309+
303310
// Options specific to overlap subcommand.
304311
cl::opt<std::string> BaseFilename(cl::Positional, cl::Required,
305312
cl::desc("<base profile file>"),
@@ -588,7 +595,8 @@ struct WriterContext {
588595
WriterContext(bool IsSparse, std::mutex &ErrLock,
589596
SmallSet<instrprof_error, 4> &WriterErrorCodes,
590597
uint64_t ReservoirSize = 0, uint64_t MaxTraceLength = 0)
591-
: Writer(IsSparse, ReservoirSize, MaxTraceLength, DoWritePrevVersion),
598+
: Writer(IsSparse, ReservoirSize, MaxTraceLength, DoWritePrevVersion,
599+
MemProfVersionRequested),
592600
ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) {}
593601
};
594602

0 commit comments

Comments
 (0)