"Reapply "[ctx_profile] Profile reader and writer" (#92199)"

This reverts commit 2c54bf497f.

Fixed gcc-7 issue.
This commit is contained in:
Mircea Trofin
2024-05-15 12:45:50 -07:00
parent 4ab2ac22d0
commit fc8775e214
7 changed files with 664 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
//===--- PGOCtxProfReader.h - Contextual profile reader ---------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// Reader for contextual iFDO profile, which comes in bitstream format.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_PROFILEDATA_CTXINSTRPROFILEREADER_H
#define LLVM_PROFILEDATA_CTXINSTRPROFILEREADER_H
#include "llvm/ADT/DenseSet.h"
#include "llvm/Bitstream/BitstreamReader.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/ProfileData/PGOCtxProfWriter.h"
#include "llvm/Support/Error.h"
#include <map>
#include <vector>
namespace llvm {
/// The loaded contextual profile, suitable for mutation during IPO passes. We
/// generally expect a fraction of counters and of callsites to be populated.
/// We continue to model counters as vectors, but callsites are modeled as a map
/// of a map. The expectation is that, typically, there is a small number of
/// indirect targets (usually, 1 for direct calls); but potentially a large
/// number of callsites, and, as inlining progresses, the callsite count of a
/// caller will grow.
class PGOContextualProfile final {
public:
using CallTargetMapTy = std::map<GlobalValue::GUID, PGOContextualProfile>;
using CallsiteMapTy = DenseMap<uint32_t, CallTargetMapTy>;
private:
friend class PGOCtxProfileReader;
GlobalValue::GUID GUID = 0;
SmallVector<uint64_t, 16> Counters;
CallsiteMapTy Callsites;
PGOContextualProfile(GlobalValue::GUID G,
SmallVectorImpl<uint64_t> &&Counters)
: GUID(G), Counters(std::move(Counters)) {}
Expected<PGOContextualProfile &>
getOrEmplace(uint32_t Index, GlobalValue::GUID G,
SmallVectorImpl<uint64_t> &&Counters);
public:
PGOContextualProfile(const PGOContextualProfile &) = delete;
PGOContextualProfile &operator=(const PGOContextualProfile &) = delete;
PGOContextualProfile(PGOContextualProfile &&) = default;
PGOContextualProfile &operator=(PGOContextualProfile &&) = default;
GlobalValue::GUID guid() const { return GUID; }
const SmallVectorImpl<uint64_t> &counters() const { return Counters; }
const CallsiteMapTy &callsites() const { return Callsites; }
CallsiteMapTy &callsites() { return Callsites; }
bool hasCallsite(uint32_t I) const {
return Callsites.find(I) != Callsites.end();
}
const CallTargetMapTy &callsite(uint32_t I) const {
assert(hasCallsite(I) && "Callsite not found");
return Callsites.find(I)->second;
}
void getContainedGuids(DenseSet<GlobalValue::GUID> &Guids) const;
};
class PGOCtxProfileReader final {
BitstreamCursor &Cursor;
Expected<BitstreamEntry> advance();
Error readMetadata();
Error wrongValue(const Twine &);
Error unsupported(const Twine &);
Expected<std::pair<std::optional<uint32_t>, PGOContextualProfile>>
readContext(bool ExpectIndex);
bool canReadContext();
public:
PGOCtxProfileReader(BitstreamCursor &Cursor) : Cursor(Cursor) {}
Expected<std::map<GlobalValue::GUID, PGOContextualProfile>> loadContexts();
};
} // namespace llvm
#endif

View File

@@ -0,0 +1,91 @@
//===- PGOCtxProfWriter.h - Contextual Profile Writer -----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file declares a utility for writing a contextual profile to bitstream.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_PROFILEDATA_PGOCTXPROFWRITER_H_
#define LLVM_PROFILEDATA_PGOCTXPROFWRITER_H_
#include "llvm/Bitstream/BitstreamWriter.h"
#include "llvm/ProfileData/CtxInstrContextNode.h"
namespace llvm {
enum PGOCtxProfileRecords { Invalid = 0, Version, Guid, CalleeIndex, Counters };
enum PGOCtxProfileBlockIDs {
ProfileMetadataBlockID = 100,
ContextNodeBlockID = ProfileMetadataBlockID + 1
};
/// Write one or more ContextNodes to the provided raw_fd_stream.
/// The caller must destroy the PGOCtxProfileWriter object before closing the
/// stream.
/// The design allows serializing a bunch of contexts embedded in some other
/// file. The overall format is:
///
/// [... other data written to the stream...]
/// SubBlock(ProfileMetadataBlockID)
/// Version
/// SubBlock(ContextNodeBlockID)
/// [RECORDS]
/// SubBlock(ContextNodeBlockID)
/// [RECORDS]
/// [... more SubBlocks]
/// EndBlock
/// EndBlock
///
/// The "RECORDS" are bitsream records. The IDs are in CtxProfileCodes (except)
/// for Version, which is just for metadata). All contexts will have Guid and
/// Counters, and all but the roots have CalleeIndex. The order in which the
/// records appear does not matter, but they must precede any subcontexts,
/// because that helps keep the reader code simpler.
///
/// Subblock containment captures the context->subcontext relationship. The
/// "next()" relationship in the raw profile, between call targets of indirect
/// calls, are just modeled as peer subblocks where the callee index is the
/// same.
///
/// Versioning: the writer may produce additional records not known by the
/// reader. The version number indicates a more structural change.
/// The current version, in particular, is set up to expect optional extensions
/// 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 {
SmallVector<char, 1 << 20> Buff;
BitstreamWriter Writer;
void writeCounters(const ctx_profile::ContextNode &Node);
void writeImpl(std::optional<uint32_t> CallerIndex,
const ctx_profile::ContextNode &Node);
public:
PGOCtxProfileWriter(raw_fd_stream &Out,
std::optional<unsigned> VersionOverride = std::nullopt)
: Writer(Buff, &Out, 0) {
Writer.EnterSubblock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID,
CodeLen);
const auto Version = VersionOverride ? *VersionOverride : CurrentVersion;
Writer.EmitRecord(PGOCtxProfileRecords::Version,
SmallVector<unsigned, 1>({Version}));
}
~PGOCtxProfileWriter() { Writer.ExitBlock(); }
void write(const ctx_profile::ContextNode &);
// constants used in writing which a reader may find useful.
static constexpr unsigned CodeLen = 2;
static constexpr uint32_t CurrentVersion = 1;
static constexpr unsigned VBREncodingBits = 6;
};
} // namespace llvm
#endif

View File

@@ -7,6 +7,8 @@ add_llvm_component_library(LLVMProfileData
ItaniumManglingCanonicalizer.cpp
MemProf.cpp
MemProfReader.cpp
PGOCtxProfReader.cpp
PGOCtxProfWriter.cpp
ProfileSummaryBuilder.cpp
SampleProf.cpp
SampleProfReader.cpp
@@ -20,6 +22,7 @@ add_llvm_component_library(LLVMProfileData
intrinsics_gen
LINK_COMPONENTS
BitstreamReader
Core
Object
Support

View File

@@ -0,0 +1,173 @@
//===- PGOCtxProfReader.cpp - Contextual Instrumentation profile reader ---===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Read a contextual profile into a datastructure suitable for maintenance
// throughout IPO
//
//===----------------------------------------------------------------------===//
#include "llvm/ProfileData/PGOCtxProfReader.h"
#include "llvm/Bitstream/BitCodeEnums.h"
#include "llvm/Bitstream/BitstreamReader.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/ProfileData/PGOCtxProfWriter.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
using namespace llvm;
// FIXME(#92054) - these Error handling macros are (re-)invented in a few
// places.
#define EXPECT_OR_RET(LHS, RHS) \
auto LHS = RHS; \
if (!LHS) \
return LHS.takeError();
#define RET_ON_ERR(EXPR) \
if (auto Err = (EXPR)) \
return Err;
Expected<PGOContextualProfile &>
PGOContextualProfile::getOrEmplace(uint32_t Index, GlobalValue::GUID G,
SmallVectorImpl<uint64_t> &&Counters) {
auto [Iter, Inserted] = Callsites[Index].insert(
{G, PGOContextualProfile(G, std::move(Counters))});
if (!Inserted)
return make_error<InstrProfError>(instrprof_error::invalid_prof,
"Duplicate GUID for same callsite.");
return Iter->second;
}
void PGOContextualProfile::getContainedGuids(
DenseSet<GlobalValue::GUID> &Guids) const {
Guids.insert(GUID);
for (const auto &[_, Callsite] : Callsites)
for (const auto &[_, Callee] : Callsite)
Callee.getContainedGuids(Guids);
}
Expected<BitstreamEntry> PGOCtxProfileReader::advance() {
return Cursor.advance(BitstreamCursor::AF_DontAutoprocessAbbrevs);
}
Error PGOCtxProfileReader::wrongValue(const Twine &Msg) {
return make_error<InstrProfError>(instrprof_error::invalid_prof, Msg);
}
Error PGOCtxProfileReader::unsupported(const Twine &Msg) {
return make_error<InstrProfError>(instrprof_error::unsupported_version, Msg);
}
bool PGOCtxProfileReader::canReadContext() {
auto Blk = advance();
if (!Blk) {
consumeError(Blk.takeError());
return false;
}
return Blk->Kind == BitstreamEntry::SubBlock &&
Blk->ID == PGOCtxProfileBlockIDs::ContextNodeBlockID;
}
Expected<std::pair<std::optional<uint32_t>, PGOContextualProfile>>
PGOCtxProfileReader::readContext(bool ExpectIndex) {
RET_ON_ERR(Cursor.EnterSubBlock(PGOCtxProfileBlockIDs::ContextNodeBlockID));
std::optional<ctx_profile::GUID> Guid;
std::optional<SmallVector<uint64_t, 16>> Counters;
std::optional<uint32_t> CallsiteIndex;
SmallVector<uint64_t, 1> RecordValues;
// 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.
auto GotAllWeNeed = [&]() {
return Guid.has_value() && Counters.has_value() &&
(!ExpectIndex || CallsiteIndex.has_value());
};
while (!GotAllWeNeed()) {
RecordValues.clear();
EXPECT_OR_RET(Entry, advance());
if (Entry->Kind != BitstreamEntry::Record)
return wrongValue(
"Expected records before encountering more subcontexts");
EXPECT_OR_RET(ReadRecord,
Cursor.readRecord(bitc::UNABBREV_RECORD, RecordValues));
switch (*ReadRecord) {
case PGOCtxProfileRecords::Guid:
if (RecordValues.size() != 1)
return wrongValue("The GUID record should have exactly one value");
Guid = RecordValues[0];
break;
case PGOCtxProfileRecords::Counters:
Counters = std::move(RecordValues);
if (Counters->empty())
return wrongValue("Empty counters. At least the entry counter (one "
"value) was expected");
break;
case PGOCtxProfileRecords::CalleeIndex:
if (!ExpectIndex)
return wrongValue("The root context should not have a callee index");
if (RecordValues.size() != 1)
return wrongValue("The callee index should have exactly one value");
CallsiteIndex = RecordValues[0];
break;
default:
// OK if we see records we do not understand, like records (profile
// components) introduced later.
break;
}
}
PGOContextualProfile Ret(*Guid, std::move(*Counters));
while (canReadContext()) {
EXPECT_OR_RET(SC, readContext(true));
auto &Targets = Ret.callsites()[*SC->first];
auto [_, Inserted] =
Targets.insert({SC->second.guid(), std::move(SC->second)});
if (!Inserted)
return wrongValue(
"Unexpected duplicate target (callee) at the same callsite.");
}
return std::make_pair(CallsiteIndex, std::move(Ret));
}
Error PGOCtxProfileReader::readMetadata() {
EXPECT_OR_RET(Blk, advance());
if (Blk->Kind != BitstreamEntry::SubBlock)
return unsupported("Expected Version record");
RET_ON_ERR(
Cursor.EnterSubBlock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID));
EXPECT_OR_RET(MData, advance());
if (MData->Kind != BitstreamEntry::Record)
return unsupported("Expected Version record");
SmallVector<uint64_t, 1> Ver;
EXPECT_OR_RET(Code, Cursor.readRecord(bitc::UNABBREV_RECORD, Ver));
if (*Code != PGOCtxProfileRecords::Version)
return unsupported("Expected Version record");
if (Ver.size() != 1 || Ver[0] > PGOCtxProfileWriter::CurrentVersion)
return unsupported("Version " + Twine(*Code) +
" is higher than supported version " +
Twine(PGOCtxProfileWriter::CurrentVersion));
return Error::success();
}
Expected<std::map<GlobalValue::GUID, PGOContextualProfile>>
PGOCtxProfileReader::loadContexts() {
std::map<GlobalValue::GUID, PGOContextualProfile> Ret;
RET_ON_ERR(readMetadata());
while (canReadContext()) {
EXPECT_OR_RET(E, readContext(false));
auto Key = E->second.guid();
if (!Ret.insert({Key, std::move(E->second)}).second)
return wrongValue("Duplicate roots");
}
return std::move(Ret);
}

View File

@@ -0,0 +1,49 @@
//===- PGOCtxProfWriter.cpp - Contextual Instrumentation profile writer ---===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Write a contextual profile to bitstream.
//
//===----------------------------------------------------------------------===//
#include "llvm/ProfileData/PGOCtxProfWriter.h"
#include "llvm/Bitstream/BitCodeEnums.h"
using namespace llvm;
using namespace llvm::ctx_profile;
void PGOCtxProfileWriter::writeCounters(const ContextNode &Node) {
Writer.EmitCode(bitc::UNABBREV_RECORD);
Writer.EmitVBR(PGOCtxProfileRecords::Counters, VBREncodingBits);
Writer.EmitVBR(Node.counters_size(), VBREncodingBits);
for (uint32_t I = 0U; I < Node.counters_size(); ++I)
Writer.EmitVBR64(Node.counters()[I], VBREncodingBits);
}
// recursively write all the subcontexts. We do need to traverse depth first to
// model the context->subcontext implicitly, and since this captures call
// stacks, we don't really need to be worried about stack overflow and we can
// keep the implementation simple.
void PGOCtxProfileWriter::writeImpl(std::optional<uint32_t> CallerIndex,
const ContextNode &Node) {
Writer.EnterSubblock(PGOCtxProfileBlockIDs::ContextNodeBlockID, CodeLen);
Writer.EmitRecord(PGOCtxProfileRecords::Guid,
SmallVector<uint64_t, 1>{Node.guid()});
if (CallerIndex)
Writer.EmitRecord(PGOCtxProfileRecords::CalleeIndex,
SmallVector<uint64_t, 1>{*CallerIndex});
writeCounters(Node);
for (uint32_t I = 0U; I < Node.callsites_size(); ++I)
for (const auto *Subcontext = Node.subContexts()[I]; Subcontext;
Subcontext = Subcontext->next())
writeImpl(I, *Subcontext);
Writer.ExitBlock();
}
void PGOCtxProfileWriter::write(const ContextNode &RootNode) {
writeImpl(std::nullopt, RootNode);
}

View File

@@ -13,6 +13,7 @@ add_llvm_unittest(ProfileDataTests
InstrProfTest.cpp
ItaniumManglingCanonicalizerTest.cpp
MemProfTest.cpp
PGOCtxProfReaderWriterTest.cpp
SampleProfTest.cpp
SymbolRemappingReaderTest.cpp
)

View File

@@ -0,0 +1,255 @@
//===-------------- PGOCtxProfReadWriteTest.cpp ---------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/Bitstream/BitstreamReader.h"
#include "llvm/ProfileData/CtxInstrContextNode.h"
#include "llvm/ProfileData/PGOCtxProfReader.h"
#include "llvm/ProfileData/PGOCtxProfWriter.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Testing/Support/SupportHelpers.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace llvm::ctx_profile;
class PGOCtxProfRWTest : public ::testing::Test {
std::vector<std::unique_ptr<char[]>> Nodes;
std::map<GUID, const ContextNode *> Roots;
public:
ContextNode *createNode(GUID Guid, uint32_t NrCounters, uint32_t NrCallsites,
ContextNode *Next = nullptr) {
auto AllocSize = ContextNode::getAllocSize(NrCounters, NrCallsites);
auto *Mem = Nodes.emplace_back(std::make_unique<char[]>(AllocSize)).get();
std::memset(Mem, 0, AllocSize);
auto *Ret = new (Mem) ContextNode(Guid, NrCounters, NrCallsites, Next);
return Ret;
}
void SetUp() override {
// Root (guid 1) has 2 callsites, one used for an indirect call to either
// guid 2 or 4.
// guid 2 calls guid 5
// guid 5 calls guid 2
// there's also a second root, guid3.
auto *Root1 = createNode(1, 2, 2);
Root1->counters()[0] = 10;
Root1->counters()[1] = 11;
Roots.insert({1, Root1});
auto *L1 = createNode(2, 1, 1);
L1->counters()[0] = 12;
Root1->subContexts()[1] = createNode(4, 3, 1, L1);
Root1->subContexts()[1]->counters()[0] = 13;
Root1->subContexts()[1]->counters()[1] = 14;
Root1->subContexts()[1]->counters()[2] = 15;
auto *L3 = createNode(5, 6, 3);
for (auto I = 0; I < 6; ++I)
L3->counters()[I] = 16 + I;
L1->subContexts()[0] = L3;
L3->subContexts()[2] = createNode(2, 1, 1);
L3->subContexts()[2]->counters()[0] = 30;
auto *Root2 = createNode(3, 1, 0);
Root2->counters()[0] = 40;
Roots.insert({3, Root2});
}
const std::map<GUID, const ContextNode *> &roots() const { return Roots; }
};
void checkSame(const ContextNode &Raw, const PGOContextualProfile &Profile) {
EXPECT_EQ(Raw.guid(), Profile.guid());
ASSERT_EQ(Raw.counters_size(), Profile.counters().size());
for (auto I = 0U; I < Raw.counters_size(); ++I)
EXPECT_EQ(Raw.counters()[I], Profile.counters()[I]);
for (auto I = 0U; I < Raw.callsites_size(); ++I) {
if (Raw.subContexts()[I] == nullptr)
continue;
EXPECT_TRUE(Profile.hasCallsite(I));
const auto &ProfileTargets = Profile.callsite(I);
std::map<GUID, const ContextNode *> Targets;
for (const auto *N = Raw.subContexts()[I]; N; N = N->next())
EXPECT_TRUE(Targets.insert({N->guid(), N}).second);
EXPECT_EQ(Targets.size(), ProfileTargets.size());
for (auto It : Targets) {
auto PIt = ProfileTargets.find(It.second->guid());
EXPECT_NE(PIt, ProfileTargets.end());
checkSame(*It.second, PIt->second);
}
}
}
TEST_F(PGOCtxProfRWTest, RoundTrip) {
llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);
{
std::error_code EC;
raw_fd_stream Out(ProfileFile.path(), EC);
ASSERT_FALSE(EC);
{
PGOCtxProfileWriter Writer(Out);
for (auto &[_, R] : roots())
Writer.write(*R);
}
}
{
ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
MemoryBuffer::getFile(ProfileFile.path());
ASSERT_TRUE(!!MB);
ASSERT_NE(*MB, nullptr);
BitstreamCursor Cursor((*MB)->getBuffer());
PGOCtxProfileReader Reader(Cursor);
auto Expected = Reader.loadContexts();
ASSERT_TRUE(!!Expected);
auto &Ctxes = *Expected;
EXPECT_EQ(Ctxes.size(), roots().size());
EXPECT_EQ(Ctxes.size(), 2U);
for (auto &[G, R] : roots())
checkSame(*R, Ctxes.find(G)->second);
}
}
TEST_F(PGOCtxProfRWTest, InvalidCounters) {
auto *R = createNode(1, 0, 1);
llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);
{
std::error_code EC;
raw_fd_stream Out(ProfileFile.path(), EC);
ASSERT_FALSE(EC);
{
PGOCtxProfileWriter Writer(Out);
Writer.write(*R);
}
}
{
auto MB = MemoryBuffer::getFile(ProfileFile.path());
ASSERT_TRUE(!!MB);
ASSERT_NE(*MB, nullptr);
BitstreamCursor Cursor((*MB)->getBuffer());
PGOCtxProfileReader Reader(Cursor);
auto Expected = Reader.loadContexts();
EXPECT_FALSE(Expected);
consumeError(Expected.takeError());
}
}
TEST_F(PGOCtxProfRWTest, Empty) {
BitstreamCursor Cursor("");
PGOCtxProfileReader Reader(Cursor);
auto Expected = Reader.loadContexts();
EXPECT_FALSE(Expected);
consumeError(Expected.takeError());
}
TEST_F(PGOCtxProfRWTest, Invalid) {
BitstreamCursor Cursor("Surely this is not valid");
PGOCtxProfileReader Reader(Cursor);
auto Expected = Reader.loadContexts();
EXPECT_FALSE(Expected);
consumeError(Expected.takeError());
}
TEST_F(PGOCtxProfRWTest, ValidButEmpty) {
llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);
{
std::error_code EC;
raw_fd_stream Out(ProfileFile.path(), EC);
ASSERT_FALSE(EC);
{
PGOCtxProfileWriter Writer(Out);
// don't write anything - this will just produce the metadata subblock.
}
}
{
auto MB = MemoryBuffer::getFile(ProfileFile.path());
ASSERT_TRUE(!!MB);
ASSERT_NE(*MB, nullptr);
BitstreamCursor Cursor((*MB)->getBuffer());
PGOCtxProfileReader Reader(Cursor);
auto Expected = Reader.loadContexts();
EXPECT_TRUE(!!Expected);
EXPECT_TRUE(Expected->empty());
}
}
TEST_F(PGOCtxProfRWTest, WrongVersion) {
llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);
{
std::error_code EC;
raw_fd_stream Out(ProfileFile.path(), EC);
ASSERT_FALSE(EC);
{
PGOCtxProfileWriter Writer(Out, PGOCtxProfileWriter::CurrentVersion + 1);
}
}
{
auto MB = MemoryBuffer::getFile(ProfileFile.path());
ASSERT_TRUE(!!MB);
ASSERT_NE(*MB, nullptr);
BitstreamCursor Cursor((*MB)->getBuffer());
PGOCtxProfileReader Reader(Cursor);
auto Expected = Reader.loadContexts();
EXPECT_FALSE(Expected);
consumeError(Expected.takeError());
}
}
TEST_F(PGOCtxProfRWTest, DuplicateRoots) {
llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);
{
std::error_code EC;
raw_fd_stream Out(ProfileFile.path(), EC);
ASSERT_FALSE(EC);
{
PGOCtxProfileWriter Writer(Out);
Writer.write(*createNode(1, 1, 1));
Writer.write(*createNode(1, 1, 1));
}
}
{
auto MB = MemoryBuffer::getFile(ProfileFile.path());
ASSERT_TRUE(!!MB);
ASSERT_NE(*MB, nullptr);
BitstreamCursor Cursor((*MB)->getBuffer());
PGOCtxProfileReader Reader(Cursor);
auto Expected = Reader.loadContexts();
EXPECT_FALSE(Expected);
consumeError(Expected.takeError());
}
}
TEST_F(PGOCtxProfRWTest, DuplicateTargets) {
llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);
{
std::error_code EC;
raw_fd_stream Out(ProfileFile.path(), EC);
ASSERT_FALSE(EC);
{
auto *R = createNode(1, 1, 1);
auto *L1 = createNode(2, 1, 0);
auto *L2 = createNode(2, 1, 0, L1);
R->subContexts()[0] = L2;
PGOCtxProfileWriter Writer(Out);
Writer.write(*R);
}
}
{
auto MB = MemoryBuffer::getFile(ProfileFile.path());
ASSERT_TRUE(!!MB);
ASSERT_NE(*MB, nullptr);
BitstreamCursor Cursor((*MB)->getBuffer());
PGOCtxProfileReader Reader(Cursor);
auto Expected = Reader.loadContexts();
EXPECT_FALSE(Expected);
consumeError(Expected.takeError());
}
}