123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- //===- unittest/ProfileData/SampleProfTest.cpp ------------------*- 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
- //
- //===----------------------------------------------------------------------===//
- #include "llvm/ProfileData/SampleProf.h"
- #include "llvm/ADT/StringMap.h"
- #include "llvm/ADT/StringRef.h"
- #include "llvm/IR/LLVMContext.h"
- #include "llvm/IR/Metadata.h"
- #include "llvm/IR/Module.h"
- #include "llvm/ProfileData/SampleProfReader.h"
- #include "llvm/ProfileData/SampleProfWriter.h"
- #include "llvm/Support/Casting.h"
- #include "llvm/Support/ErrorOr.h"
- #include "llvm/Support/MemoryBuffer.h"
- #include "llvm/Support/raw_ostream.h"
- #include "gtest/gtest.h"
- #include <string>
- #include <vector>
- using namespace llvm;
- using namespace sampleprof;
- static ::testing::AssertionResult NoError(std::error_code EC) {
- if (!EC)
- return ::testing::AssertionSuccess();
- return ::testing::AssertionFailure() << "error " << EC.value() << ": "
- << EC.message();
- }
- namespace {
- struct SampleProfTest : ::testing::Test {
- LLVMContext Context;
- std::unique_ptr<SampleProfileWriter> Writer;
- std::unique_ptr<SampleProfileReader> Reader;
- SampleProfTest() : Writer(), Reader() {}
- void createWriter(SampleProfileFormat Format, StringRef Profile) {
- std::error_code EC;
- std::unique_ptr<raw_ostream> OS(
- new raw_fd_ostream(Profile, EC, sys::fs::OF_None));
- auto WriterOrErr = SampleProfileWriter::create(OS, Format);
- ASSERT_TRUE(NoError(WriterOrErr.getError()));
- Writer = std::move(WriterOrErr.get());
- }
- void readProfile(const Module &M, StringRef Profile,
- StringRef RemapFile = "") {
- auto ReaderOrErr = SampleProfileReader::create(Profile, Context, RemapFile);
- ASSERT_TRUE(NoError(ReaderOrErr.getError()));
- Reader = std::move(ReaderOrErr.get());
- Reader->collectFuncsFrom(M);
- }
- void createRemapFile(SmallVectorImpl<char> &RemapPath, StringRef &RemapFile) {
- std::error_code EC =
- llvm::sys::fs::createTemporaryFile("remapfile", "", RemapPath);
- ASSERT_TRUE(NoError(EC));
- RemapFile = StringRef(RemapPath.data(), RemapPath.size());
- std::unique_ptr<raw_fd_ostream> OS(
- new raw_fd_ostream(RemapFile, EC, sys::fs::OF_None));
- *OS << R"(
- # Types 'int' and 'long' are equivalent
- type i l
- # Function names 'foo' and 'faux' are equivalent
- name 3foo 4faux
- )";
- OS->close();
- }
- void testRoundTrip(SampleProfileFormat Format, bool Remap) {
- SmallVector<char, 128> ProfilePath;
- ASSERT_TRUE(NoError(llvm::sys::fs::createTemporaryFile("profile", "", ProfilePath)));
- StringRef Profile(ProfilePath.data(), ProfilePath.size());
- createWriter(Format, Profile);
- StringRef FooName("_Z3fooi");
- FunctionSamples FooSamples;
- FooSamples.setName(FooName);
- FooSamples.addTotalSamples(7711);
- FooSamples.addHeadSamples(610);
- FooSamples.addBodySamples(1, 0, 610);
- FooSamples.addBodySamples(2, 0, 600);
- FooSamples.addBodySamples(4, 0, 60000);
- FooSamples.addBodySamples(8, 0, 60351);
- FooSamples.addBodySamples(10, 0, 605);
- StringRef BarName("_Z3bari");
- FunctionSamples BarSamples;
- BarSamples.setName(BarName);
- BarSamples.addTotalSamples(20301);
- BarSamples.addHeadSamples(1437);
- BarSamples.addBodySamples(1, 0, 1437);
- // Test how reader/writer handles unmangled names.
- StringRef MconstructName("_M_construct<char *>");
- StringRef StringviewName("string_view<std::allocator<char> >");
- BarSamples.addCalledTargetSamples(1, 0, MconstructName, 1000);
- BarSamples.addCalledTargetSamples(1, 0, StringviewName, 437);
- StringRef BazName("_Z3bazi");
- FunctionSamples BazSamples;
- BazSamples.setName(BazName);
- BazSamples.addTotalSamples(12557);
- BazSamples.addHeadSamples(1257);
- BazSamples.addBodySamples(1, 0, 12557);
- StringRef BooName("_Z3booi");
- FunctionSamples BooSamples;
- BooSamples.setName(BooName);
- BooSamples.addTotalSamples(1232);
- BooSamples.addHeadSamples(1);
- BooSamples.addBodySamples(1, 0, 1232);
- StringMap<FunctionSamples> Profiles;
- Profiles[FooName] = std::move(FooSamples);
- Profiles[BarName] = std::move(BarSamples);
- Profiles[BazName] = std::move(BazSamples);
- Profiles[BooName] = std::move(BooSamples);
- Module M("my_module", Context);
- FunctionType *fn_type =
- FunctionType::get(Type::getVoidTy(Context), {}, false);
- SmallVector<char, 128> RemapPath;
- StringRef RemapFile;
- if (Remap) {
- createRemapFile(RemapPath, RemapFile);
- FooName = "_Z4fauxi";
- BarName = "_Z3barl";
- }
- M.getOrInsertFunction(FooName, fn_type);
- M.getOrInsertFunction(BarName, fn_type);
- M.getOrInsertFunction(BooName, fn_type);
- ProfileSymbolList List;
- if (Format == SampleProfileFormat::SPF_Ext_Binary) {
- List.add("zoo", true);
- List.add("moo", true);
- }
- Writer->setProfileSymbolList(&List);
- std::error_code EC;
- EC = Writer->write(Profiles);
- ASSERT_TRUE(NoError(EC));
- Writer->getOutputStream().flush();
- readProfile(M, Profile, RemapFile);
- EC = Reader->read();
- ASSERT_TRUE(NoError(EC));
- if (Format == SampleProfileFormat::SPF_Ext_Binary) {
- std::unique_ptr<ProfileSymbolList> ReaderList =
- Reader->getProfileSymbolList();
- ReaderList->contains("zoo");
- ReaderList->contains("moo");
- }
- FunctionSamples *ReadFooSamples = Reader->getSamplesFor(FooName);
- ASSERT_TRUE(ReadFooSamples != nullptr);
- if (Format != SampleProfileFormat::SPF_Compact_Binary) {
- ASSERT_EQ("_Z3fooi", ReadFooSamples->getName());
- }
- ASSERT_EQ(7711u, ReadFooSamples->getTotalSamples());
- ASSERT_EQ(610u, ReadFooSamples->getHeadSamples());
- FunctionSamples *ReadBarSamples = Reader->getSamplesFor(BarName);
- ASSERT_TRUE(ReadBarSamples != nullptr);
- if (Format != SampleProfileFormat::SPF_Compact_Binary) {
- ASSERT_EQ("_Z3bari", ReadBarSamples->getName());
- }
- ASSERT_EQ(20301u, ReadBarSamples->getTotalSamples());
- ASSERT_EQ(1437u, ReadBarSamples->getHeadSamples());
- ErrorOr<SampleRecord::CallTargetMap> CTMap =
- ReadBarSamples->findCallTargetMapAt(1, 0);
- ASSERT_FALSE(CTMap.getError());
- // Because _Z3bazi is not defined in module M, expect _Z3bazi's profile
- // is not loaded when the profile is ExtBinary or Compact format because
- // these formats support loading function profiles on demand.
- FunctionSamples *ReadBazSamples = Reader->getSamplesFor(BazName);
- if (Format == SampleProfileFormat::SPF_Ext_Binary ||
- Format == SampleProfileFormat::SPF_Compact_Binary) {
- ASSERT_TRUE(ReadBazSamples == nullptr);
- ASSERT_EQ(3u, Reader->getProfiles().size());
- } else {
- ASSERT_TRUE(ReadBazSamples != nullptr);
- ASSERT_EQ(12557u, ReadBazSamples->getTotalSamples());
- ASSERT_EQ(4u, Reader->getProfiles().size());
- }
- FunctionSamples *ReadBooSamples = Reader->getSamplesFor(BooName);
- ASSERT_TRUE(ReadBooSamples != nullptr);
- ASSERT_EQ(1232u, ReadBooSamples->getTotalSamples());
- std::string MconstructGUID;
- StringRef MconstructRep =
- getRepInFormat(MconstructName, Format, MconstructGUID);
- std::string StringviewGUID;
- StringRef StringviewRep =
- getRepInFormat(StringviewName, Format, StringviewGUID);
- ASSERT_EQ(1000u, CTMap.get()[MconstructRep]);
- ASSERT_EQ(437u, CTMap.get()[StringviewRep]);
- auto VerifySummary = [](ProfileSummary &Summary) mutable {
- ASSERT_EQ(ProfileSummary::PSK_Sample, Summary.getKind());
- ASSERT_EQ(137392u, Summary.getTotalCount());
- ASSERT_EQ(8u, Summary.getNumCounts());
- ASSERT_EQ(4u, Summary.getNumFunctions());
- ASSERT_EQ(1437u, Summary.getMaxFunctionCount());
- ASSERT_EQ(60351u, Summary.getMaxCount());
- uint32_t Cutoff = 800000;
- auto Predicate = [&Cutoff](const ProfileSummaryEntry &PE) {
- return PE.Cutoff == Cutoff;
- };
- std::vector<ProfileSummaryEntry> &Details = Summary.getDetailedSummary();
- auto EightyPerc = find_if(Details, Predicate);
- Cutoff = 900000;
- auto NinetyPerc = find_if(Details, Predicate);
- Cutoff = 950000;
- auto NinetyFivePerc = find_if(Details, Predicate);
- Cutoff = 990000;
- auto NinetyNinePerc = find_if(Details, Predicate);
- ASSERT_EQ(60000u, EightyPerc->MinCount);
- ASSERT_EQ(12557u, NinetyPerc->MinCount);
- ASSERT_EQ(12557u, NinetyFivePerc->MinCount);
- ASSERT_EQ(610u, NinetyNinePerc->MinCount);
- };
- ProfileSummary &Summary = Reader->getSummary();
- VerifySummary(Summary);
- // Test that conversion of summary to and from Metadata works.
- Metadata *MD = Summary.getMD(Context);
- ASSERT_TRUE(MD);
- ProfileSummary *PS = ProfileSummary::getFromMD(MD);
- ASSERT_TRUE(PS);
- VerifySummary(*PS);
- delete PS;
- // Test that summary can be attached to and read back from module.
- M.setProfileSummary(MD, ProfileSummary::PSK_Sample);
- MD = M.getProfileSummary(/* IsCS */ false);
- ASSERT_TRUE(MD);
- PS = ProfileSummary::getFromMD(MD);
- ASSERT_TRUE(PS);
- VerifySummary(*PS);
- delete PS;
- }
- void addFunctionSamples(StringMap<FunctionSamples> *Smap, const char *Fname,
- uint64_t TotalSamples, uint64_t HeadSamples) {
- StringRef Name(Fname);
- FunctionSamples FcnSamples;
- FcnSamples.setName(Name);
- FcnSamples.addTotalSamples(TotalSamples);
- FcnSamples.addHeadSamples(HeadSamples);
- FcnSamples.addBodySamples(1, 0, HeadSamples);
- (*Smap)[Name] = FcnSamples;
- }
- StringMap<FunctionSamples> setupFcnSamplesForElisionTest(StringRef Policy) {
- StringMap<FunctionSamples> Smap;
- addFunctionSamples(&Smap, "foo", uint64_t(20301), uint64_t(1437));
- if (Policy == "" || Policy == "all")
- return Smap;
- addFunctionSamples(&Smap, "foo.bar", uint64_t(20303), uint64_t(1439));
- if (Policy == "selected")
- return Smap;
- addFunctionSamples(&Smap, "foo.llvm.2465", uint64_t(20305), uint64_t(1441));
- return Smap;
- }
- void createFunctionWithSampleProfileElisionPolicy(Module *M,
- const char *Fname,
- StringRef Policy) {
- FunctionType *FnType =
- FunctionType::get(Type::getVoidTy(Context), {}, false);
- auto Inserted = M->getOrInsertFunction(Fname, FnType);
- auto Fcn = cast<Function>(Inserted.getCallee());
- if (Policy != "")
- Fcn->addFnAttr("sample-profile-suffix-elision-policy", Policy);
- }
- void setupModuleForElisionTest(Module *M, StringRef Policy) {
- createFunctionWithSampleProfileElisionPolicy(M, "foo", Policy);
- createFunctionWithSampleProfileElisionPolicy(M, "foo.bar", Policy);
- createFunctionWithSampleProfileElisionPolicy(M, "foo.llvm.2465", Policy);
- }
- void testSuffixElisionPolicy(SampleProfileFormat Format, StringRef Policy,
- const StringMap<uint64_t> &Expected) {
- SmallVector<char, 128> ProfilePath;
- std::error_code EC;
- EC = llvm::sys::fs::createTemporaryFile("profile", "", ProfilePath);
- ASSERT_TRUE(NoError(EC));
- StringRef ProfileFile(ProfilePath.data(), ProfilePath.size());
- Module M("my_module", Context);
- setupModuleForElisionTest(&M, Policy);
- StringMap<FunctionSamples> ProfMap = setupFcnSamplesForElisionTest(Policy);
- // write profile
- createWriter(Format, ProfileFile);
- EC = Writer->write(ProfMap);
- ASSERT_TRUE(NoError(EC));
- Writer->getOutputStream().flush();
- // read profile
- readProfile(M, ProfileFile);
- EC = Reader->read();
- ASSERT_TRUE(NoError(EC));
- for (auto I = Expected.begin(); I != Expected.end(); ++I) {
- uint64_t Esamples = uint64_t(-1);
- FunctionSamples *Samples = Reader->getSamplesFor(I->getKey());
- if (Samples != nullptr)
- Esamples = Samples->getTotalSamples();
- ASSERT_EQ(I->getValue(), Esamples);
- }
- }
- };
- TEST_F(SampleProfTest, roundtrip_text_profile) {
- testRoundTrip(SampleProfileFormat::SPF_Text, false);
- }
- TEST_F(SampleProfTest, roundtrip_raw_binary_profile) {
- testRoundTrip(SampleProfileFormat::SPF_Binary, false);
- }
- TEST_F(SampleProfTest, roundtrip_compact_binary_profile) {
- testRoundTrip(SampleProfileFormat::SPF_Compact_Binary, false);
- }
- TEST_F(SampleProfTest, roundtrip_ext_binary_profile) {
- testRoundTrip(SampleProfileFormat::SPF_Ext_Binary, false);
- }
- TEST_F(SampleProfTest, remap_text_profile) {
- testRoundTrip(SampleProfileFormat::SPF_Text, true);
- }
- TEST_F(SampleProfTest, remap_raw_binary_profile) {
- testRoundTrip(SampleProfileFormat::SPF_Binary, true);
- }
- TEST_F(SampleProfTest, remap_ext_binary_profile) {
- testRoundTrip(SampleProfileFormat::SPF_Ext_Binary, true);
- }
- TEST_F(SampleProfTest, sample_overflow_saturation) {
- const uint64_t Max = std::numeric_limits<uint64_t>::max();
- sampleprof_error Result;
- FunctionSamples FooSamples;
- Result = FooSamples.addTotalSamples(1);
- ASSERT_EQ(Result, sampleprof_error::success);
- Result = FooSamples.addHeadSamples(1);
- ASSERT_EQ(Result, sampleprof_error::success);
- Result = FooSamples.addBodySamples(10, 0, 1);
- ASSERT_EQ(Result, sampleprof_error::success);
- Result = FooSamples.addTotalSamples(Max);
- ASSERT_EQ(Result, sampleprof_error::counter_overflow);
- ASSERT_EQ(FooSamples.getTotalSamples(), Max);
- Result = FooSamples.addHeadSamples(Max);
- ASSERT_EQ(Result, sampleprof_error::counter_overflow);
- ASSERT_EQ(FooSamples.getHeadSamples(), Max);
- Result = FooSamples.addBodySamples(10, 0, Max);
- ASSERT_EQ(Result, sampleprof_error::counter_overflow);
- ErrorOr<uint64_t> BodySamples = FooSamples.findSamplesAt(10, 0);
- ASSERT_FALSE(BodySamples.getError());
- ASSERT_EQ(BodySamples.get(), Max);
- }
- TEST_F(SampleProfTest, default_suffix_elision_text) {
- // Default suffix elision policy: strip everything after first dot.
- // This implies that all suffix variants will map to "foo", so
- // we don't expect to see any entries for them in the sample
- // profile.
- StringMap<uint64_t> Expected;
- Expected["foo"] = uint64_t(20301);
- Expected["foo.bar"] = uint64_t(-1);
- Expected["foo.llvm.2465"] = uint64_t(-1);
- testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "", Expected);
- }
- TEST_F(SampleProfTest, default_suffix_elision_compact_binary) {
- // Default suffix elision policy: strip everything after first dot.
- // This implies that all suffix variants will map to "foo", so
- // we don't expect to see any entries for them in the sample
- // profile.
- StringMap<uint64_t> Expected;
- Expected["foo"] = uint64_t(20301);
- Expected["foo.bar"] = uint64_t(-1);
- Expected["foo.llvm.2465"] = uint64_t(-1);
- testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "",
- Expected);
- }
- TEST_F(SampleProfTest, selected_suffix_elision_text) {
- // Profile is created and searched using the "selected"
- // suffix elision policy: we only strip a .XXX suffix if
- // it matches a pattern known to be generated by the compiler
- // (e.g. ".llvm.<digits>").
- StringMap<uint64_t> Expected;
- Expected["foo"] = uint64_t(20301);
- Expected["foo.bar"] = uint64_t(20303);
- Expected["foo.llvm.2465"] = uint64_t(-1);
- testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "selected", Expected);
- }
- TEST_F(SampleProfTest, selected_suffix_elision_compact_binary) {
- // Profile is created and searched using the "selected"
- // suffix elision policy: we only strip a .XXX suffix if
- // it matches a pattern known to be generated by the compiler
- // (e.g. ".llvm.<digits>").
- StringMap<uint64_t> Expected;
- Expected["foo"] = uint64_t(20301);
- Expected["foo.bar"] = uint64_t(20303);
- Expected["foo.llvm.2465"] = uint64_t(-1);
- testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "selected",
- Expected);
- }
- TEST_F(SampleProfTest, none_suffix_elision_text) {
- // Profile is created and searched using the "none"
- // suffix elision policy: no stripping of suffixes at all.
- // Here we expect to see all variants in the profile.
- StringMap<uint64_t> Expected;
- Expected["foo"] = uint64_t(20301);
- Expected["foo.bar"] = uint64_t(20303);
- Expected["foo.llvm.2465"] = uint64_t(20305);
- testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "none", Expected);
- }
- TEST_F(SampleProfTest, none_suffix_elision_compact_binary) {
- // Profile is created and searched using the "none"
- // suffix elision policy: no stripping of suffixes at all.
- // Here we expect to see all variants in the profile.
- StringMap<uint64_t> Expected;
- Expected["foo"] = uint64_t(20301);
- Expected["foo.bar"] = uint64_t(20303);
- Expected["foo.llvm.2465"] = uint64_t(20305);
- testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "none",
- Expected);
- }
- } // end anonymous namespace
|