SampleProfTest.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. //===- unittest/ProfileData/SampleProfTest.cpp ------------------*- C++ -*-===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "llvm/ProfileData/SampleProf.h"
  9. #include "llvm/ADT/StringMap.h"
  10. #include "llvm/ADT/StringRef.h"
  11. #include "llvm/IR/LLVMContext.h"
  12. #include "llvm/IR/Metadata.h"
  13. #include "llvm/IR/Module.h"
  14. #include "llvm/ProfileData/SampleProfReader.h"
  15. #include "llvm/ProfileData/SampleProfWriter.h"
  16. #include "llvm/Support/Casting.h"
  17. #include "llvm/Support/ErrorOr.h"
  18. #include "llvm/Support/MemoryBuffer.h"
  19. #include "llvm/Support/raw_ostream.h"
  20. #include "gtest/gtest.h"
  21. #include <string>
  22. #include <vector>
  23. using namespace llvm;
  24. using namespace sampleprof;
  25. static ::testing::AssertionResult NoError(std::error_code EC) {
  26. if (!EC)
  27. return ::testing::AssertionSuccess();
  28. return ::testing::AssertionFailure() << "error " << EC.value() << ": "
  29. << EC.message();
  30. }
  31. namespace {
  32. struct SampleProfTest : ::testing::Test {
  33. LLVMContext Context;
  34. std::unique_ptr<SampleProfileWriter> Writer;
  35. std::unique_ptr<SampleProfileReader> Reader;
  36. SampleProfTest() : Writer(), Reader() {}
  37. void createWriter(SampleProfileFormat Format, StringRef Profile) {
  38. std::error_code EC;
  39. std::unique_ptr<raw_ostream> OS(
  40. new raw_fd_ostream(Profile, EC, sys::fs::OF_None));
  41. auto WriterOrErr = SampleProfileWriter::create(OS, Format);
  42. ASSERT_TRUE(NoError(WriterOrErr.getError()));
  43. Writer = std::move(WriterOrErr.get());
  44. }
  45. void readProfile(const Module &M, StringRef Profile) {
  46. auto ReaderOrErr = SampleProfileReader::create(Profile, Context);
  47. ASSERT_TRUE(NoError(ReaderOrErr.getError()));
  48. Reader = std::move(ReaderOrErr.get());
  49. Reader->collectFuncsToUse(M);
  50. }
  51. void testRoundTrip(SampleProfileFormat Format, bool Remap) {
  52. SmallVector<char, 128> ProfilePath;
  53. ASSERT_TRUE(NoError(llvm::sys::fs::createTemporaryFile("profile", "", ProfilePath)));
  54. StringRef Profile(ProfilePath.data(), ProfilePath.size());
  55. createWriter(Format, Profile);
  56. StringRef FooName("_Z3fooi");
  57. FunctionSamples FooSamples;
  58. FooSamples.setName(FooName);
  59. FooSamples.addTotalSamples(7711);
  60. FooSamples.addHeadSamples(610);
  61. FooSamples.addBodySamples(1, 0, 610);
  62. FooSamples.addBodySamples(2, 0, 600);
  63. FooSamples.addBodySamples(4, 0, 60000);
  64. FooSamples.addBodySamples(8, 0, 60351);
  65. FooSamples.addBodySamples(10, 0, 605);
  66. StringRef BarName("_Z3bari");
  67. FunctionSamples BarSamples;
  68. BarSamples.setName(BarName);
  69. BarSamples.addTotalSamples(20301);
  70. BarSamples.addHeadSamples(1437);
  71. BarSamples.addBodySamples(1, 0, 1437);
  72. // Test how reader/writer handles unmangled names.
  73. StringRef MconstructName("_M_construct<char *>");
  74. StringRef StringviewName("string_view<std::allocator<char> >");
  75. BarSamples.addCalledTargetSamples(1, 0, MconstructName, 1000);
  76. BarSamples.addCalledTargetSamples(1, 0, StringviewName, 437);
  77. Module M("my_module", Context);
  78. FunctionType *fn_type =
  79. FunctionType::get(Type::getVoidTy(Context), {}, false);
  80. M.getOrInsertFunction(FooName, fn_type);
  81. M.getOrInsertFunction(BarName, fn_type);
  82. StringMap<FunctionSamples> Profiles;
  83. Profiles[FooName] = std::move(FooSamples);
  84. Profiles[BarName] = std::move(BarSamples);
  85. std::error_code EC;
  86. EC = Writer->write(Profiles);
  87. ASSERT_TRUE(NoError(EC));
  88. Writer->getOutputStream().flush();
  89. readProfile(M, Profile);
  90. EC = Reader->read();
  91. ASSERT_TRUE(NoError(EC));
  92. if (Remap) {
  93. auto MemBuffer = llvm::MemoryBuffer::getMemBuffer(R"(
  94. # Types 'int' and 'long' are equivalent
  95. type i l
  96. # Function names 'foo' and 'faux' are equivalent
  97. name 3foo 4faux
  98. )");
  99. Reader.reset(new SampleProfileReaderItaniumRemapper(
  100. std::move(MemBuffer), Context, std::move(Reader)));
  101. FooName = "_Z4fauxi";
  102. BarName = "_Z3barl";
  103. EC = Reader->read();
  104. ASSERT_TRUE(NoError(EC));
  105. }
  106. ASSERT_EQ(2u, Reader->getProfiles().size());
  107. FunctionSamples *ReadFooSamples = Reader->getSamplesFor(FooName);
  108. ASSERT_TRUE(ReadFooSamples != nullptr);
  109. if (Format != SampleProfileFormat::SPF_Compact_Binary) {
  110. ASSERT_EQ("_Z3fooi", ReadFooSamples->getName());
  111. }
  112. ASSERT_EQ(7711u, ReadFooSamples->getTotalSamples());
  113. ASSERT_EQ(610u, ReadFooSamples->getHeadSamples());
  114. FunctionSamples *ReadBarSamples = Reader->getSamplesFor(BarName);
  115. ASSERT_TRUE(ReadBarSamples != nullptr);
  116. if (Format != SampleProfileFormat::SPF_Compact_Binary) {
  117. ASSERT_EQ("_Z3bari", ReadBarSamples->getName());
  118. }
  119. ASSERT_EQ(20301u, ReadBarSamples->getTotalSamples());
  120. ASSERT_EQ(1437u, ReadBarSamples->getHeadSamples());
  121. ErrorOr<SampleRecord::CallTargetMap> CTMap =
  122. ReadBarSamples->findCallTargetMapAt(1, 0);
  123. ASSERT_FALSE(CTMap.getError());
  124. std::string MconstructGUID;
  125. StringRef MconstructRep =
  126. getRepInFormat(MconstructName, Format, MconstructGUID);
  127. std::string StringviewGUID;
  128. StringRef StringviewRep =
  129. getRepInFormat(StringviewName, Format, StringviewGUID);
  130. ASSERT_EQ(1000u, CTMap.get()[MconstructRep]);
  131. ASSERT_EQ(437u, CTMap.get()[StringviewRep]);
  132. auto VerifySummary = [](ProfileSummary &Summary) mutable {
  133. ASSERT_EQ(ProfileSummary::PSK_Sample, Summary.getKind());
  134. ASSERT_EQ(123603u, Summary.getTotalCount());
  135. ASSERT_EQ(6u, Summary.getNumCounts());
  136. ASSERT_EQ(2u, Summary.getNumFunctions());
  137. ASSERT_EQ(1437u, Summary.getMaxFunctionCount());
  138. ASSERT_EQ(60351u, Summary.getMaxCount());
  139. uint32_t Cutoff = 800000;
  140. auto Predicate = [&Cutoff](const ProfileSummaryEntry &PE) {
  141. return PE.Cutoff == Cutoff;
  142. };
  143. std::vector<ProfileSummaryEntry> &Details = Summary.getDetailedSummary();
  144. auto EightyPerc = find_if(Details, Predicate);
  145. Cutoff = 900000;
  146. auto NinetyPerc = find_if(Details, Predicate);
  147. Cutoff = 950000;
  148. auto NinetyFivePerc = find_if(Details, Predicate);
  149. Cutoff = 990000;
  150. auto NinetyNinePerc = find_if(Details, Predicate);
  151. ASSERT_EQ(60000u, EightyPerc->MinCount);
  152. ASSERT_EQ(60000u, NinetyPerc->MinCount);
  153. ASSERT_EQ(60000u, NinetyFivePerc->MinCount);
  154. ASSERT_EQ(610u, NinetyNinePerc->MinCount);
  155. };
  156. ProfileSummary &Summary = Reader->getSummary();
  157. VerifySummary(Summary);
  158. // Test that conversion of summary to and from Metadata works.
  159. Metadata *MD = Summary.getMD(Context);
  160. ASSERT_TRUE(MD);
  161. ProfileSummary *PS = ProfileSummary::getFromMD(MD);
  162. ASSERT_TRUE(PS);
  163. VerifySummary(*PS);
  164. delete PS;
  165. // Test that summary can be attached to and read back from module.
  166. M.setProfileSummary(MD, ProfileSummary::PSK_Sample);
  167. MD = M.getProfileSummary(/* IsCS */ false);
  168. ASSERT_TRUE(MD);
  169. PS = ProfileSummary::getFromMD(MD);
  170. ASSERT_TRUE(PS);
  171. VerifySummary(*PS);
  172. delete PS;
  173. }
  174. void addFunctionSamples(StringMap<FunctionSamples> *Smap, const char *Fname,
  175. uint64_t TotalSamples, uint64_t HeadSamples) {
  176. StringRef Name(Fname);
  177. FunctionSamples FcnSamples;
  178. FcnSamples.setName(Name);
  179. FcnSamples.addTotalSamples(TotalSamples);
  180. FcnSamples.addHeadSamples(HeadSamples);
  181. FcnSamples.addBodySamples(1, 0, HeadSamples);
  182. (*Smap)[Name] = FcnSamples;
  183. }
  184. StringMap<FunctionSamples> setupFcnSamplesForElisionTest(StringRef Policy) {
  185. StringMap<FunctionSamples> Smap;
  186. addFunctionSamples(&Smap, "foo", uint64_t(20301), uint64_t(1437));
  187. if (Policy == "" || Policy == "all")
  188. return Smap;
  189. addFunctionSamples(&Smap, "foo.bar", uint64_t(20303), uint64_t(1439));
  190. if (Policy == "selected")
  191. return Smap;
  192. addFunctionSamples(&Smap, "foo.llvm.2465", uint64_t(20305), uint64_t(1441));
  193. return Smap;
  194. }
  195. void createFunctionWithSampleProfileElisionPolicy(Module *M,
  196. const char *Fname,
  197. StringRef Policy) {
  198. FunctionType *FnType =
  199. FunctionType::get(Type::getVoidTy(Context), {}, false);
  200. auto Inserted = M->getOrInsertFunction(Fname, FnType);
  201. auto Fcn = cast<Function>(Inserted.getCallee());
  202. if (Policy != "")
  203. Fcn->addFnAttr("sample-profile-suffix-elision-policy", Policy);
  204. }
  205. void setupModuleForElisionTest(Module *M, StringRef Policy) {
  206. createFunctionWithSampleProfileElisionPolicy(M, "foo", Policy);
  207. createFunctionWithSampleProfileElisionPolicy(M, "foo.bar", Policy);
  208. createFunctionWithSampleProfileElisionPolicy(M, "foo.llvm.2465", Policy);
  209. }
  210. void testSuffixElisionPolicy(SampleProfileFormat Format, StringRef Policy,
  211. const StringMap<uint64_t> &Expected) {
  212. SmallVector<char, 128> ProfilePath;
  213. std::error_code EC;
  214. EC = llvm::sys::fs::createTemporaryFile("profile", "", ProfilePath);
  215. ASSERT_TRUE(NoError(EC));
  216. StringRef ProfileFile(ProfilePath.data(), ProfilePath.size());
  217. Module M("my_module", Context);
  218. setupModuleForElisionTest(&M, Policy);
  219. StringMap<FunctionSamples> ProfMap = setupFcnSamplesForElisionTest(Policy);
  220. // write profile
  221. createWriter(Format, ProfileFile);
  222. EC = Writer->write(ProfMap);
  223. ASSERT_TRUE(NoError(EC));
  224. Writer->getOutputStream().flush();
  225. // read profile
  226. readProfile(M, ProfileFile);
  227. EC = Reader->read();
  228. ASSERT_TRUE(NoError(EC));
  229. for (auto I = Expected.begin(); I != Expected.end(); ++I) {
  230. uint64_t Esamples = uint64_t(-1);
  231. FunctionSamples *Samples = Reader->getSamplesFor(I->getKey());
  232. if (Samples != nullptr)
  233. Esamples = Samples->getTotalSamples();
  234. ASSERT_EQ(I->getValue(), Esamples);
  235. }
  236. }
  237. };
  238. TEST_F(SampleProfTest, roundtrip_text_profile) {
  239. testRoundTrip(SampleProfileFormat::SPF_Text, false);
  240. }
  241. TEST_F(SampleProfTest, roundtrip_raw_binary_profile) {
  242. testRoundTrip(SampleProfileFormat::SPF_Binary, false);
  243. }
  244. TEST_F(SampleProfTest, roundtrip_compact_binary_profile) {
  245. testRoundTrip(SampleProfileFormat::SPF_Compact_Binary, false);
  246. }
  247. TEST_F(SampleProfTest, remap_text_profile) {
  248. testRoundTrip(SampleProfileFormat::SPF_Text, true);
  249. }
  250. TEST_F(SampleProfTest, remap_raw_binary_profile) {
  251. testRoundTrip(SampleProfileFormat::SPF_Binary, true);
  252. }
  253. TEST_F(SampleProfTest, sample_overflow_saturation) {
  254. const uint64_t Max = std::numeric_limits<uint64_t>::max();
  255. sampleprof_error Result;
  256. StringRef FooName("_Z3fooi");
  257. FunctionSamples FooSamples;
  258. Result = FooSamples.addTotalSamples(1);
  259. ASSERT_EQ(Result, sampleprof_error::success);
  260. Result = FooSamples.addHeadSamples(1);
  261. ASSERT_EQ(Result, sampleprof_error::success);
  262. Result = FooSamples.addBodySamples(10, 0, 1);
  263. ASSERT_EQ(Result, sampleprof_error::success);
  264. Result = FooSamples.addTotalSamples(Max);
  265. ASSERT_EQ(Result, sampleprof_error::counter_overflow);
  266. ASSERT_EQ(FooSamples.getTotalSamples(), Max);
  267. Result = FooSamples.addHeadSamples(Max);
  268. ASSERT_EQ(Result, sampleprof_error::counter_overflow);
  269. ASSERT_EQ(FooSamples.getHeadSamples(), Max);
  270. Result = FooSamples.addBodySamples(10, 0, Max);
  271. ASSERT_EQ(Result, sampleprof_error::counter_overflow);
  272. ErrorOr<uint64_t> BodySamples = FooSamples.findSamplesAt(10, 0);
  273. ASSERT_FALSE(BodySamples.getError());
  274. ASSERT_EQ(BodySamples.get(), Max);
  275. }
  276. TEST_F(SampleProfTest, default_suffix_elision_text) {
  277. // Default suffix elision policy: strip everything after first dot.
  278. // This implies that all suffix variants will map to "foo", so
  279. // we don't expect to see any entries for them in the sample
  280. // profile.
  281. StringMap<uint64_t> Expected;
  282. Expected["foo"] = uint64_t(20301);
  283. Expected["foo.bar"] = uint64_t(-1);
  284. Expected["foo.llvm.2465"] = uint64_t(-1);
  285. testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "", Expected);
  286. }
  287. TEST_F(SampleProfTest, default_suffix_elision_compact_binary) {
  288. // Default suffix elision policy: strip everything after first dot.
  289. // This implies that all suffix variants will map to "foo", so
  290. // we don't expect to see any entries for them in the sample
  291. // profile.
  292. StringMap<uint64_t> Expected;
  293. Expected["foo"] = uint64_t(20301);
  294. Expected["foo.bar"] = uint64_t(-1);
  295. Expected["foo.llvm.2465"] = uint64_t(-1);
  296. testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "",
  297. Expected);
  298. }
  299. TEST_F(SampleProfTest, selected_suffix_elision_text) {
  300. // Profile is created and searched using the "selected"
  301. // suffix elision policy: we only strip a .XXX suffix if
  302. // it matches a pattern known to be generated by the compiler
  303. // (e.g. ".llvm.<digits>").
  304. StringMap<uint64_t> Expected;
  305. Expected["foo"] = uint64_t(20301);
  306. Expected["foo.bar"] = uint64_t(20303);
  307. Expected["foo.llvm.2465"] = uint64_t(-1);
  308. testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "selected", Expected);
  309. }
  310. TEST_F(SampleProfTest, selected_suffix_elision_compact_binary) {
  311. // Profile is created and searched using the "selected"
  312. // suffix elision policy: we only strip a .XXX suffix if
  313. // it matches a pattern known to be generated by the compiler
  314. // (e.g. ".llvm.<digits>").
  315. StringMap<uint64_t> Expected;
  316. Expected["foo"] = uint64_t(20301);
  317. Expected["foo.bar"] = uint64_t(20303);
  318. Expected["foo.llvm.2465"] = uint64_t(-1);
  319. testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "selected",
  320. Expected);
  321. }
  322. TEST_F(SampleProfTest, none_suffix_elision_text) {
  323. // Profile is created and searched using the "none"
  324. // suffix elision policy: no stripping of suffixes at all.
  325. // Here we expect to see all variants in the profile.
  326. StringMap<uint64_t> Expected;
  327. Expected["foo"] = uint64_t(20301);
  328. Expected["foo.bar"] = uint64_t(20303);
  329. Expected["foo.llvm.2465"] = uint64_t(20305);
  330. testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "none", Expected);
  331. }
  332. TEST_F(SampleProfTest, none_suffix_elision_compact_binary) {
  333. // Profile is created and searched using the "none"
  334. // suffix elision policy: no stripping of suffixes at all.
  335. // Here we expect to see all variants in the profile.
  336. StringMap<uint64_t> Expected;
  337. Expected["foo"] = uint64_t(20301);
  338. Expected["foo.bar"] = uint64_t(20303);
  339. Expected["foo.llvm.2465"] = uint64_t(20305);
  340. testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "none",
  341. Expected);
  342. }
  343. } // end anonymous namespace