[yaml2obj/obj2yaml] - Add support for .stack_sizes sections.
[llvm-complete.git] / unittests / ProfileData / SampleProfTest.cpp
blobdd5ded5bc9b2fac1befb430a62a0560c4c1f0924
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 //===----------------------------------------------------------------------===//
9 #include "llvm/ProfileData/SampleProf.h"
10 #include "llvm/ADT/StringMap.h"
11 #include "llvm/ADT/StringRef.h"
12 #include "llvm/IR/LLVMContext.h"
13 #include "llvm/IR/Metadata.h"
14 #include "llvm/IR/Module.h"
15 #include "llvm/ProfileData/SampleProfReader.h"
16 #include "llvm/ProfileData/SampleProfWriter.h"
17 #include "llvm/Support/Casting.h"
18 #include "llvm/Support/ErrorOr.h"
19 #include "llvm/Support/MemoryBuffer.h"
20 #include "llvm/Support/raw_ostream.h"
21 #include "gtest/gtest.h"
22 #include <string>
23 #include <vector>
25 using namespace llvm;
26 using namespace sampleprof;
28 static ::testing::AssertionResult NoError(std::error_code EC) {
29 if (!EC)
30 return ::testing::AssertionSuccess();
31 return ::testing::AssertionFailure() << "error " << EC.value() << ": "
32 << EC.message();
35 namespace {
37 struct SampleProfTest : ::testing::Test {
38 LLVMContext Context;
39 std::unique_ptr<SampleProfileWriter> Writer;
40 std::unique_ptr<SampleProfileReader> Reader;
42 SampleProfTest() : Writer(), Reader() {}
44 void createWriter(SampleProfileFormat Format, StringRef Profile) {
45 std::error_code EC;
46 std::unique_ptr<raw_ostream> OS(
47 new raw_fd_ostream(Profile, EC, sys::fs::OF_None));
48 auto WriterOrErr = SampleProfileWriter::create(OS, Format);
49 ASSERT_TRUE(NoError(WriterOrErr.getError()));
50 Writer = std::move(WriterOrErr.get());
53 void readProfile(const Module &M, StringRef Profile) {
54 auto ReaderOrErr = SampleProfileReader::create(Profile, Context);
55 ASSERT_TRUE(NoError(ReaderOrErr.getError()));
56 Reader = std::move(ReaderOrErr.get());
57 Reader->collectFuncsToUse(M);
60 void testRoundTrip(SampleProfileFormat Format, bool Remap) {
61 SmallVector<char, 128> ProfilePath;
62 ASSERT_TRUE(NoError(llvm::sys::fs::createTemporaryFile("profile", "", ProfilePath)));
63 StringRef Profile(ProfilePath.data(), ProfilePath.size());
64 createWriter(Format, Profile);
66 StringRef FooName("_Z3fooi");
67 FunctionSamples FooSamples;
68 FooSamples.setName(FooName);
69 FooSamples.addTotalSamples(7711);
70 FooSamples.addHeadSamples(610);
71 FooSamples.addBodySamples(1, 0, 610);
72 FooSamples.addBodySamples(2, 0, 600);
73 FooSamples.addBodySamples(4, 0, 60000);
74 FooSamples.addBodySamples(8, 0, 60351);
75 FooSamples.addBodySamples(10, 0, 605);
77 StringRef BarName("_Z3bari");
78 FunctionSamples BarSamples;
79 BarSamples.setName(BarName);
80 BarSamples.addTotalSamples(20301);
81 BarSamples.addHeadSamples(1437);
82 BarSamples.addBodySamples(1, 0, 1437);
83 // Test how reader/writer handles unmangled names.
84 StringRef MconstructName("_M_construct<char *>");
85 StringRef StringviewName("string_view<std::allocator<char> >");
86 BarSamples.addCalledTargetSamples(1, 0, MconstructName, 1000);
87 BarSamples.addCalledTargetSamples(1, 0, StringviewName, 437);
89 Module M("my_module", Context);
90 FunctionType *fn_type =
91 FunctionType::get(Type::getVoidTy(Context), {}, false);
92 M.getOrInsertFunction(FooName, fn_type);
93 M.getOrInsertFunction(BarName, fn_type);
95 StringMap<FunctionSamples> Profiles;
96 Profiles[FooName] = std::move(FooSamples);
97 Profiles[BarName] = std::move(BarSamples);
99 ProfileSymbolList List;
100 if (Format == SampleProfileFormat::SPF_Ext_Binary) {
101 List.add("zoo", true);
102 List.add("moo", true);
104 Writer->setProfileSymbolList(&List);
106 std::error_code EC;
107 EC = Writer->write(Profiles);
108 ASSERT_TRUE(NoError(EC));
110 Writer->getOutputStream().flush();
112 readProfile(M, Profile);
114 EC = Reader->read();
115 ASSERT_TRUE(NoError(EC));
117 if (Format == SampleProfileFormat::SPF_Ext_Binary) {
118 std::unique_ptr<ProfileSymbolList> ReaderList =
119 Reader->getProfileSymbolList();
120 ReaderList->contains("zoo");
121 ReaderList->contains("moo");
124 if (Remap) {
125 auto MemBuffer = llvm::MemoryBuffer::getMemBuffer(R"(
126 # Types 'int' and 'long' are equivalent
127 type i l
128 # Function names 'foo' and 'faux' are equivalent
129 name 3foo 4faux
130 )");
131 Reader.reset(new SampleProfileReaderItaniumRemapper(
132 std::move(MemBuffer), Context, std::move(Reader)));
133 FooName = "_Z4fauxi";
134 BarName = "_Z3barl";
136 EC = Reader->read();
137 ASSERT_TRUE(NoError(EC));
140 ASSERT_EQ(2u, Reader->getProfiles().size());
142 FunctionSamples *ReadFooSamples = Reader->getSamplesFor(FooName);
143 ASSERT_TRUE(ReadFooSamples != nullptr);
144 if (Format != SampleProfileFormat::SPF_Compact_Binary) {
145 ASSERT_EQ("_Z3fooi", ReadFooSamples->getName());
147 ASSERT_EQ(7711u, ReadFooSamples->getTotalSamples());
148 ASSERT_EQ(610u, ReadFooSamples->getHeadSamples());
150 FunctionSamples *ReadBarSamples = Reader->getSamplesFor(BarName);
151 ASSERT_TRUE(ReadBarSamples != nullptr);
152 if (Format != SampleProfileFormat::SPF_Compact_Binary) {
153 ASSERT_EQ("_Z3bari", ReadBarSamples->getName());
155 ASSERT_EQ(20301u, ReadBarSamples->getTotalSamples());
156 ASSERT_EQ(1437u, ReadBarSamples->getHeadSamples());
157 ErrorOr<SampleRecord::CallTargetMap> CTMap =
158 ReadBarSamples->findCallTargetMapAt(1, 0);
159 ASSERT_FALSE(CTMap.getError());
161 std::string MconstructGUID;
162 StringRef MconstructRep =
163 getRepInFormat(MconstructName, Format, MconstructGUID);
164 std::string StringviewGUID;
165 StringRef StringviewRep =
166 getRepInFormat(StringviewName, Format, StringviewGUID);
167 ASSERT_EQ(1000u, CTMap.get()[MconstructRep]);
168 ASSERT_EQ(437u, CTMap.get()[StringviewRep]);
170 auto VerifySummary = [](ProfileSummary &Summary) mutable {
171 ASSERT_EQ(ProfileSummary::PSK_Sample, Summary.getKind());
172 ASSERT_EQ(123603u, Summary.getTotalCount());
173 ASSERT_EQ(6u, Summary.getNumCounts());
174 ASSERT_EQ(2u, Summary.getNumFunctions());
175 ASSERT_EQ(1437u, Summary.getMaxFunctionCount());
176 ASSERT_EQ(60351u, Summary.getMaxCount());
178 uint32_t Cutoff = 800000;
179 auto Predicate = [&Cutoff](const ProfileSummaryEntry &PE) {
180 return PE.Cutoff == Cutoff;
182 std::vector<ProfileSummaryEntry> &Details = Summary.getDetailedSummary();
183 auto EightyPerc = find_if(Details, Predicate);
184 Cutoff = 900000;
185 auto NinetyPerc = find_if(Details, Predicate);
186 Cutoff = 950000;
187 auto NinetyFivePerc = find_if(Details, Predicate);
188 Cutoff = 990000;
189 auto NinetyNinePerc = find_if(Details, Predicate);
190 ASSERT_EQ(60000u, EightyPerc->MinCount);
191 ASSERT_EQ(60000u, NinetyPerc->MinCount);
192 ASSERT_EQ(60000u, NinetyFivePerc->MinCount);
193 ASSERT_EQ(610u, NinetyNinePerc->MinCount);
196 ProfileSummary &Summary = Reader->getSummary();
197 VerifySummary(Summary);
199 // Test that conversion of summary to and from Metadata works.
200 Metadata *MD = Summary.getMD(Context);
201 ASSERT_TRUE(MD);
202 ProfileSummary *PS = ProfileSummary::getFromMD(MD);
203 ASSERT_TRUE(PS);
204 VerifySummary(*PS);
205 delete PS;
207 // Test that summary can be attached to and read back from module.
208 M.setProfileSummary(MD, ProfileSummary::PSK_Sample);
209 MD = M.getProfileSummary(/* IsCS */ false);
210 ASSERT_TRUE(MD);
211 PS = ProfileSummary::getFromMD(MD);
212 ASSERT_TRUE(PS);
213 VerifySummary(*PS);
214 delete PS;
217 void addFunctionSamples(StringMap<FunctionSamples> *Smap, const char *Fname,
218 uint64_t TotalSamples, uint64_t HeadSamples) {
219 StringRef Name(Fname);
220 FunctionSamples FcnSamples;
221 FcnSamples.setName(Name);
222 FcnSamples.addTotalSamples(TotalSamples);
223 FcnSamples.addHeadSamples(HeadSamples);
224 FcnSamples.addBodySamples(1, 0, HeadSamples);
225 (*Smap)[Name] = FcnSamples;
228 StringMap<FunctionSamples> setupFcnSamplesForElisionTest(StringRef Policy) {
229 StringMap<FunctionSamples> Smap;
230 addFunctionSamples(&Smap, "foo", uint64_t(20301), uint64_t(1437));
231 if (Policy == "" || Policy == "all")
232 return Smap;
233 addFunctionSamples(&Smap, "foo.bar", uint64_t(20303), uint64_t(1439));
234 if (Policy == "selected")
235 return Smap;
236 addFunctionSamples(&Smap, "foo.llvm.2465", uint64_t(20305), uint64_t(1441));
237 return Smap;
240 void createFunctionWithSampleProfileElisionPolicy(Module *M,
241 const char *Fname,
242 StringRef Policy) {
243 FunctionType *FnType =
244 FunctionType::get(Type::getVoidTy(Context), {}, false);
245 auto Inserted = M->getOrInsertFunction(Fname, FnType);
246 auto Fcn = cast<Function>(Inserted.getCallee());
247 if (Policy != "")
248 Fcn->addFnAttr("sample-profile-suffix-elision-policy", Policy);
251 void setupModuleForElisionTest(Module *M, StringRef Policy) {
252 createFunctionWithSampleProfileElisionPolicy(M, "foo", Policy);
253 createFunctionWithSampleProfileElisionPolicy(M, "foo.bar", Policy);
254 createFunctionWithSampleProfileElisionPolicy(M, "foo.llvm.2465", Policy);
257 void testSuffixElisionPolicy(SampleProfileFormat Format, StringRef Policy,
258 const StringMap<uint64_t> &Expected) {
259 SmallVector<char, 128> ProfilePath;
260 std::error_code EC;
261 EC = llvm::sys::fs::createTemporaryFile("profile", "", ProfilePath);
262 ASSERT_TRUE(NoError(EC));
263 StringRef ProfileFile(ProfilePath.data(), ProfilePath.size());
265 Module M("my_module", Context);
266 setupModuleForElisionTest(&M, Policy);
267 StringMap<FunctionSamples> ProfMap = setupFcnSamplesForElisionTest(Policy);
269 // write profile
270 createWriter(Format, ProfileFile);
271 EC = Writer->write(ProfMap);
272 ASSERT_TRUE(NoError(EC));
273 Writer->getOutputStream().flush();
275 // read profile
276 readProfile(M, ProfileFile);
277 EC = Reader->read();
278 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);
290 TEST_F(SampleProfTest, roundtrip_text_profile) {
291 testRoundTrip(SampleProfileFormat::SPF_Text, false);
294 TEST_F(SampleProfTest, roundtrip_raw_binary_profile) {
295 testRoundTrip(SampleProfileFormat::SPF_Binary, false);
298 TEST_F(SampleProfTest, roundtrip_compact_binary_profile) {
299 testRoundTrip(SampleProfileFormat::SPF_Compact_Binary, false);
302 TEST_F(SampleProfTest, roundtrip_ext_binary_profile) {
303 testRoundTrip(SampleProfileFormat::SPF_Ext_Binary, false);
306 TEST_F(SampleProfTest, remap_text_profile) {
307 testRoundTrip(SampleProfileFormat::SPF_Text, true);
310 TEST_F(SampleProfTest, remap_raw_binary_profile) {
311 testRoundTrip(SampleProfileFormat::SPF_Binary, true);
314 TEST_F(SampleProfTest, remap_ext_binary_profile) {
315 testRoundTrip(SampleProfileFormat::SPF_Ext_Binary, true);
318 TEST_F(SampleProfTest, sample_overflow_saturation) {
319 const uint64_t Max = std::numeric_limits<uint64_t>::max();
320 sampleprof_error Result;
322 FunctionSamples FooSamples;
323 Result = FooSamples.addTotalSamples(1);
324 ASSERT_EQ(Result, sampleprof_error::success);
326 Result = FooSamples.addHeadSamples(1);
327 ASSERT_EQ(Result, sampleprof_error::success);
329 Result = FooSamples.addBodySamples(10, 0, 1);
330 ASSERT_EQ(Result, sampleprof_error::success);
332 Result = FooSamples.addTotalSamples(Max);
333 ASSERT_EQ(Result, sampleprof_error::counter_overflow);
334 ASSERT_EQ(FooSamples.getTotalSamples(), Max);
336 Result = FooSamples.addHeadSamples(Max);
337 ASSERT_EQ(Result, sampleprof_error::counter_overflow);
338 ASSERT_EQ(FooSamples.getHeadSamples(), Max);
340 Result = FooSamples.addBodySamples(10, 0, Max);
341 ASSERT_EQ(Result, sampleprof_error::counter_overflow);
342 ErrorOr<uint64_t> BodySamples = FooSamples.findSamplesAt(10, 0);
343 ASSERT_FALSE(BodySamples.getError());
344 ASSERT_EQ(BodySamples.get(), Max);
347 TEST_F(SampleProfTest, default_suffix_elision_text) {
348 // Default suffix elision policy: strip everything after first dot.
349 // This implies that all suffix variants will map to "foo", so
350 // we don't expect to see any entries for them in the sample
351 // profile.
352 StringMap<uint64_t> Expected;
353 Expected["foo"] = uint64_t(20301);
354 Expected["foo.bar"] = uint64_t(-1);
355 Expected["foo.llvm.2465"] = uint64_t(-1);
356 testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "", Expected);
359 TEST_F(SampleProfTest, default_suffix_elision_compact_binary) {
360 // Default suffix elision policy: strip everything after first dot.
361 // This implies that all suffix variants will map to "foo", so
362 // we don't expect to see any entries for them in the sample
363 // profile.
364 StringMap<uint64_t> Expected;
365 Expected["foo"] = uint64_t(20301);
366 Expected["foo.bar"] = uint64_t(-1);
367 Expected["foo.llvm.2465"] = uint64_t(-1);
368 testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "",
369 Expected);
372 TEST_F(SampleProfTest, selected_suffix_elision_text) {
373 // Profile is created and searched using the "selected"
374 // suffix elision policy: we only strip a .XXX suffix if
375 // it matches a pattern known to be generated by the compiler
376 // (e.g. ".llvm.<digits>").
377 StringMap<uint64_t> Expected;
378 Expected["foo"] = uint64_t(20301);
379 Expected["foo.bar"] = uint64_t(20303);
380 Expected["foo.llvm.2465"] = uint64_t(-1);
381 testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "selected", Expected);
384 TEST_F(SampleProfTest, selected_suffix_elision_compact_binary) {
385 // Profile is created and searched using the "selected"
386 // suffix elision policy: we only strip a .XXX suffix if
387 // it matches a pattern known to be generated by the compiler
388 // (e.g. ".llvm.<digits>").
389 StringMap<uint64_t> Expected;
390 Expected["foo"] = uint64_t(20301);
391 Expected["foo.bar"] = uint64_t(20303);
392 Expected["foo.llvm.2465"] = uint64_t(-1);
393 testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "selected",
394 Expected);
397 TEST_F(SampleProfTest, none_suffix_elision_text) {
398 // Profile is created and searched using the "none"
399 // suffix elision policy: no stripping of suffixes at all.
400 // Here we expect to see all variants in the profile.
401 StringMap<uint64_t> Expected;
402 Expected["foo"] = uint64_t(20301);
403 Expected["foo.bar"] = uint64_t(20303);
404 Expected["foo.llvm.2465"] = uint64_t(20305);
405 testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "none", Expected);
408 TEST_F(SampleProfTest, none_suffix_elision_compact_binary) {
409 // Profile is created and searched using the "none"
410 // suffix elision policy: no stripping of suffixes at all.
411 // Here we expect to see all variants in the profile.
412 StringMap<uint64_t> Expected;
413 Expected["foo"] = uint64_t(20301);
414 Expected["foo.bar"] = uint64_t(20303);
415 Expected["foo.llvm.2465"] = uint64_t(20305);
416 testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "none",
417 Expected);
420 } // end anonymous namespace