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 | |
9 | #include "llvm/ProfileData/SampleProf.h" |
10 | #include "llvm/ADT/StringMap.h" |
11 | #include "llvm/ADT/StringRef.h" |
12 | #include "llvm/IR/DebugInfoMetadata.h" |
13 | #include "llvm/IR/LLVMContext.h" |
14 | #include "llvm/IR/Metadata.h" |
15 | #include "llvm/IR/Module.h" |
16 | #include "llvm/ProfileData/SampleProfReader.h" |
17 | #include "llvm/ProfileData/SampleProfWriter.h" |
18 | #include "llvm/Support/Casting.h" |
19 | #include "llvm/Support/ErrorOr.h" |
20 | #include "llvm/Support/FileSystem.h" |
21 | #include "llvm/Support/MemoryBuffer.h" |
22 | #include "llvm/Support/VirtualFileSystem.h" |
23 | #include "llvm/Support/raw_ostream.h" |
24 | #include "llvm/Testing/Support/SupportHelpers.h" |
25 | #include "gtest/gtest.h" |
26 | #include <string> |
27 | #include <vector> |
28 | |
29 | using namespace llvm; |
30 | using namespace sampleprof; |
31 | |
32 | using llvm::unittest::TempFile; |
33 | |
34 | static ::testing::AssertionResult NoError(std::error_code EC) { |
35 | if (!EC) |
36 | return ::testing::AssertionSuccess(); |
37 | return ::testing::AssertionFailure() << "error " << EC.value() << ": " |
38 | << EC.message(); |
39 | } |
40 | |
41 | namespace { |
42 | |
43 | struct SampleProfTest : ::testing::Test { |
44 | LLVMContext Context; |
45 | std::unique_ptr<SampleProfileWriter> Writer; |
46 | std::unique_ptr<SampleProfileReader> Reader; |
47 | |
48 | SampleProfTest() : Writer(), Reader() {} |
49 | |
50 | void createWriter(SampleProfileFormat Format, StringRef Profile) { |
51 | std::error_code EC; |
52 | std::unique_ptr<raw_ostream> OS( |
53 | new raw_fd_ostream(Profile, EC, sys::fs::OF_None)); |
54 | auto WriterOrErr = SampleProfileWriter::create(OS, Format); |
55 | ASSERT_TRUE(NoError(WriterOrErr.getError())); |
56 | Writer = std::move(WriterOrErr.get()); |
57 | } |
58 | |
59 | void readProfile(const Module &M, StringRef Profile, |
60 | StringRef RemapFile = "" ) { |
61 | auto FS = vfs::getRealFileSystem(); |
62 | auto ReaderOrErr = SampleProfileReader::create( |
63 | Filename: std::string(Profile), C&: Context, FS&: *FS, P: FSDiscriminatorPass::Base, |
64 | RemapFilename: std::string(RemapFile)); |
65 | ASSERT_TRUE(NoError(ReaderOrErr.getError())); |
66 | Reader = std::move(ReaderOrErr.get()); |
67 | Reader->setModule(&M); |
68 | } |
69 | |
70 | TempFile createRemapFile() { |
71 | return TempFile("remapfile" , "" , R"( |
72 | # Types 'int' and 'long' are equivalent |
73 | type i l |
74 | # Function names 'foo' and 'faux' are equivalent |
75 | name 3foo 4faux |
76 | )" , |
77 | /*Unique*/ true); |
78 | } |
79 | |
80 | // Verify profile summary is consistent in the roundtrip to and from |
81 | // Metadata. \p AddPartialField is to choose whether the Metadata |
82 | // contains the IsPartialProfile field which is optional. |
83 | void verifyProfileSummary(ProfileSummary &Summary, Module &M, |
84 | const bool AddPartialField, |
85 | const bool AddPartialProfileRatioField) { |
86 | LLVMContext &Context = M.getContext(); |
87 | const bool IsPartialProfile = Summary.isPartialProfile(); |
88 | const double PartialProfileRatio = Summary.getPartialProfileRatio(); |
89 | auto VerifySummary = [IsPartialProfile, PartialProfileRatio]( |
90 | ProfileSummary &Summary) mutable { |
91 | ASSERT_EQ(ProfileSummary::PSK_Sample, Summary.getKind()); |
92 | ASSERT_EQ(138211u, Summary.getTotalCount()); |
93 | ASSERT_EQ(10u, Summary.getNumCounts()); |
94 | ASSERT_EQ(4u, Summary.getNumFunctions()); |
95 | ASSERT_EQ(1437u, Summary.getMaxFunctionCount()); |
96 | ASSERT_EQ(60351u, Summary.getMaxCount()); |
97 | ASSERT_EQ(IsPartialProfile, Summary.isPartialProfile()); |
98 | ASSERT_EQ(PartialProfileRatio, Summary.getPartialProfileRatio()); |
99 | |
100 | uint32_t Cutoff = 800000; |
101 | auto Predicate = [&Cutoff](const ProfileSummaryEntry &PE) { |
102 | return PE.Cutoff == Cutoff; |
103 | }; |
104 | const std::vector<ProfileSummaryEntry> &Details = |
105 | Summary.getDetailedSummary(); |
106 | auto EightyPerc = find_if(Range: Details, P: Predicate); |
107 | Cutoff = 900000; |
108 | auto NinetyPerc = find_if(Range: Details, P: Predicate); |
109 | Cutoff = 950000; |
110 | auto NinetyFivePerc = find_if(Range: Details, P: Predicate); |
111 | Cutoff = 990000; |
112 | auto NinetyNinePerc = find_if(Range: Details, P: Predicate); |
113 | ASSERT_EQ(60000u, EightyPerc->MinCount); |
114 | ASSERT_EQ(12557u, NinetyPerc->MinCount); |
115 | ASSERT_EQ(12557u, NinetyFivePerc->MinCount); |
116 | ASSERT_EQ(600u, NinetyNinePerc->MinCount); |
117 | }; |
118 | VerifySummary(Summary); |
119 | |
120 | // Test that conversion of summary to and from Metadata works. |
121 | Metadata *MD = |
122 | Summary.getMD(Context, AddPartialField, AddPartialProfileRatioField); |
123 | ASSERT_TRUE(MD); |
124 | ProfileSummary *PS = ProfileSummary::getFromMD(MD); |
125 | ASSERT_TRUE(PS); |
126 | VerifySummary(*PS); |
127 | delete PS; |
128 | |
129 | // Test that summary can be attached to and read back from module. |
130 | M.eraseNamedMetadata(NMD: M.getOrInsertModuleFlagsMetadata()); |
131 | M.setProfileSummary(M: MD, Kind: ProfileSummary::PSK_Sample); |
132 | MD = M.getProfileSummary(/* IsCS */ false); |
133 | ASSERT_TRUE(MD); |
134 | PS = ProfileSummary::getFromMD(MD); |
135 | ASSERT_TRUE(PS); |
136 | VerifySummary(*PS); |
137 | delete PS; |
138 | } |
139 | |
140 | void testRoundTrip(SampleProfileFormat Format, bool Remap, bool UseMD5) { |
141 | TempFile ProfileFile("profile" , "" , "" , /*Unique*/ true); |
142 | createWriter(Format, Profile: ProfileFile.path()); |
143 | if (Format == SampleProfileFormat::SPF_Ext_Binary && UseMD5) |
144 | static_cast<SampleProfileWriterExtBinary *>(Writer.get())->setUseMD5(); |
145 | |
146 | StringRef FooName("_Z3fooi" ); |
147 | FunctionSamples FooSamples; |
148 | FooSamples.setFunction(FunctionId(FooName)); |
149 | FooSamples.addTotalSamples(Num: 7711); |
150 | FooSamples.addHeadSamples(Num: 610); |
151 | FooSamples.addBodySamples(LineOffset: 1, Discriminator: 0, Num: 610); |
152 | FooSamples.addBodySamples(LineOffset: 2, Discriminator: 0, Num: 600); |
153 | FooSamples.addBodySamples(LineOffset: 4, Discriminator: 0, Num: 60000); |
154 | FooSamples.addBodySamples(LineOffset: 8, Discriminator: 0, Num: 60351); |
155 | FooSamples.addBodySamples(LineOffset: 10, Discriminator: 0, Num: 605); |
156 | |
157 | // Add inline instance with name "_Z3gooi". |
158 | FunctionId GooName(StringRef("_Z3gooi" )); |
159 | auto &GooSamples = |
160 | FooSamples.functionSamplesAt(Loc: LineLocation(7, 0))[GooName]; |
161 | GooSamples.setFunction(GooName); |
162 | GooSamples.addTotalSamples(Num: 502); |
163 | GooSamples.addBodySamples(LineOffset: 3, Discriminator: 0, Num: 502); |
164 | |
165 | // Add inline instance with name "_Z3hooi". |
166 | FunctionId HooName(StringRef("_Z3hooi" )); |
167 | auto &HooSamples = |
168 | GooSamples.functionSamplesAt(Loc: LineLocation(9, 0))[HooName]; |
169 | HooSamples.setFunction(HooName); |
170 | HooSamples.addTotalSamples(Num: 317); |
171 | HooSamples.addBodySamples(LineOffset: 4, Discriminator: 0, Num: 317); |
172 | |
173 | StringRef BarName("_Z3bari" ); |
174 | FunctionSamples BarSamples; |
175 | BarSamples.setFunction(FunctionId(BarName)); |
176 | BarSamples.addTotalSamples(Num: 20301); |
177 | BarSamples.addHeadSamples(Num: 1437); |
178 | BarSamples.addBodySamples(LineOffset: 1, Discriminator: 0, Num: 1437); |
179 | // Test how reader/writer handles unmangled names. |
180 | StringRef MconstructName("_M_construct<char *>" ); |
181 | StringRef StringviewName("string_view<std::allocator<char> >" ); |
182 | BarSamples.addCalledTargetSamples(LineOffset: 1, Discriminator: 0, Func: FunctionId(MconstructName), Num: 1000); |
183 | BarSamples.addCalledTargetSamples(LineOffset: 1, Discriminator: 0, Func: FunctionId(StringviewName), Num: 437); |
184 | |
185 | StringRef BazName("_Z3bazi" ); |
186 | FunctionSamples BazSamples; |
187 | BazSamples.setFunction(FunctionId(BazName)); |
188 | BazSamples.addTotalSamples(Num: 12557); |
189 | BazSamples.addHeadSamples(Num: 1257); |
190 | BazSamples.addBodySamples(LineOffset: 1, Discriminator: 0, Num: 12557); |
191 | |
192 | StringRef BooName("_Z3booi" ); |
193 | FunctionSamples BooSamples; |
194 | BooSamples.setFunction(FunctionId(BooName)); |
195 | BooSamples.addTotalSamples(Num: 1232); |
196 | BooSamples.addHeadSamples(Num: 1); |
197 | BooSamples.addBodySamples(LineOffset: 1, Discriminator: 0, Num: 1232); |
198 | |
199 | SampleProfileMap Profiles; |
200 | Profiles[FooName] = std::move(FooSamples); |
201 | Profiles[BarName] = std::move(BarSamples); |
202 | Profiles[BazName] = std::move(BazSamples); |
203 | Profiles[BooName] = std::move(BooSamples); |
204 | |
205 | Module M("my_module" , Context); |
206 | FunctionType *fn_type = |
207 | FunctionType::get(Result: Type::getVoidTy(C&: Context), Params: {}, isVarArg: false); |
208 | |
209 | TempFile RemapFile(createRemapFile()); |
210 | if (Remap) { |
211 | FooName = "_Z4fauxi" ; |
212 | BarName = "_Z3barl" ; |
213 | GooName = FunctionId(StringRef("_Z3gool" )); |
214 | HooName = FunctionId(StringRef("_Z3hool" )); |
215 | } |
216 | |
217 | M.getOrInsertFunction(Name: FooName, T: fn_type); |
218 | M.getOrInsertFunction(Name: BarName, T: fn_type); |
219 | M.getOrInsertFunction(Name: BooName, T: fn_type); |
220 | |
221 | ProfileSymbolList List; |
222 | if (Format == SampleProfileFormat::SPF_Ext_Binary) { |
223 | List.add(Name: "zoo" , copy: true); |
224 | List.add(Name: "moo" , copy: true); |
225 | } |
226 | Writer->setProfileSymbolList(&List); |
227 | |
228 | std::error_code EC; |
229 | EC = Writer->write(ProfileMap: Profiles); |
230 | ASSERT_TRUE(NoError(EC)); |
231 | |
232 | Writer->getOutputStream().flush(); |
233 | |
234 | readProfile(M, Profile: ProfileFile.path(), RemapFile: RemapFile.path()); |
235 | EC = Reader->read(); |
236 | ASSERT_TRUE(NoError(EC)); |
237 | |
238 | if (Format == SampleProfileFormat::SPF_Ext_Binary) { |
239 | std::unique_ptr<ProfileSymbolList> ReaderList = |
240 | Reader->getProfileSymbolList(); |
241 | ReaderList->contains(Name: "zoo" ); |
242 | ReaderList->contains(Name: "moo" ); |
243 | } |
244 | |
245 | FunctionSamples *ReadFooSamples = Reader->getSamplesFor(Fname: FooName); |
246 | ASSERT_TRUE(ReadFooSamples != nullptr); |
247 | if (!UseMD5) { |
248 | ASSERT_EQ("_Z3fooi" , ReadFooSamples->getFunction().str()); |
249 | } |
250 | ASSERT_EQ(7711u, ReadFooSamples->getTotalSamples()); |
251 | ASSERT_EQ(610u, ReadFooSamples->getHeadSamples()); |
252 | |
253 | // Try to find a FunctionSamples with GooName at given callsites containing |
254 | // inline instance for GooName. Test the correct FunctionSamples can be |
255 | // found with Remapper support. |
256 | const FunctionSamples *ReadGooSamples = |
257 | ReadFooSamples->findFunctionSamplesAt(Loc: LineLocation(7, 0), |
258 | CalleeName: GooName.stringRef(), |
259 | Remapper: Reader->getRemapper()); |
260 | ASSERT_TRUE(ReadGooSamples != nullptr); |
261 | ASSERT_EQ(502u, ReadGooSamples->getTotalSamples()); |
262 | |
263 | // Try to find a FunctionSamples with GooName at given callsites containing |
264 | // no inline instance for GooName. Test no FunctionSamples will be |
265 | // found with Remapper support. |
266 | const FunctionSamples *ReadGooSamplesAgain = |
267 | ReadFooSamples->findFunctionSamplesAt(Loc: LineLocation(9, 0), |
268 | CalleeName: GooName.stringRef(), |
269 | Remapper: Reader->getRemapper()); |
270 | ASSERT_TRUE(ReadGooSamplesAgain == nullptr); |
271 | |
272 | // The inline instance of Hoo is inside of the inline instance of Goo. |
273 | // Try to find a FunctionSamples with HooName at given callsites containing |
274 | // inline instance for HooName. Test the correct FunctionSamples can be |
275 | // found with Remapper support. |
276 | const FunctionSamples *ReadHooSamples = |
277 | ReadGooSamples->findFunctionSamplesAt(Loc: LineLocation(9, 0), |
278 | CalleeName: HooName.stringRef(), |
279 | Remapper: Reader->getRemapper()); |
280 | ASSERT_TRUE(ReadHooSamples != nullptr); |
281 | ASSERT_EQ(317u, ReadHooSamples->getTotalSamples()); |
282 | |
283 | FunctionSamples *ReadBarSamples = Reader->getSamplesFor(Fname: BarName); |
284 | ASSERT_TRUE(ReadBarSamples != nullptr); |
285 | if (!UseMD5) { |
286 | ASSERT_EQ("_Z3bari" , ReadBarSamples->getFunction().str()); |
287 | } |
288 | ASSERT_EQ(20301u, ReadBarSamples->getTotalSamples()); |
289 | ASSERT_EQ(1437u, ReadBarSamples->getHeadSamples()); |
290 | ErrorOr<SampleRecord::CallTargetMap> CTMap = |
291 | ReadBarSamples->findCallTargetMapAt(LineOffset: 1, Discriminator: 0); |
292 | ASSERT_FALSE(CTMap.getError()); |
293 | |
294 | // Because _Z3bazi is not defined in module M, expect _Z3bazi's profile |
295 | // is not loaded when the profile is ExtBinary format because this format |
296 | // supports loading function profiles on demand. |
297 | FunctionSamples *ReadBazSamples = Reader->getSamplesFor(Fname: BazName); |
298 | if (Format == SampleProfileFormat::SPF_Ext_Binary) { |
299 | ASSERT_TRUE(ReadBazSamples == nullptr); |
300 | ASSERT_EQ(3u, Reader->getProfiles().size()); |
301 | } else { |
302 | ASSERT_TRUE(ReadBazSamples != nullptr); |
303 | ASSERT_EQ(12557u, ReadBazSamples->getTotalSamples()); |
304 | ASSERT_EQ(4u, Reader->getProfiles().size()); |
305 | } |
306 | |
307 | FunctionSamples *ReadBooSamples = Reader->getSamplesFor(Fname: BooName); |
308 | ASSERT_TRUE(ReadBooSamples != nullptr); |
309 | ASSERT_EQ(1232u, ReadBooSamples->getTotalSamples()); |
310 | |
311 | FunctionId MconstructRep = getRepInFormat(Name: MconstructName); |
312 | FunctionId StringviewRep = getRepInFormat(Name: StringviewName); |
313 | ASSERT_EQ(1000u, CTMap.get()[MconstructRep]); |
314 | ASSERT_EQ(437u, CTMap.get()[StringviewRep]); |
315 | |
316 | |
317 | ProfileSummary &Summary = Reader->getSummary(); |
318 | Summary.setPartialProfile(true); |
319 | verifyProfileSummary(Summary, M, AddPartialField: true, AddPartialProfileRatioField: false); |
320 | |
321 | Summary.setPartialProfile(false); |
322 | verifyProfileSummary(Summary, M, AddPartialField: true, AddPartialProfileRatioField: false); |
323 | |
324 | verifyProfileSummary(Summary, M, AddPartialField: false, AddPartialProfileRatioField: false); |
325 | |
326 | Summary.setPartialProfile(true); |
327 | Summary.setPartialProfileRatio(0.5); |
328 | verifyProfileSummary(Summary, M, AddPartialField: true, AddPartialProfileRatioField: true); |
329 | } |
330 | |
331 | void addFunctionSamples(SampleProfileMap *Smap, const char *Fname, |
332 | uint64_t TotalSamples, uint64_t HeadSamples) { |
333 | StringRef Name(Fname); |
334 | FunctionSamples FcnSamples; |
335 | FcnSamples.setFunction(FunctionId(Name)); |
336 | FcnSamples.addTotalSamples(Num: TotalSamples); |
337 | FcnSamples.addHeadSamples(Num: HeadSamples); |
338 | FcnSamples.addBodySamples(LineOffset: 1, Discriminator: 0, Num: HeadSamples); |
339 | (*Smap)[Name] = FcnSamples; |
340 | } |
341 | |
342 | SampleProfileMap setupFcnSamplesForElisionTest(StringRef Policy) { |
343 | SampleProfileMap Smap; |
344 | addFunctionSamples(Smap: &Smap, Fname: "foo" , TotalSamples: uint64_t(20301), HeadSamples: uint64_t(1437)); |
345 | if (Policy == "" || Policy == "all" ) |
346 | return Smap; |
347 | addFunctionSamples(Smap: &Smap, Fname: "foo.bar" , TotalSamples: uint64_t(20303), HeadSamples: uint64_t(1439)); |
348 | if (Policy == "selected" ) |
349 | return Smap; |
350 | addFunctionSamples(Smap: &Smap, Fname: "foo.llvm.2465" , TotalSamples: uint64_t(20305), HeadSamples: uint64_t(1441)); |
351 | return Smap; |
352 | } |
353 | |
354 | void createFunctionWithSampleProfileElisionPolicy(Module *M, |
355 | const char *Fname, |
356 | StringRef Policy) { |
357 | FunctionType *FnType = |
358 | FunctionType::get(Result: Type::getVoidTy(C&: Context), Params: {}, isVarArg: false); |
359 | auto Inserted = M->getOrInsertFunction(Name: Fname, T: FnType); |
360 | auto Fcn = cast<Function>(Val: Inserted.getCallee()); |
361 | if (Policy != "" ) |
362 | Fcn->addFnAttr(Kind: "sample-profile-suffix-elision-policy" , Val: Policy); |
363 | } |
364 | |
365 | void setupModuleForElisionTest(Module *M, StringRef Policy) { |
366 | createFunctionWithSampleProfileElisionPolicy(M, Fname: "foo" , Policy); |
367 | createFunctionWithSampleProfileElisionPolicy(M, Fname: "foo.bar" , Policy); |
368 | createFunctionWithSampleProfileElisionPolicy(M, Fname: "foo.llvm.2465" , Policy); |
369 | } |
370 | |
371 | void testSuffixElisionPolicy(SampleProfileFormat Format, StringRef Policy, |
372 | const StringMap<uint64_t> &Expected) { |
373 | TempFile ProfileFile("profile" , "" , "" , /*Unique*/ true); |
374 | |
375 | Module M("my_module" , Context); |
376 | setupModuleForElisionTest(M: &M, Policy); |
377 | SampleProfileMap ProfMap = setupFcnSamplesForElisionTest(Policy); |
378 | |
379 | // write profile |
380 | createWriter(Format, Profile: ProfileFile.path()); |
381 | std::error_code EC; |
382 | EC = Writer->write(ProfileMap: ProfMap); |
383 | ASSERT_TRUE(NoError(EC)); |
384 | Writer->getOutputStream().flush(); |
385 | |
386 | // read profile |
387 | readProfile(M, Profile: ProfileFile.path()); |
388 | EC = Reader->read(); |
389 | ASSERT_TRUE(NoError(EC)); |
390 | |
391 | for (auto I = Expected.begin(); I != Expected.end(); ++I) { |
392 | uint64_t Esamples = uint64_t(-1); |
393 | FunctionSamples *Samples = Reader->getSamplesFor(Fname: I->getKey()); |
394 | if (Samples != nullptr) |
395 | Esamples = Samples->getTotalSamples(); |
396 | ASSERT_EQ(I->getValue(), Esamples); |
397 | } |
398 | } |
399 | }; |
400 | |
401 | TEST_F(SampleProfTest, roundtrip_text_profile) { |
402 | testRoundTrip(Format: SampleProfileFormat::SPF_Text, Remap: false, UseMD5: false); |
403 | } |
404 | |
405 | TEST_F(SampleProfTest, roundtrip_raw_binary_profile) { |
406 | testRoundTrip(Format: SampleProfileFormat::SPF_Binary, Remap: false, UseMD5: false); |
407 | } |
408 | |
409 | TEST_F(SampleProfTest, roundtrip_ext_binary_profile) { |
410 | testRoundTrip(Format: SampleProfileFormat::SPF_Ext_Binary, Remap: false, UseMD5: false); |
411 | } |
412 | |
413 | TEST_F(SampleProfTest, roundtrip_md5_ext_binary_profile) { |
414 | testRoundTrip(Format: SampleProfileFormat::SPF_Ext_Binary, Remap: false, UseMD5: true); |
415 | } |
416 | |
417 | TEST_F(SampleProfTest, remap_text_profile) { |
418 | testRoundTrip(Format: SampleProfileFormat::SPF_Text, Remap: true, UseMD5: false); |
419 | } |
420 | |
421 | TEST_F(SampleProfTest, remap_raw_binary_profile) { |
422 | testRoundTrip(Format: SampleProfileFormat::SPF_Binary, Remap: true, UseMD5: false); |
423 | } |
424 | |
425 | TEST_F(SampleProfTest, remap_ext_binary_profile) { |
426 | testRoundTrip(Format: SampleProfileFormat::SPF_Ext_Binary, Remap: true, UseMD5: false); |
427 | } |
428 | |
429 | TEST_F(SampleProfTest, sample_overflow_saturation) { |
430 | const uint64_t Max = std::numeric_limits<uint64_t>::max(); |
431 | sampleprof_error Result; |
432 | |
433 | FunctionSamples FooSamples; |
434 | Result = FooSamples.addTotalSamples(Num: 1); |
435 | ASSERT_EQ(Result, sampleprof_error::success); |
436 | |
437 | Result = FooSamples.addHeadSamples(Num: 1); |
438 | ASSERT_EQ(Result, sampleprof_error::success); |
439 | |
440 | Result = FooSamples.addBodySamples(LineOffset: 10, Discriminator: 0, Num: 1); |
441 | ASSERT_EQ(Result, sampleprof_error::success); |
442 | |
443 | Result = FooSamples.addTotalSamples(Num: Max); |
444 | ASSERT_EQ(Result, sampleprof_error::counter_overflow); |
445 | ASSERT_EQ(FooSamples.getTotalSamples(), Max); |
446 | |
447 | Result = FooSamples.addHeadSamples(Num: Max); |
448 | ASSERT_EQ(Result, sampleprof_error::counter_overflow); |
449 | ASSERT_EQ(FooSamples.getHeadSamples(), Max); |
450 | |
451 | Result = FooSamples.addBodySamples(LineOffset: 10, Discriminator: 0, Num: Max); |
452 | ASSERT_EQ(Result, sampleprof_error::counter_overflow); |
453 | ErrorOr<uint64_t> BodySamples = FooSamples.findSamplesAt(LineOffset: 10, Discriminator: 0); |
454 | ASSERT_FALSE(BodySamples.getError()); |
455 | ASSERT_EQ(BodySamples.get(), Max); |
456 | } |
457 | |
458 | TEST_F(SampleProfTest, default_suffix_elision_text) { |
459 | // Default suffix elision policy: strip everything after first dot. |
460 | // This implies that all suffix variants will map to "foo", so |
461 | // we don't expect to see any entries for them in the sample |
462 | // profile. |
463 | StringMap<uint64_t> Expected; |
464 | Expected["foo" ] = uint64_t(20301); |
465 | Expected["foo.bar" ] = uint64_t(-1); |
466 | Expected["foo.llvm.2465" ] = uint64_t(-1); |
467 | testSuffixElisionPolicy(Format: SampleProfileFormat::SPF_Text, Policy: "" , Expected); |
468 | } |
469 | |
470 | TEST_F(SampleProfTest, selected_suffix_elision_text) { |
471 | // Profile is created and searched using the "selected" |
472 | // suffix elision policy: we only strip a .XXX suffix if |
473 | // it matches a pattern known to be generated by the compiler |
474 | // (e.g. ".llvm.<digits>"). |
475 | StringMap<uint64_t> Expected; |
476 | Expected["foo" ] = uint64_t(20301); |
477 | Expected["foo.bar" ] = uint64_t(20303); |
478 | Expected["foo.llvm.2465" ] = uint64_t(-1); |
479 | testSuffixElisionPolicy(Format: SampleProfileFormat::SPF_Text, Policy: "selected" , Expected); |
480 | } |
481 | |
482 | TEST_F(SampleProfTest, none_suffix_elision_text) { |
483 | // Profile is created and searched using the "none" |
484 | // suffix elision policy: no stripping of suffixes at all. |
485 | // Here we expect to see all variants in the profile. |
486 | StringMap<uint64_t> Expected; |
487 | Expected["foo" ] = uint64_t(20301); |
488 | Expected["foo.bar" ] = uint64_t(20303); |
489 | Expected["foo.llvm.2465" ] = uint64_t(20305); |
490 | testSuffixElisionPolicy(Format: SampleProfileFormat::SPF_Text, Policy: "none" , Expected); |
491 | } |
492 | |
493 | } // end anonymous namespace |
494 | |