[ctx_prof] Make the profile output analyzable by llvm-bcanalyzer (#99563)
This requires output-ing a "Magic" 4-byte header. We also emit a block info block, to describe our blocks and records. The output of `llvm-bcanalyzer` would look like:
```
<BLOCKINFO_BLOCK/>
<Metadata NumWords=17 BlockCodeSize=2>
<Version op0=1/>
<Context NumWords=13 BlockCodeSize=2>
<GUID op0=2/>
<Counters op0=1 op1=2 op2=3/>
```
Instead of having `Unknown` for block and record IDs.
This commit is contained in:
@@ -73,7 +73,8 @@ public:
|
||||
};
|
||||
|
||||
class PGOCtxProfileReader final {
|
||||
BitstreamCursor &Cursor;
|
||||
StringRef Magic;
|
||||
BitstreamCursor Cursor;
|
||||
Expected<BitstreamEntry> advance();
|
||||
Error readMetadata();
|
||||
Error wrongValue(const Twine &);
|
||||
@@ -84,7 +85,9 @@ class PGOCtxProfileReader final {
|
||||
bool canReadContext();
|
||||
|
||||
public:
|
||||
PGOCtxProfileReader(BitstreamCursor &Cursor) : Cursor(Cursor) {}
|
||||
PGOCtxProfileReader(StringRef Buffer)
|
||||
: Magic(Buffer.substr(0, PGOCtxProfileWriter::ContainerMagic.size())),
|
||||
Cursor(Buffer.substr(PGOCtxProfileWriter::ContainerMagic.size())) {}
|
||||
|
||||
Expected<std::map<GlobalValue::GUID, PGOContextualProfile>> loadContexts();
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#ifndef LLVM_PROFILEDATA_PGOCTXPROFWRITER_H_
|
||||
#define LLVM_PROFILEDATA_PGOCTXPROFWRITER_H_
|
||||
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Bitstream/BitCodeEnums.h"
|
||||
#include "llvm/Bitstream/BitstreamWriter.h"
|
||||
#include "llvm/ProfileData/CtxInstrContextNode.h"
|
||||
@@ -68,15 +69,7 @@ class PGOCtxProfileWriter final {
|
||||
|
||||
public:
|
||||
PGOCtxProfileWriter(raw_ostream &Out,
|
||||
std::optional<unsigned> VersionOverride = std::nullopt)
|
||||
: Writer(Out, 0) {
|
||||
Writer.EnterSubblock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID,
|
||||
CodeLen);
|
||||
const auto Version = VersionOverride ? *VersionOverride : CurrentVersion;
|
||||
Writer.EmitRecord(PGOCtxProfileRecords::Version,
|
||||
SmallVector<unsigned, 1>({Version}));
|
||||
}
|
||||
|
||||
std::optional<unsigned> VersionOverride = std::nullopt);
|
||||
~PGOCtxProfileWriter() { Writer.ExitBlock(); }
|
||||
|
||||
void write(const ctx_profile::ContextNode &);
|
||||
@@ -85,6 +78,7 @@ public:
|
||||
static constexpr unsigned CodeLen = 2;
|
||||
static constexpr uint32_t CurrentVersion = 1;
|
||||
static constexpr unsigned VBREncodingBits = 6;
|
||||
static constexpr StringRef ContainerMagic = "CTXP";
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ProfileData/PGOCtxProfReader.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Bitstream/BitCodeEnums.h"
|
||||
#include "llvm/Bitstream/BitstreamReader.h"
|
||||
#include "llvm/ProfileData/InstrProf.h"
|
||||
@@ -139,6 +140,20 @@ PGOCtxProfileReader::readContext(bool ExpectIndex) {
|
||||
}
|
||||
|
||||
Error PGOCtxProfileReader::readMetadata() {
|
||||
if (Magic.size() < PGOCtxProfileWriter::ContainerMagic.size() ||
|
||||
Magic != PGOCtxProfileWriter::ContainerMagic)
|
||||
return make_error<InstrProfError>(instrprof_error::invalid_prof,
|
||||
"Invalid magic");
|
||||
|
||||
BitstreamEntry Entry;
|
||||
RET_ON_ERR(Cursor.advance().moveInto(Entry));
|
||||
if (Entry.Kind != BitstreamEntry::SubBlock ||
|
||||
Entry.ID != bitc::BLOCKINFO_BLOCK_ID)
|
||||
return unsupported("Expected Block ID");
|
||||
// We don't need the blockinfo to read the rest, it's metadata usable for e.g.
|
||||
// llvm-bcanalyzer.
|
||||
RET_ON_ERR(Cursor.SkipBlock());
|
||||
|
||||
EXPECT_OR_RET(Blk, advance());
|
||||
if (Blk->Kind != BitstreamEntry::SubBlock)
|
||||
return unsupported("Expected Version record");
|
||||
|
||||
@@ -16,6 +16,40 @@
|
||||
using namespace llvm;
|
||||
using namespace llvm::ctx_profile;
|
||||
|
||||
PGOCtxProfileWriter::PGOCtxProfileWriter(
|
||||
raw_ostream &Out, std::optional<unsigned> VersionOverride)
|
||||
: Writer(Out, 0) {
|
||||
static_assert(ContainerMagic.size() == 4);
|
||||
Out.write(ContainerMagic.data(), ContainerMagic.size());
|
||||
Writer.EnterBlockInfoBlock();
|
||||
{
|
||||
auto DescribeBlock = [&](unsigned ID, StringRef Name) {
|
||||
Writer.EmitRecord(bitc::BLOCKINFO_CODE_SETBID,
|
||||
SmallVector<unsigned, 1>{ID});
|
||||
Writer.EmitRecord(bitc::BLOCKINFO_CODE_BLOCKNAME,
|
||||
llvm::arrayRefFromStringRef(Name));
|
||||
};
|
||||
SmallVector<uint64_t, 16> Data;
|
||||
auto DescribeRecord = [&](unsigned RecordID, StringRef Name) {
|
||||
Data.clear();
|
||||
Data.push_back(RecordID);
|
||||
llvm::append_range(Data, Name);
|
||||
Writer.EmitRecord(bitc::BLOCKINFO_CODE_SETRECORDNAME, Data);
|
||||
};
|
||||
DescribeBlock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID, "Metadata");
|
||||
DescribeRecord(PGOCtxProfileRecords::Version, "Version");
|
||||
DescribeBlock(PGOCtxProfileBlockIDs::ContextNodeBlockID, "Context");
|
||||
DescribeRecord(PGOCtxProfileRecords::Guid, "GUID");
|
||||
DescribeRecord(PGOCtxProfileRecords::CalleeIndex, "CalleeIndex");
|
||||
DescribeRecord(PGOCtxProfileRecords::Counters, "Counters");
|
||||
}
|
||||
Writer.ExitBlock();
|
||||
Writer.EnterSubblock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID, CodeLen);
|
||||
const auto Version = VersionOverride ? *VersionOverride : CurrentVersion;
|
||||
Writer.EmitRecord(PGOCtxProfileRecords::Version,
|
||||
SmallVector<unsigned, 1>({Version}));
|
||||
}
|
||||
|
||||
void PGOCtxProfileWriter::writeCounters(const ContextNode &Node) {
|
||||
Writer.EmitCode(bitc::UNABBREV_RECORD);
|
||||
Writer.EmitVBR(PGOCtxProfileRecords::Counters, VBREncodingBits);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Bitstream/BitstreamReader.h"
|
||||
#include "llvm/Bitcode/BitcodeAnalyzer.h"
|
||||
#include "llvm/ProfileData/CtxInstrContextNode.h"
|
||||
#include "llvm/ProfileData/PGOCtxProfReader.h"
|
||||
#include "llvm/ProfileData/PGOCtxProfWriter.h"
|
||||
@@ -106,8 +106,20 @@ TEST_F(PGOCtxProfRWTest, RoundTrip) {
|
||||
MemoryBuffer::getFile(ProfileFile.path());
|
||||
ASSERT_TRUE(!!MB);
|
||||
ASSERT_NE(*MB, nullptr);
|
||||
BitstreamCursor Cursor((*MB)->getBuffer());
|
||||
PGOCtxProfileReader Reader(Cursor);
|
||||
|
||||
// Check it's analyzable by the BCAnalyzer
|
||||
BitcodeAnalyzer BA((*MB)->getBuffer());
|
||||
std::string AnalyzerDump;
|
||||
raw_string_ostream OS(AnalyzerDump);
|
||||
BCDumpOptions Opts(OS);
|
||||
|
||||
// As in, expect no error.
|
||||
EXPECT_FALSE(BA.analyze(Opts));
|
||||
EXPECT_TRUE(AnalyzerDump.find("<Metadata BlockID") != std::string::npos);
|
||||
EXPECT_TRUE(AnalyzerDump.find("<Context BlockID") != std::string::npos);
|
||||
EXPECT_TRUE(AnalyzerDump.find("<CalleeIndex codeid") != std::string::npos);
|
||||
|
||||
PGOCtxProfileReader Reader((*MB)->getBuffer());
|
||||
auto Expected = Reader.loadContexts();
|
||||
ASSERT_TRUE(!!Expected);
|
||||
auto &Ctxes = *Expected;
|
||||
@@ -143,8 +155,7 @@ TEST_F(PGOCtxProfRWTest, InvalidCounters) {
|
||||
auto MB = MemoryBuffer::getFile(ProfileFile.path());
|
||||
ASSERT_TRUE(!!MB);
|
||||
ASSERT_NE(*MB, nullptr);
|
||||
BitstreamCursor Cursor((*MB)->getBuffer());
|
||||
PGOCtxProfileReader Reader(Cursor);
|
||||
PGOCtxProfileReader Reader((*MB)->getBuffer());
|
||||
auto Expected = Reader.loadContexts();
|
||||
EXPECT_FALSE(Expected);
|
||||
consumeError(Expected.takeError());
|
||||
@@ -152,16 +163,14 @@ TEST_F(PGOCtxProfRWTest, InvalidCounters) {
|
||||
}
|
||||
|
||||
TEST_F(PGOCtxProfRWTest, Empty) {
|
||||
BitstreamCursor Cursor("");
|
||||
PGOCtxProfileReader Reader(Cursor);
|
||||
PGOCtxProfileReader Reader("");
|
||||
auto Expected = Reader.loadContexts();
|
||||
EXPECT_FALSE(Expected);
|
||||
consumeError(Expected.takeError());
|
||||
}
|
||||
|
||||
TEST_F(PGOCtxProfRWTest, Invalid) {
|
||||
BitstreamCursor Cursor("Surely this is not valid");
|
||||
PGOCtxProfileReader Reader(Cursor);
|
||||
PGOCtxProfileReader Reader("Surely this is not valid");
|
||||
auto Expected = Reader.loadContexts();
|
||||
EXPECT_FALSE(Expected);
|
||||
consumeError(Expected.takeError());
|
||||
@@ -182,8 +191,8 @@ TEST_F(PGOCtxProfRWTest, ValidButEmpty) {
|
||||
auto MB = MemoryBuffer::getFile(ProfileFile.path());
|
||||
ASSERT_TRUE(!!MB);
|
||||
ASSERT_NE(*MB, nullptr);
|
||||
BitstreamCursor Cursor((*MB)->getBuffer());
|
||||
PGOCtxProfileReader Reader(Cursor);
|
||||
|
||||
PGOCtxProfileReader Reader((*MB)->getBuffer());
|
||||
auto Expected = Reader.loadContexts();
|
||||
EXPECT_TRUE(!!Expected);
|
||||
EXPECT_TRUE(Expected->empty());
|
||||
@@ -204,8 +213,8 @@ TEST_F(PGOCtxProfRWTest, WrongVersion) {
|
||||
auto MB = MemoryBuffer::getFile(ProfileFile.path());
|
||||
ASSERT_TRUE(!!MB);
|
||||
ASSERT_NE(*MB, nullptr);
|
||||
BitstreamCursor Cursor((*MB)->getBuffer());
|
||||
PGOCtxProfileReader Reader(Cursor);
|
||||
|
||||
PGOCtxProfileReader Reader((*MB)->getBuffer());
|
||||
auto Expected = Reader.loadContexts();
|
||||
EXPECT_FALSE(Expected);
|
||||
consumeError(Expected.takeError());
|
||||
@@ -228,8 +237,7 @@ TEST_F(PGOCtxProfRWTest, DuplicateRoots) {
|
||||
auto MB = MemoryBuffer::getFile(ProfileFile.path());
|
||||
ASSERT_TRUE(!!MB);
|
||||
ASSERT_NE(*MB, nullptr);
|
||||
BitstreamCursor Cursor((*MB)->getBuffer());
|
||||
PGOCtxProfileReader Reader(Cursor);
|
||||
PGOCtxProfileReader Reader((*MB)->getBuffer());
|
||||
auto Expected = Reader.loadContexts();
|
||||
EXPECT_FALSE(Expected);
|
||||
consumeError(Expected.takeError());
|
||||
@@ -255,8 +263,7 @@ TEST_F(PGOCtxProfRWTest, DuplicateTargets) {
|
||||
auto MB = MemoryBuffer::getFile(ProfileFile.path());
|
||||
ASSERT_TRUE(!!MB);
|
||||
ASSERT_NE(*MB, nullptr);
|
||||
BitstreamCursor Cursor((*MB)->getBuffer());
|
||||
PGOCtxProfileReader Reader(Cursor);
|
||||
PGOCtxProfileReader Reader((*MB)->getBuffer());
|
||||
auto Expected = Reader.loadContexts();
|
||||
EXPECT_FALSE(Expected);
|
||||
consumeError(Expected.takeError());
|
||||
|
||||
Reference in New Issue
Block a user