xref: /llvm-project/llvm/unittests/tools/llvm-profdata/OutputSizeLimitTest.cpp (revision 406e2fbf6ac1e408deb31f480d940aaf15b8bce5)
1 //===- llvm/unittests/tools/llvm-profdata/OutputSizeLimitTest.cpp ---------===//
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/SampleProfReader.h"
10 #include "llvm/ProfileData/SampleProfWriter.h"
11 #include "llvm/Support/FileSystem.h"
12 #include "llvm/Support/VirtualFileSystem.h"
13 #include "llvm/Testing/Support/Error.h"
14 #include "gtest/gtest.h"
15 
16 using namespace llvm;
17 using llvm::unittest::TempFile;
18 
19 std::string Input1 = R"(main:184019:0
20  4: 534
21  4.2: 534
22  5: 1075
23  5.1: 1075
24  6: 2080
25  7: 534
26  9: 2064 _Z3bari:1471 _Z3fooi:631
27  10: inline1:1000
28   1: 1000
29  10: inline2:2000
30   1: 2000
31 _Z3bari:20301:1437
32  1: 1437
33 _Z3fooi:7711:610
34  1: 610)";
35 
36 const char EmptyProfile[18] = "\xff\xe5\xd0\xb1\xf4\xc9\x94\xa8\x53\x67";
37 
38 /// sys::fs and SampleProf mix Error and error_code, making an adapter class
39 /// to keep code elegant.
40 template <typename T> class ExpectedErrorOr : public Expected<T> {
41 public:
42   ExpectedErrorOr(T &&Obj) : Expected<T>(Obj) {}
43 
44   ExpectedErrorOr(std::error_code EC) : Expected<T>(errorCodeToError(EC)) {}
45 
46   ExpectedErrorOr(Error &&E) : Expected<T>(std::move(E)) {}
47 
48   template <typename U>
49   ExpectedErrorOr(ErrorOr<U> &&E)
50       : Expected<T>(errorCodeToError(E.getError())) {}
51 
52   template <typename U>
53   ExpectedErrorOr(Expected<U> &&E) : Expected<T>(E.takeError()) {}
54 };
55 
56 #define DEF_VAR_RETURN_IF_ERROR(Var, Value)                                    \
57   auto Var##OrErr = Value;                                                     \
58   if (!Var##OrErr)                                                             \
59     return Var##OrErr;                                                         \
60   auto Var = std::move(Var##OrErr.get())
61 
62 #define VAR_RETURN_IF_ERROR(Var, Value)                                        \
63   Var##OrErr = Value;                                                          \
64   if (!Var##OrErr)                                                             \
65     return Var##OrErr;                                                         \
66   Var = std::move(Var##OrErr.get())
67 
68 #define RETURN_IF_ERROR(Value)                                                 \
69   if (auto E = Value)                                                          \
70   return std::move(E)
71 
72 /// The main testing routine. After rewriting profiles with size limit, check
73 /// the following:
74 /// 1. The file size of the new profile is within the size limit.
75 /// 2. The new profile is a subset of the old profile, and the content of every
76 /// sample in the new profile is unchanged.
77 /// Note that even though by default samples with fewest total count are dropped
78 /// first, this is not a requirement. Samples can be dropped by any order.
79 static ExpectedErrorOr<void *> RunTest(StringRef Input, size_t SizeLimit,
80                                        SampleProfileFormat Format,
81                                        bool Compress = false) {
82   // Read Input profile.
83   auto FS = vfs::getRealFileSystem();
84   LLVMContext Context;
85   auto InputBuffer = MemoryBuffer::getMemBuffer(Input);
86   DEF_VAR_RETURN_IF_ERROR(
87       Reader, SampleProfileReader::create(InputBuffer, Context, *FS));
88   RETURN_IF_ERROR(Reader->read());
89   SampleProfileMap OldProfiles = Reader->getProfiles();
90 
91   // Rewrite it to a temp file with size limit.
92   TempFile Temp("profile", "afdo", "", true);
93   bool isEmpty = false;
94   {
95     DEF_VAR_RETURN_IF_ERROR(Writer,
96                             SampleProfileWriter::create(Temp.path(), Format));
97     if (Compress)
98       Writer->setToCompressAllSections();
99     std::error_code EC = Writer->writeWithSizeLimit(OldProfiles, SizeLimit);
100     // too_large means no sample could be written because SizeLimit is too
101     // small. Otherwise any other error code indicates unexpected failure.
102     if (EC == sampleprof_error::too_large)
103       isEmpty = true;
104     else if (EC)
105       return EC;
106   }
107 
108   // Read the temp file to get new profiles. Use the default empty profile if
109   // temp file was not written because size limit is too small.
110   SampleProfileMap NewProfiles;
111   InputBuffer = MemoryBuffer::getMemBuffer(StringRef(EmptyProfile, 17));
112   DEF_VAR_RETURN_IF_ERROR(
113       NewReader, SampleProfileReader::create(InputBuffer, Context, *FS));
114   if (!isEmpty) {
115     VAR_RETURN_IF_ERROR(NewReader, SampleProfileReader::create(
116                                        Temp.path().str(), Context, *FS));
117     RETURN_IF_ERROR(NewReader->read());
118     NewProfiles = NewReader->getProfiles();
119   }
120 
121   // Check temp file is actually within size limit.
122   uint64_t FileSize;
123   RETURN_IF_ERROR(sys::fs::file_size(Temp.path(), FileSize));
124   EXPECT_LE(FileSize, SizeLimit);
125 
126   // For compact binary format, function names are stored as MD5, so we cannot
127   // directly match the samples of the new profile with the old profile. A
128   // simple way is to convert the old profile to compact binary format and read
129   // it back
130   if (Format == llvm::sampleprof::SPF_Compact_Binary) {
131     TempFile CompBinary("compbinary", "afdo", "", true);
132     {
133       DEF_VAR_RETURN_IF_ERROR(
134           Writer, SampleProfileWriter::create(
135                       CompBinary.path(), llvm::sampleprof::SPF_Compact_Binary));
136       RETURN_IF_ERROR(Writer->write(OldProfiles));
137     }
138     VAR_RETURN_IF_ERROR(Reader, SampleProfileReader::create(
139                                     CompBinary.path().str(), Context, *FS));
140     RETURN_IF_ERROR(Reader->read());
141     OldProfiles = Reader->getProfiles();
142   }
143 
144   // For every sample in the new profile, confirm it is in the old profile and
145   // unchanged.
146   for (auto Sample : NewProfiles) {
147     auto FindResult = OldProfiles.find(Sample.first);
148     EXPECT_NE(FindResult, OldProfiles.end());
149     if (FindResult != OldProfiles.end()) {
150       EXPECT_EQ(Sample.second.getHeadSamples(),
151                 FindResult->second.getHeadSamples());
152       EXPECT_EQ(Sample.second, FindResult->second);
153     }
154   }
155   return nullptr;
156 }
157 
158 TEST(TestOutputSizeLimit, TestOutputSizeLimitExtBinary) {
159   for (size_t OutputSizeLimit : {490, 489, 488, 475, 474, 459, 400})
160     ASSERT_THAT_EXPECTED(
161         RunTest(Input1, OutputSizeLimit, llvm::sampleprof::SPF_Ext_Binary),
162         Succeeded());
163 }
164 
165 TEST(TestOutputSizeLimit, TestOutputSizeLimitBinary) {
166   for (size_t OutputSizeLimit : {250, 249, 248, 237, 236, 223, 200})
167     ASSERT_THAT_EXPECTED(
168         RunTest(Input1, OutputSizeLimit, llvm::sampleprof::SPF_Binary),
169         Succeeded());
170 }
171 
172 TEST(TestOutputSizeLimit, TestOutputSizeLimitCompBinary) {
173   for (size_t OutputSizeLimit : {277, 276, 275, 264, 263, 250, 200})
174     ASSERT_THAT_EXPECTED(
175         RunTest(Input1, OutputSizeLimit, llvm::sampleprof::SPF_Compact_Binary),
176         Succeeded());
177 }
178 
179 TEST(TestOutputSizeLimit, TestOutputSizeLimitText) {
180   for (size_t OutputSizeLimit :
181        {229, 228, 227, 213, 212, 211, 189, 188, 187, 186, 150})
182     ASSERT_THAT_EXPECTED(
183         RunTest(Input1, OutputSizeLimit, llvm::sampleprof::SPF_Text),
184         Succeeded());
185 }
186 
187 #if LLVM_ENABLE_ZLIB
188 TEST(TestOutputSizeLimit, TestOutputSizeLimitExtBinaryCompressed) {
189   for (size_t OutputSizeLimit :
190        {507, 506, 505, 494, 493, 492, 483, 482, 481, 480})
191     ASSERT_THAT_EXPECTED(RunTest(Input1, OutputSizeLimit,
192                                  llvm::sampleprof::SPF_Ext_Binary, true),
193                          Succeeded());
194 }
195 #endif
196