SampleProfTest.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  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. StringRef RemapFile = "") {
  47. auto ReaderOrErr = SampleProfileReader::create(Profile, Context, RemapFile);
  48. ASSERT_TRUE(NoError(ReaderOrErr.getError()));
  49. Reader = std::move(ReaderOrErr.get());
  50. Reader->collectFuncsFrom(M);
  51. }
  52. void createRemapFile(SmallVectorImpl<char> &RemapPath, StringRef &RemapFile) {
  53. std::error_code EC =
  54. llvm::sys::fs::createTemporaryFile("remapfile", "", RemapPath);
  55. ASSERT_TRUE(NoError(EC));
  56. RemapFile = StringRef(RemapPath.data(), RemapPath.size());
  57. std::unique_ptr<raw_fd_ostream> OS(
  58. new raw_fd_ostream(RemapFile, EC, sys::fs::OF_None));
  59. *OS << R"(
  60. # Types 'int' and 'long' are equivalent
  61. type i l
  62. # Function names 'foo' and 'faux' are equivalent
  63. name 3foo 4faux
  64. )";
  65. OS->close();
  66. }
  67. void testRoundTrip(SampleProfileFormat Format, bool Remap) {
  68. SmallVector<char, 128> ProfilePath;
  69. ASSERT_TRUE(NoError(llvm::sys::fs::createTemporaryFile("profile", "", ProfilePath)));
  70. StringRef Profile(ProfilePath.data(), ProfilePath.size());
  71. createWriter(Format, Profile);
  72. StringRef FooName("_Z3fooi");
  73. FunctionSamples FooSamples;
  74. FooSamples.setName(FooName);
  75. FooSamples.addTotalSamples(7711);
  76. FooSamples.addHeadSamples(610);
  77. FooSamples.addBodySamples(1, 0, 610);
  78. FooSamples.addBodySamples(2, 0, 600);
  79. FooSamples.addBodySamples(4, 0, 60000);
  80. FooSamples.addBodySamples(8, 0, 60351);
  81. FooSamples.addBodySamples(10, 0, 605);
  82. StringRef BarName("_Z3bari");
  83. FunctionSamples BarSamples;
  84. BarSamples.setName(BarName);
  85. BarSamples.addTotalSamples(20301);
  86. BarSamples.addHeadSamples(1437);
  87. BarSamples.addBodySamples(1, 0, 1437);
  88. // Test how reader/writer handles unmangled names.
  89. StringRef MconstructName("_M_construct<char *>");
  90. StringRef StringviewName("string_view<std::allocator<char> >");
  91. BarSamples.addCalledTargetSamples(1, 0, MconstructName, 1000);
  92. BarSamples.addCalledTargetSamples(1, 0, StringviewName, 437);
  93. StringRef BazName("_Z3bazi");
  94. FunctionSamples BazSamples;
  95. BazSamples.setName(BazName);
  96. BazSamples.addTotalSamples(12557);
  97. BazSamples.addHeadSamples(1257);
  98. BazSamples.addBodySamples(1, 0, 12557);
  99. StringRef BooName("_Z3booi");
  100. FunctionSamples BooSamples;
  101. BooSamples.setName(BooName);
  102. BooSamples.addTotalSamples(1232);
  103. BooSamples.addHeadSamples(1);
  104. BooSamples.addBodySamples(1, 0, 1232);
  105. StringMap<FunctionSamples> Profiles;
  106. Profiles[FooName] = std::move(FooSamples);
  107. Profiles[BarName] = std::move(BarSamples);
  108. Profiles[BazName] = std::move(BazSamples);
  109. Profiles[BooName] = std::move(BooSamples);
  110. Module M("my_module", Context);
  111. FunctionType *fn_type =
  112. FunctionType::get(Type::getVoidTy(Context), {}, false);
  113. SmallVector<char, 128> RemapPath;
  114. StringRef RemapFile;
  115. if (Remap) {
  116. createRemapFile(RemapPath, RemapFile);
  117. FooName = "_Z4fauxi";
  118. BarName = "_Z3barl";
  119. }
  120. M.getOrInsertFunction(FooName, fn_type);
  121. M.getOrInsertFunction(BarName, fn_type);
  122. M.getOrInsertFunction(BooName, fn_type);
  123. ProfileSymbolList List;
  124. if (Format == SampleProfileFormat::SPF_Ext_Binary) {
  125. List.add("zoo", true);
  126. List.add("moo", true);
  127. }
  128. Writer->setProfileSymbolList(&List);
  129. std::error_code EC;
  130. EC = Writer->write(Profiles);
  131. ASSERT_TRUE(NoError(EC));
  132. Writer->getOutputStream().flush();
  133. readProfile(M, Profile, RemapFile);
  134. EC = Reader->read();
  135. ASSERT_TRUE(NoError(EC));
  136. if (Format == SampleProfileFormat::SPF_Ext_Binary) {
  137. std::unique_ptr<ProfileSymbolList> ReaderList =
  138. Reader->getProfileSymbolList();
  139. ReaderList->contains("zoo");
  140. ReaderList->contains("moo");
  141. }
  142. FunctionSamples *ReadFooSamples = Reader->getSamplesFor(FooName);
  143. ASSERT_TRUE(ReadFooSamples != nullptr);
  144. if (Format != SampleProfileFormat::SPF_Compact_Binary) {
  145. ASSERT_EQ("_Z3fooi", ReadFooSamples->getName());
  146. }
  147. ASSERT_EQ(7711u, ReadFooSamples->getTotalSamples());
  148. ASSERT_EQ(610u, ReadFooSamples->getHeadSamples());
  149. FunctionSamples *ReadBarSamples = Reader->getSamplesFor(BarName);
  150. ASSERT_TRUE(ReadBarSamples != nullptr);
  151. if (Format != SampleProfileFormat::SPF_Compact_Binary) {
  152. ASSERT_EQ("_Z3bari", ReadBarSamples->getName());
  153. }
  154. ASSERT_EQ(20301u, ReadBarSamples->getTotalSamples());
  155. ASSERT_EQ(1437u, ReadBarSamples->getHeadSamples());
  156. ErrorOr<SampleRecord::CallTargetMap> CTMap =
  157. ReadBarSamples->findCallTargetMapAt(1, 0);
  158. ASSERT_FALSE(CTMap.getError());
  159. // Because _Z3bazi is not defined in module M, expect _Z3bazi's profile
  160. // is not loaded when the profile is ExtBinary or Compact format because
  161. // these formats support loading function profiles on demand.
  162. FunctionSamples *ReadBazSamples = Reader->getSamplesFor(BazName);
  163. if (Format == SampleProfileFormat::SPF_Ext_Binary ||
  164. Format == SampleProfileFormat::SPF_Compact_Binary) {
  165. ASSERT_TRUE(ReadBazSamples == nullptr);
  166. ASSERT_EQ(3u, Reader->getProfiles().size());
  167. } else {
  168. ASSERT_TRUE(ReadBazSamples != nullptr);
  169. ASSERT_EQ(12557u, ReadBazSamples->getTotalSamples());
  170. ASSERT_EQ(4u, Reader->getProfiles().size());
  171. }
  172. FunctionSamples *ReadBooSamples = Reader->getSamplesFor(BooName);
  173. ASSERT_TRUE(ReadBooSamples != nullptr);
  174. ASSERT_EQ(1232u, ReadBooSamples->getTotalSamples());
  175. std::string MconstructGUID;
  176. StringRef MconstructRep =
  177. getRepInFormat(MconstructName, Format, MconstructGUID);
  178. std::string StringviewGUID;
  179. StringRef StringviewRep =
  180. getRepInFormat(StringviewName, Format, StringviewGUID);
  181. ASSERT_EQ(1000u, CTMap.get()[MconstructRep]);
  182. ASSERT_EQ(437u, CTMap.get()[StringviewRep]);
  183. auto VerifySummary = [](ProfileSummary &Summary) mutable {
  184. ASSERT_EQ(ProfileSummary::PSK_Sample, Summary.getKind());
  185. ASSERT_EQ(137392u, Summary.getTotalCount());
  186. ASSERT_EQ(8u, Summary.getNumCounts());
  187. ASSERT_EQ(4u, Summary.getNumFunctions());
  188. ASSERT_EQ(1437u, Summary.getMaxFunctionCount());
  189. ASSERT_EQ(60351u, Summary.getMaxCount());
  190. uint32_t Cutoff = 800000;
  191. auto Predicate = [&Cutoff](const ProfileSummaryEntry &PE) {
  192. return PE.Cutoff == Cutoff;
  193. };
  194. std::vector<ProfileSummaryEntry> &Details = Summary.getDetailedSummary();
  195. auto EightyPerc = find_if(Details, Predicate);
  196. Cutoff = 900000;
  197. auto NinetyPerc = find_if(Details, Predicate);
  198. Cutoff = 950000;
  199. auto NinetyFivePerc = find_if(Details, Predicate);
  200. Cutoff = 990000;
  201. auto NinetyNinePerc = find_if(Details, Predicate);
  202. ASSERT_EQ(60000u, EightyPerc->MinCount);
  203. ASSERT_EQ(12557u, NinetyPerc->MinCount);
  204. ASSERT_EQ(12557u, NinetyFivePerc->MinCount);
  205. ASSERT_EQ(610u, NinetyNinePerc->MinCount);
  206. };
  207. ProfileSummary &Summary = Reader->getSummary();
  208. VerifySummary(Summary);
  209. // Test that conversion of summary to and from Metadata works.
  210. Metadata *MD = Summary.getMD(Context);
  211. ASSERT_TRUE(MD);
  212. ProfileSummary *PS = ProfileSummary::getFromMD(MD);
  213. ASSERT_TRUE(PS);
  214. VerifySummary(*PS);
  215. delete PS;
  216. // Test that summary can be attached to and read back from module.
  217. M.setProfileSummary(MD, ProfileSummary::PSK_Sample);
  218. MD = M.getProfileSummary(/* IsCS */ false);
  219. ASSERT_TRUE(MD);
  220. PS = ProfileSummary::getFromMD(MD);
  221. ASSERT_TRUE(PS);
  222. VerifySummary(*PS);
  223. delete PS;
  224. }
  225. void addFunctionSamples(StringMap<FunctionSamples> *Smap, const char *Fname,
  226. uint64_t TotalSamples, uint64_t HeadSamples) {
  227. StringRef Name(Fname);
  228. FunctionSamples FcnSamples;
  229. FcnSamples.setName(Name);
  230. FcnSamples.addTotalSamples(TotalSamples);
  231. FcnSamples.addHeadSamples(HeadSamples);
  232. FcnSamples.addBodySamples(1, 0, HeadSamples);
  233. (*Smap)[Name] = FcnSamples;
  234. }
  235. StringMap<FunctionSamples> setupFcnSamplesForElisionTest(StringRef Policy) {
  236. StringMap<FunctionSamples> Smap;
  237. addFunctionSamples(&Smap, "foo", uint64_t(20301), uint64_t(1437));
  238. if (Policy == "" || Policy == "all")
  239. return Smap;
  240. addFunctionSamples(&Smap, "foo.bar", uint64_t(20303), uint64_t(1439));
  241. if (Policy == "selected")
  242. return Smap;
  243. addFunctionSamples(&Smap, "foo.llvm.2465", uint64_t(20305), uint64_t(1441));
  244. return Smap;
  245. }
  246. void createFunctionWithSampleProfileElisionPolicy(Module *M,
  247. const char *Fname,
  248. StringRef Policy) {
  249. FunctionType *FnType =
  250. FunctionType::get(Type::getVoidTy(Context), {}, false);
  251. auto Inserted = M->getOrInsertFunction(Fname, FnType);
  252. auto Fcn = cast<Function>(Inserted.getCallee());
  253. if (Policy != "")
  254. Fcn->addFnAttr("sample-profile-suffix-elision-policy", Policy);
  255. }
  256. void setupModuleForElisionTest(Module *M, StringRef Policy) {
  257. createFunctionWithSampleProfileElisionPolicy(M, "foo", Policy);
  258. createFunctionWithSampleProfileElisionPolicy(M, "foo.bar", Policy);
  259. createFunctionWithSampleProfileElisionPolicy(M, "foo.llvm.2465", Policy);
  260. }
  261. void testSuffixElisionPolicy(SampleProfileFormat Format, StringRef Policy,
  262. const StringMap<uint64_t> &Expected) {
  263. SmallVector<char, 128> ProfilePath;
  264. std::error_code EC;
  265. EC = llvm::sys::fs::createTemporaryFile("profile", "", ProfilePath);
  266. ASSERT_TRUE(NoError(EC));
  267. StringRef ProfileFile(ProfilePath.data(), ProfilePath.size());
  268. Module M("my_module", Context);
  269. setupModuleForElisionTest(&M, Policy);
  270. StringMap<FunctionSamples> ProfMap = setupFcnSamplesForElisionTest(Policy);
  271. // write profile
  272. createWriter(Format, ProfileFile);
  273. EC = Writer->write(ProfMap);
  274. ASSERT_TRUE(NoError(EC));
  275. Writer->getOutputStream().flush();
  276. // read profile
  277. readProfile(M, ProfileFile);
  278. EC = Reader->read();
  279. ASSERT_TRUE(NoError(EC));
  280. for (auto I = Expected.begin(); I != Expected.end(); ++I) {
  281. uint64_t Esamples = uint64_t(-1);
  282. FunctionSamples *Samples = Reader->getSamplesFor(I->getKey());
  283. if (Samples != nullptr)
  284. Esamples = Samples->getTotalSamples();
  285. ASSERT_EQ(I->getValue(), Esamples);
  286. }
  287. }
  288. };
  289. TEST_F(SampleProfTest, roundtrip_text_profile) {
  290. testRoundTrip(SampleProfileFormat::SPF_Text, false);
  291. }
  292. TEST_F(SampleProfTest, roundtrip_raw_binary_profile) {
  293. testRoundTrip(SampleProfileFormat::SPF_Binary, false);
  294. }
  295. TEST_F(SampleProfTest, roundtrip_compact_binary_profile) {
  296. testRoundTrip(SampleProfileFormat::SPF_Compact_Binary, false);
  297. }
  298. TEST_F(SampleProfTest, roundtrip_ext_binary_profile) {
  299. testRoundTrip(SampleProfileFormat::SPF_Ext_Binary, false);
  300. }
  301. TEST_F(SampleProfTest, remap_text_profile) {
  302. testRoundTrip(SampleProfileFormat::SPF_Text, true);
  303. }
  304. TEST_F(SampleProfTest, remap_raw_binary_profile) {
  305. testRoundTrip(SampleProfileFormat::SPF_Binary, true);
  306. }
  307. TEST_F(SampleProfTest, remap_ext_binary_profile) {
  308. testRoundTrip(SampleProfileFormat::SPF_Ext_Binary, true);
  309. }
  310. TEST_F(SampleProfTest, sample_overflow_saturation) {
  311. const uint64_t Max = std::numeric_limits<uint64_t>::max();
  312. sampleprof_error Result;
  313. FunctionSamples FooSamples;
  314. Result = FooSamples.addTotalSamples(1);
  315. ASSERT_EQ(Result, sampleprof_error::success);
  316. Result = FooSamples.addHeadSamples(1);
  317. ASSERT_EQ(Result, sampleprof_error::success);
  318. Result = FooSamples.addBodySamples(10, 0, 1);
  319. ASSERT_EQ(Result, sampleprof_error::success);
  320. Result = FooSamples.addTotalSamples(Max);
  321. ASSERT_EQ(Result, sampleprof_error::counter_overflow);
  322. ASSERT_EQ(FooSamples.getTotalSamples(), Max);
  323. Result = FooSamples.addHeadSamples(Max);
  324. ASSERT_EQ(Result, sampleprof_error::counter_overflow);
  325. ASSERT_EQ(FooSamples.getHeadSamples(), Max);
  326. Result = FooSamples.addBodySamples(10, 0, Max);
  327. ASSERT_EQ(Result, sampleprof_error::counter_overflow);
  328. ErrorOr<uint64_t> BodySamples = FooSamples.findSamplesAt(10, 0);
  329. ASSERT_FALSE(BodySamples.getError());
  330. ASSERT_EQ(BodySamples.get(), Max);
  331. }
  332. TEST_F(SampleProfTest, default_suffix_elision_text) {
  333. // Default suffix elision policy: strip everything after first dot.
  334. // This implies that all suffix variants will map to "foo", so
  335. // we don't expect to see any entries for them in the sample
  336. // profile.
  337. StringMap<uint64_t> Expected;
  338. Expected["foo"] = uint64_t(20301);
  339. Expected["foo.bar"] = uint64_t(-1);
  340. Expected["foo.llvm.2465"] = uint64_t(-1);
  341. testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "", Expected);
  342. }
  343. TEST_F(SampleProfTest, default_suffix_elision_compact_binary) {
  344. // Default suffix elision policy: strip everything after first dot.
  345. // This implies that all suffix variants will map to "foo", so
  346. // we don't expect to see any entries for them in the sample
  347. // profile.
  348. StringMap<uint64_t> Expected;
  349. Expected["foo"] = uint64_t(20301);
  350. Expected["foo.bar"] = uint64_t(-1);
  351. Expected["foo.llvm.2465"] = uint64_t(-1);
  352. testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "",
  353. Expected);
  354. }
  355. TEST_F(SampleProfTest, selected_suffix_elision_text) {
  356. // Profile is created and searched using the "selected"
  357. // suffix elision policy: we only strip a .XXX suffix if
  358. // it matches a pattern known to be generated by the compiler
  359. // (e.g. ".llvm.<digits>").
  360. StringMap<uint64_t> Expected;
  361. Expected["foo"] = uint64_t(20301);
  362. Expected["foo.bar"] = uint64_t(20303);
  363. Expected["foo.llvm.2465"] = uint64_t(-1);
  364. testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "selected", Expected);
  365. }
  366. TEST_F(SampleProfTest, selected_suffix_elision_compact_binary) {
  367. // Profile is created and searched using the "selected"
  368. // suffix elision policy: we only strip a .XXX suffix if
  369. // it matches a pattern known to be generated by the compiler
  370. // (e.g. ".llvm.<digits>").
  371. StringMap<uint64_t> Expected;
  372. Expected["foo"] = uint64_t(20301);
  373. Expected["foo.bar"] = uint64_t(20303);
  374. Expected["foo.llvm.2465"] = uint64_t(-1);
  375. testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "selected",
  376. Expected);
  377. }
  378. TEST_F(SampleProfTest, none_suffix_elision_text) {
  379. // Profile is created and searched using the "none"
  380. // suffix elision policy: no stripping of suffixes at all.
  381. // Here we expect to see all variants in the profile.
  382. StringMap<uint64_t> Expected;
  383. Expected["foo"] = uint64_t(20301);
  384. Expected["foo.bar"] = uint64_t(20303);
  385. Expected["foo.llvm.2465"] = uint64_t(20305);
  386. testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "none", Expected);
  387. }
  388. TEST_F(SampleProfTest, none_suffix_elision_compact_binary) {
  389. // Profile is created and searched using the "none"
  390. // suffix elision policy: no stripping of suffixes at all.
  391. // Here we expect to see all variants in the profile.
  392. StringMap<uint64_t> Expected;
  393. Expected["foo"] = uint64_t(20301);
  394. Expected["foo.bar"] = uint64_t(20303);
  395. Expected["foo.llvm.2465"] = uint64_t(20305);
  396. testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "none",
  397. Expected);
  398. }
  399. } // end anonymous namespace