Skip to content

[ctxprof] Prepare profile format for flat profiles #129626

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions compiler-rt/lib/ctx_profile/CtxInstrContextNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,14 @@ class ContextNode final {
};

/// Abstraction for the parameter passed to `__llvm_ctx_profile_fetch`.
/// `startContextSection` is called before any context roots are sent for
/// writing. Then one or more `writeContextual` calls are made; finally,
/// `endContextSection` is called.
class ProfileWriter {
public:
virtual void startContextSection() = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could have this method return an object which runs endContextSection on destruction. Then we wouldn't have to worry about pairing up the calls to start and end each time. Perhaps it's too much for a simple interface like this though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought of that but yes, seemed overkill. It's a low-level interface, really meant to go between compiler-rt and whatever service handles writing the profile.

virtual void writeContextual(const ctx_profile::ContextNode &RootNode) = 0;
virtual void endContextSection() = 0;
virtual ~ProfileWriter() = default;
};
} // namespace ctx_profile
Expand Down
2 changes: 2 additions & 0 deletions compiler-rt/lib/ctx_profile/CtxInstrProfiling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ bool __llvm_ctx_profile_fetch(ProfileWriter &Writer) {
__sanitizer::GenericScopedLock<__sanitizer::SpinMutex> Lock(
&AllContextsMutex);

Writer.startContextSection();
for (int I = 0, E = AllContextRoots.Size(); I < E; ++I) {
auto *Root = AllContextRoots[I];
__sanitizer::GenericScopedLock<__sanitizer::StaticSpinMutex> TakenLock(
Expand All @@ -308,6 +309,7 @@ bool __llvm_ctx_profile_fetch(ProfileWriter &Writer) {
}
Writer.writeContextual(*Root->FirstNode);
}
Writer.endContextSection();
return true;
}

Expand Down
15 changes: 15 additions & 0 deletions compiler-rt/lib/ctx_profile/tests/CtxInstrProfilingTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,18 @@ TEST_F(ContextTest, Dump) {
public:
ContextRoot *const Root;
const size_t Entries;

int EnteredSectionCount = 0;
int ExitedSectionCount = 0;

bool State = false;

TestProfileWriter(ContextRoot *Root, size_t Entries)
: Root(Root), Entries(Entries) {}

void writeContextual(const ContextNode &Node) override {
EXPECT_EQ(EnteredSectionCount, 1);
EXPECT_EQ(ExitedSectionCount, 0);
EXPECT_FALSE(Root->Taken.TryLock());
EXPECT_EQ(Node.guid(), 1U);
EXPECT_EQ(Node.counters()[0], Entries);
Expand All @@ -205,7 +212,13 @@ TEST_F(ContextTest, Dump) {
EXPECT_EQ(SN.subContexts()[0], nullptr);
State = true;
}
void startContextSection() override { ++EnteredSectionCount; }
void endContextSection() override {
EXPECT_EQ(EnteredSectionCount, 1);
++ExitedSectionCount;
}
};

TestProfileWriter W(&Root, 1);
EXPECT_FALSE(W.State);
__llvm_ctx_profile_fetch(W);
Expand All @@ -217,4 +230,6 @@ TEST_F(ContextTest, Dump) {
EXPECT_FALSE(W2.State);
__llvm_ctx_profile_fetch(W2);
EXPECT_TRUE(W2.State);
EXPECT_EQ(W2.EnteredSectionCount, 1);
EXPECT_EQ(W2.ExitedSectionCount, 1);
}
11 changes: 10 additions & 1 deletion compiler-rt/test/ctx_profile/TestCases/generate-context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,14 @@ class TestProfileWriter : public ProfileWriter {
}
}

public:
void startContextSection() override {
std::cout << "Entered Context Section" << std::endl;
}

void endContextSection() override {
std::cout << "Exited Context Section" << std::endl;
}

void writeContextual(const ContextNode &RootNode) override {
printProfile(RootNode, "", "");
}
Expand All @@ -77,6 +84,7 @@ class TestProfileWriter : public ProfileWriter {
// path gets instrumented).
// The second context is in the loop. We expect 2 entries and each of the
// branches would be taken once, so the second counter is 1.
// CHECK-NEXT: Entered Context Section
// CHECK-NEXT: Guid: 8657661246551306189
// CHECK-NEXT: Entries: 1
// CHECK-NEXT: 2 counters and 3 callsites
Expand All @@ -91,6 +99,7 @@ class TestProfileWriter : public ProfileWriter {
// CHECK-NEXT: Entries: 2
// CHECK-NEXT: 2 counters and 2 callsites
// CHECK-NEXT: Counter values: 2 1
// CHECK-NEXT: Exited Context Section

bool profileWriter() {
TestProfileWriter W;
Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/Analysis/CtxProfAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ class PGOContextualProfile {
return Profiles.Contexts;
}

const PGOCtxProfile &profiles() const { return Profiles; }

bool isFunctionKnown(const Function &F) const {
return getDefinedFunctionGUID(F) != 0;
}
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/ProfileData/CtxInstrContextNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,14 @@ class ContextNode final {
};

/// Abstraction for the parameter passed to `__llvm_ctx_profile_fetch`.
/// `startContextSection` is called before any context roots are sent for
/// writing. Then one or more `writeContextual` calls are made; finally,
/// `endContextSection` is called.
class ProfileWriter {
public:
virtual void startContextSection() = 0;
virtual void writeContextual(const ctx_profile::ContextNode &RootNode) = 0;
virtual void endContextSection() = 0;
virtual ~ProfileWriter() = default;
};
} // namespace ctx_profile
Expand Down
11 changes: 7 additions & 4 deletions llvm/include/llvm/ProfileData/PGOCtxProfReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,12 @@ class PGOCtxProfileReader final {
Error unsupported(const Twine &);

Expected<std::pair<std::optional<uint32_t>, PGOCtxProfContext>>
readContext(bool ExpectIndex);
bool canReadContext();
readProfile(PGOCtxProfileBlockIDs Kind);

bool canEnterBlockWithID(PGOCtxProfileBlockIDs ID);
Error enterBlockWithID(PGOCtxProfileBlockIDs ID);

Error loadContexts(CtxProfContextualProfiles &);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: name the parameter.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed in PR #129626


public:
PGOCtxProfileReader(StringRef Buffer)
Expand All @@ -201,7 +205,6 @@ class PGOCtxProfileReader final {
Expected<PGOCtxProfile> loadProfiles();
};

void convertCtxProfToYaml(raw_ostream &OS,
const PGOCtxProfContext::CallTargetMapTy &);
void convertCtxProfToYaml(raw_ostream &OS, const PGOCtxProfile &);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: name the second param too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed in PR #129626

} // namespace llvm
#endif
21 changes: 15 additions & 6 deletions llvm/include/llvm/ProfileData/PGOCtxProfWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ enum PGOCtxProfileRecords { Invalid = 0, Version, Guid, CalleeIndex, Counters };

enum PGOCtxProfileBlockIDs {
ProfileMetadataBlockID = bitc::FIRST_APPLICATION_BLOCKID,
ContextNodeBlockID = ProfileMetadataBlockID + 1
ContextsSectionBlockID = ProfileMetadataBlockID + 1,
ContextRootBlockID = ContextsSectionBlockID + 1,
ContextNodeBlockID = ContextRootBlockID + 1,
};

/// Write one or more ContextNodes to the provided raw_fd_stream.
Expand Down Expand Up @@ -60,23 +62,30 @@ enum PGOCtxProfileBlockIDs {
/// like value profiling - which would appear as additional records. For
/// example, value profiling would produce a new record with a new record ID,
/// containing the profiled values (much like the counters)
class PGOCtxProfileWriter final {
class PGOCtxProfileWriter : public ctx_profile::ProfileWriter {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can still keep the final keyword here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed in PR #129626

enum class EmptyContextCriteria { None, EntryIsZero, AllAreZero };

BitstreamWriter Writer;
const bool IncludeEmpty;

void writeCounters(const ctx_profile::ContextNode &Node);
void writeGuid(ctx_profile::GUID Guid);
void writeCounters(ArrayRef<uint64_t> Counters);
void writeImpl(std::optional<uint32_t> CallerIndex,
const ctx_profile::ContextNode &Node);

public:
PGOCtxProfileWriter(raw_ostream &Out,
std::optional<unsigned> VersionOverride = std::nullopt);
std::optional<unsigned> VersionOverride = std::nullopt,
bool IncludeEmpty = false);
~PGOCtxProfileWriter() { Writer.ExitBlock(); }

void write(const ctx_profile::ContextNode &);
void startContextSection() override;
void writeContextual(const ctx_profile::ContextNode &RootNode) override;
void endContextSection() override;

// constants used in writing which a reader may find useful.
static constexpr unsigned CodeLen = 2;
static constexpr uint32_t CurrentVersion = 1;
static constexpr uint32_t CurrentVersion = 2;
static constexpr unsigned VBREncodingBits = 6;
static constexpr StringRef ContainerMagic = "CTXP";
};
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Analysis/CtxProfAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ PreservedAnalyses CtxProfAnalysisPrinterPass::run(Module &M,

if (Mode == PrintMode::Everything)
OS << "\nCurrent Profile:\n";
convertCtxProfToYaml(OS, C.contexts());
convertCtxProfToYaml(OS, C.profiles());
OS << "\n";
if (Mode == PrintMode::YAML)
return PreservedAnalyses::all();
Expand Down
78 changes: 53 additions & 25 deletions llvm/lib/ProfileData/PGOCtxProfReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/YAMLTraits.h"
#include <iterator>
#include <utility>

using namespace llvm;
Expand Down Expand Up @@ -58,26 +57,34 @@ Error PGOCtxProfileReader::unsupported(const Twine &Msg) {
return make_error<InstrProfError>(instrprof_error::unsupported_version, Msg);
}

bool PGOCtxProfileReader::canReadContext() {
bool PGOCtxProfileReader::canEnterBlockWithID(PGOCtxProfileBlockIDs ID) {
auto Blk = advance();
if (!Blk) {
consumeError(Blk.takeError());
return false;
}
return Blk->Kind == BitstreamEntry::SubBlock &&
Blk->ID == PGOCtxProfileBlockIDs::ContextNodeBlockID;
return Blk->Kind == BitstreamEntry::SubBlock && Blk->ID == ID;
}

Error PGOCtxProfileReader::enterBlockWithID(PGOCtxProfileBlockIDs ID) {
RET_ON_ERR(Cursor.EnterSubBlock(ID));
return Error::success();
}

Expected<std::pair<std::optional<uint32_t>, PGOCtxProfContext>>
PGOCtxProfileReader::readContext(bool ExpectIndex) {
RET_ON_ERR(Cursor.EnterSubBlock(PGOCtxProfileBlockIDs::ContextNodeBlockID));
PGOCtxProfileReader::readProfile(PGOCtxProfileBlockIDs Kind) {
assert((Kind == PGOCtxProfileBlockIDs::ContextRootBlockID ||
Kind == PGOCtxProfileBlockIDs::ContextNodeBlockID) &&
"Unexpected profile kind");
RET_ON_ERR(enterBlockWithID(Kind));

std::optional<ctx_profile::GUID> Guid;
std::optional<SmallVector<uint64_t, 16>> Counters;
std::optional<uint32_t> CallsiteIndex;

SmallVector<uint64_t, 1> RecordValues;

const bool ExpectIndex = Kind == PGOCtxProfileBlockIDs::ContextNodeBlockID;
// We don't prescribe the order in which the records come in, and we are ok
// if other unsupported records appear. We seek in the current subblock until
// we get all we know.
Expand Down Expand Up @@ -121,8 +128,8 @@ PGOCtxProfileReader::readContext(bool ExpectIndex) {

PGOCtxProfContext Ret(*Guid, std::move(*Counters));

while (canReadContext()) {
EXPECT_OR_RET(SC, readContext(true));
while (canEnterBlockWithID(PGOCtxProfileBlockIDs::ContextNodeBlockID)) {
EXPECT_OR_RET(SC, readProfile(PGOCtxProfileBlockIDs::ContextNodeBlockID));
auto &Targets = Ret.callsites()[*SC->first];
auto [_, Inserted] =
Targets.insert({SC->second.guid(), std::move(SC->second)});
Expand Down Expand Up @@ -168,15 +175,23 @@ Error PGOCtxProfileReader::readMetadata() {
return Error::success();
}

Error PGOCtxProfileReader::loadContexts(CtxProfContextualProfiles &P) {
if (canEnterBlockWithID(PGOCtxProfileBlockIDs::ContextsSectionBlockID)) {
RET_ON_ERR(enterBlockWithID(PGOCtxProfileBlockIDs::ContextsSectionBlockID));
while (canEnterBlockWithID(PGOCtxProfileBlockIDs::ContextRootBlockID)) {
EXPECT_OR_RET(E, readProfile(PGOCtxProfileBlockIDs::ContextRootBlockID));
auto Key = E->second.guid();
if (!P.insert({Key, std::move(E->second)}).second)
return wrongValue("Duplicate roots");
}
}
return Error::success();
}

Expected<PGOCtxProfile> PGOCtxProfileReader::loadProfiles() {
PGOCtxProfile Ret;
RET_ON_ERR(readMetadata());
while (canReadContext()) {
EXPECT_OR_RET(E, readContext(false));
auto Key = E->second.guid();
if (!Ret.Contexts.insert({Key, std::move(E->second)}).second)
return wrongValue("Duplicate roots");
}
PGOCtxProfile Ret;
RET_ON_ERR(loadContexts(Ret.Contexts));
return std::move(Ret);
}

Expand Down Expand Up @@ -224,41 +239,54 @@ void toYaml(yaml::Output &Out,
Out.endSequence();
}

void toYaml(yaml::Output &Out, const PGOCtxProfContext &Ctx) {
void toYaml(yaml::Output &Out, GlobalValue::GUID Guid,
const SmallVectorImpl<uint64_t> &Counters,
const PGOCtxProfContext::CallsiteMapTy &Callsites) {
yaml::EmptyContext Empty;
Out.beginMapping();
void *SaveInfo = nullptr;
bool UseDefault = false;
{
Out.preflightKey("Guid", /*Required=*/true, /*SameAsDefault=*/false,
UseDefault, SaveInfo);
auto Guid = Ctx.guid();
yaml::yamlize(Out, Guid, true, Empty);
Out.postflightKey(nullptr);
}
{
Out.preflightKey("Counters", true, false, UseDefault, SaveInfo);
Out.beginFlowSequence();
for (size_t I = 0U, E = Ctx.counters().size(); I < E; ++I) {
for (size_t I = 0U, E = Counters.size(); I < E; ++I) {
Out.preflightFlowElement(I, SaveInfo);
uint64_t V = Ctx.counters()[I];
uint64_t V = Counters[I];
yaml::yamlize(Out, V, true, Empty);
Out.postflightFlowElement(SaveInfo);
}
Out.endFlowSequence();
Out.postflightKey(nullptr);
}
if (!Ctx.callsites().empty()) {
if (!Callsites.empty()) {
Out.preflightKey("Callsites", true, false, UseDefault, SaveInfo);
toYaml(Out, Ctx.callsites());
toYaml(Out, Callsites);
Out.postflightKey(nullptr);
}
Out.endMapping();
}
void toYaml(yaml::Output &Out, const PGOCtxProfContext &Ctx) {
toYaml(Out, Ctx.guid(), Ctx.counters(), Ctx.callsites());
}

} // namespace

void llvm::convertCtxProfToYaml(
raw_ostream &OS, const PGOCtxProfContext::CallTargetMapTy &Profiles) {
void llvm::convertCtxProfToYaml(raw_ostream &OS,
const PGOCtxProfile &Profiles) {
yaml::Output Out(OS);
toYaml(Out, Profiles);
}
void *SaveInfo = nullptr;
bool UseDefault = false;
Out.beginMapping();
if (!Profiles.Contexts.empty()) {
Out.preflightKey("Contexts", false, false, UseDefault, SaveInfo);
toYaml(Out, Profiles.Contexts);
Out.postflightKey(nullptr);
}
Out.endMapping();
}
Loading