xref: /llvm-project/llvm/unittests/tools/llvm-profdata/OutputSizeLimitTest.cpp (revision 7624de5beae2f142abfdb3e32a63c263a586d768)
179971d0dSWilliam Huang //===- llvm/unittests/tools/llvm-profdata/OutputSizeLimitTest.cpp ---------===//
279971d0dSWilliam Huang //
379971d0dSWilliam Huang // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
479971d0dSWilliam Huang // See https://llvm.org/LICENSE.txt for license information.
579971d0dSWilliam Huang // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
679971d0dSWilliam Huang //
779971d0dSWilliam Huang //===----------------------------------------------------------------------===//
879971d0dSWilliam Huang 
979971d0dSWilliam Huang #include "llvm/ProfileData/SampleProfReader.h"
1079971d0dSWilliam Huang #include "llvm/ProfileData/SampleProfWriter.h"
1179971d0dSWilliam Huang #include "llvm/Support/FileSystem.h"
1279971d0dSWilliam Huang #include "llvm/Support/VirtualFileSystem.h"
1379971d0dSWilliam Huang #include "llvm/Testing/Support/Error.h"
1479971d0dSWilliam Huang #include "gtest/gtest.h"
1579971d0dSWilliam Huang 
1679971d0dSWilliam Huang using namespace llvm;
1779971d0dSWilliam Huang using llvm::unittest::TempFile;
1879971d0dSWilliam Huang 
1979971d0dSWilliam Huang std::string Input1 = R"(main:184019:0
2079971d0dSWilliam Huang  4: 534
2179971d0dSWilliam Huang  4.2: 534
2279971d0dSWilliam Huang  5: 1075
2379971d0dSWilliam Huang  5.1: 1075
2479971d0dSWilliam Huang  6: 2080
2579971d0dSWilliam Huang  7: 534
2679971d0dSWilliam Huang  9: 2064 _Z3bari:1471 _Z3fooi:631
2779971d0dSWilliam Huang  10: inline1:1000
2879971d0dSWilliam Huang   1: 1000
2979971d0dSWilliam Huang  10: inline2:2000
3079971d0dSWilliam Huang   1: 2000
3179971d0dSWilliam Huang _Z3bari:20301:1437
3279971d0dSWilliam Huang  1: 1437
3379971d0dSWilliam Huang _Z3fooi:7711:610
3479971d0dSWilliam Huang  1: 610)";
3579971d0dSWilliam Huang 
3679971d0dSWilliam Huang const char EmptyProfile[18] = "\xff\xe5\xd0\xb1\xf4\xc9\x94\xa8\x53\x67";
3779971d0dSWilliam Huang 
3879971d0dSWilliam Huang /// sys::fs and SampleProf mix Error and error_code, making an adapter class
3979971d0dSWilliam Huang /// to keep code elegant.
4079971d0dSWilliam Huang template <typename T> class ExpectedErrorOr : public Expected<T> {
4179971d0dSWilliam Huang public:
ExpectedErrorOr(T && Obj)4279971d0dSWilliam Huang   ExpectedErrorOr(T &&Obj) : Expected<T>(Obj) {}
4379971d0dSWilliam Huang 
ExpectedErrorOr(std::error_code EC)4479971d0dSWilliam Huang   ExpectedErrorOr(std::error_code EC) : Expected<T>(errorCodeToError(EC)) {}
4579971d0dSWilliam Huang 
ExpectedErrorOr(Error && E)4679971d0dSWilliam Huang   ExpectedErrorOr(Error &&E) : Expected<T>(std::move(E)) {}
4779971d0dSWilliam Huang 
4879971d0dSWilliam Huang   template <typename U>
ExpectedErrorOr(ErrorOr<U> && E)4979971d0dSWilliam Huang   ExpectedErrorOr(ErrorOr<U> &&E)
5079971d0dSWilliam Huang       : Expected<T>(errorCodeToError(E.getError())) {}
5179971d0dSWilliam Huang 
5279971d0dSWilliam Huang   template <typename U>
ExpectedErrorOr(Expected<U> && E)5379971d0dSWilliam Huang   ExpectedErrorOr(Expected<U> &&E) : Expected<T>(E.takeError()) {}
5479971d0dSWilliam Huang };
5579971d0dSWilliam Huang 
5679971d0dSWilliam Huang #define DEF_VAR_RETURN_IF_ERROR(Var, Value)                                    \
5779971d0dSWilliam Huang   auto Var##OrErr = Value;                                                     \
5879971d0dSWilliam Huang   if (!Var##OrErr)                                                             \
5979971d0dSWilliam Huang     return Var##OrErr;                                                         \
6079971d0dSWilliam Huang   auto Var = std::move(Var##OrErr.get())
6179971d0dSWilliam Huang 
6279971d0dSWilliam Huang #define VAR_RETURN_IF_ERROR(Var, Value)                                        \
6379971d0dSWilliam Huang   Var##OrErr = Value;                                                          \
6479971d0dSWilliam Huang   if (!Var##OrErr)                                                             \
6579971d0dSWilliam Huang     return Var##OrErr;                                                         \
6679971d0dSWilliam Huang   Var = std::move(Var##OrErr.get())
6779971d0dSWilliam Huang 
6879971d0dSWilliam Huang #define RETURN_IF_ERROR(Value)                                                 \
6979971d0dSWilliam Huang   if (auto E = Value)                                                          \
7079971d0dSWilliam Huang   return std::move(E)
7179971d0dSWilliam Huang 
7279971d0dSWilliam Huang /// The main testing routine. After rewriting profiles with size limit, check
7379971d0dSWilliam Huang /// the following:
7479971d0dSWilliam Huang /// 1. The file size of the new profile is within the size limit.
7579971d0dSWilliam Huang /// 2. The new profile is a subset of the old profile, and the content of every
7679971d0dSWilliam Huang /// sample in the new profile is unchanged.
7779971d0dSWilliam Huang /// Note that even though by default samples with fewest total count are dropped
7879971d0dSWilliam Huang /// first, this is not a requirement. Samples can be dropped by any order.
RunTest(StringRef Input,size_t SizeLimit,SampleProfileFormat Format,bool Compress=false)7979971d0dSWilliam Huang static ExpectedErrorOr<void *> RunTest(StringRef Input, size_t SizeLimit,
80d8690bc1SWilliam Huang                                        SampleProfileFormat Format,
81d8690bc1SWilliam Huang                                        bool Compress = false) {
8279971d0dSWilliam Huang   // Read Input profile.
8379971d0dSWilliam Huang   auto FS = vfs::getRealFileSystem();
8479971d0dSWilliam Huang   LLVMContext Context;
8579971d0dSWilliam Huang   auto InputBuffer = MemoryBuffer::getMemBuffer(Input);
8679971d0dSWilliam Huang   DEF_VAR_RETURN_IF_ERROR(
8779971d0dSWilliam Huang       Reader, SampleProfileReader::create(InputBuffer, Context, *FS));
8879971d0dSWilliam Huang   RETURN_IF_ERROR(Reader->read());
8979971d0dSWilliam Huang   SampleProfileMap OldProfiles = Reader->getProfiles();
9079971d0dSWilliam Huang 
9179971d0dSWilliam Huang   // Rewrite it to a temp file with size limit.
9279971d0dSWilliam Huang   TempFile Temp("profile", "afdo", "", true);
9379971d0dSWilliam Huang   bool isEmpty = false;
9479971d0dSWilliam Huang   {
9579971d0dSWilliam Huang     DEF_VAR_RETURN_IF_ERROR(Writer,
9679971d0dSWilliam Huang                             SampleProfileWriter::create(Temp.path(), Format));
97d8690bc1SWilliam Huang     if (Compress)
98d8690bc1SWilliam Huang       Writer->setToCompressAllSections();
9979971d0dSWilliam Huang     std::error_code EC = Writer->writeWithSizeLimit(OldProfiles, SizeLimit);
10079971d0dSWilliam Huang     // too_large means no sample could be written because SizeLimit is too
10179971d0dSWilliam Huang     // small. Otherwise any other error code indicates unexpected failure.
10279971d0dSWilliam Huang     if (EC == sampleprof_error::too_large)
10379971d0dSWilliam Huang       isEmpty = true;
10479971d0dSWilliam Huang     else if (EC)
10579971d0dSWilliam Huang       return EC;
10679971d0dSWilliam Huang   }
10779971d0dSWilliam Huang 
10879971d0dSWilliam Huang   // Read the temp file to get new profiles. Use the default empty profile if
10979971d0dSWilliam Huang   // temp file was not written because size limit is too small.
11079971d0dSWilliam Huang   SampleProfileMap NewProfiles;
11179971d0dSWilliam Huang   InputBuffer = MemoryBuffer::getMemBuffer(StringRef(EmptyProfile, 17));
11279971d0dSWilliam Huang   DEF_VAR_RETURN_IF_ERROR(
11379971d0dSWilliam Huang       NewReader, SampleProfileReader::create(InputBuffer, Context, *FS));
11479971d0dSWilliam Huang   if (!isEmpty) {
11579971d0dSWilliam Huang     VAR_RETURN_IF_ERROR(NewReader, SampleProfileReader::create(
11679971d0dSWilliam Huang                                        Temp.path().str(), Context, *FS));
11779971d0dSWilliam Huang     RETURN_IF_ERROR(NewReader->read());
11879971d0dSWilliam Huang     NewProfiles = NewReader->getProfiles();
11979971d0dSWilliam Huang   }
12079971d0dSWilliam Huang 
12179971d0dSWilliam Huang   // Check temp file is actually within size limit.
12279971d0dSWilliam Huang   uint64_t FileSize;
12379971d0dSWilliam Huang   RETURN_IF_ERROR(sys::fs::file_size(Temp.path(), FileSize));
12479971d0dSWilliam Huang   EXPECT_LE(FileSize, SizeLimit);
12579971d0dSWilliam Huang 
12679971d0dSWilliam Huang   // For every sample in the new profile, confirm it is in the old profile and
12779971d0dSWilliam Huang   // unchanged.
12879971d0dSWilliam Huang   for (auto Sample : NewProfiles) {
129*7624de5bSWilliam Huang     auto FindResult = OldProfiles.find(Sample.second.getContext());
13079971d0dSWilliam Huang     EXPECT_NE(FindResult, OldProfiles.end());
13179971d0dSWilliam Huang     if (FindResult != OldProfiles.end()) {
13279971d0dSWilliam Huang       EXPECT_EQ(Sample.second.getHeadSamples(),
13379971d0dSWilliam Huang                 FindResult->second.getHeadSamples());
13479971d0dSWilliam Huang       EXPECT_EQ(Sample.second, FindResult->second);
13579971d0dSWilliam Huang     }
13679971d0dSWilliam Huang   }
13779971d0dSWilliam Huang   return nullptr;
13879971d0dSWilliam Huang }
13979971d0dSWilliam Huang 
TEST(TestOutputSizeLimit,TestOutputSizeLimitExtBinary)14079971d0dSWilliam Huang TEST(TestOutputSizeLimit, TestOutputSizeLimitExtBinary) {
14179971d0dSWilliam Huang   for (size_t OutputSizeLimit : {490, 489, 488, 475, 474, 459, 400})
14279971d0dSWilliam Huang     ASSERT_THAT_EXPECTED(
14379971d0dSWilliam Huang         RunTest(Input1, OutputSizeLimit, llvm::sampleprof::SPF_Ext_Binary),
14479971d0dSWilliam Huang         Succeeded());
14579971d0dSWilliam Huang }
14679971d0dSWilliam Huang 
TEST(TestOutputSizeLimit,TestOutputSizeLimitBinary)14779971d0dSWilliam Huang TEST(TestOutputSizeLimit, TestOutputSizeLimitBinary) {
14879971d0dSWilliam Huang   for (size_t OutputSizeLimit : {250, 249, 248, 237, 236, 223, 200})
14979971d0dSWilliam Huang     ASSERT_THAT_EXPECTED(
15079971d0dSWilliam Huang         RunTest(Input1, OutputSizeLimit, llvm::sampleprof::SPF_Binary),
15179971d0dSWilliam Huang         Succeeded());
15279971d0dSWilliam Huang }
15379971d0dSWilliam Huang 
TEST(TestOutputSizeLimit,TestOutputSizeLimitText)15479971d0dSWilliam Huang TEST(TestOutputSizeLimit, TestOutputSizeLimitText) {
15579971d0dSWilliam Huang   for (size_t OutputSizeLimit :
15679971d0dSWilliam Huang        {229, 228, 227, 213, 212, 211, 189, 188, 187, 186, 150})
15779971d0dSWilliam Huang     ASSERT_THAT_EXPECTED(
15879971d0dSWilliam Huang         RunTest(Input1, OutputSizeLimit, llvm::sampleprof::SPF_Text),
15979971d0dSWilliam Huang         Succeeded());
16079971d0dSWilliam Huang }
161d8690bc1SWilliam Huang 
162406e2fbfSDouglas Yung #if LLVM_ENABLE_ZLIB
TEST(TestOutputSizeLimit,TestOutputSizeLimitExtBinaryCompressed)163d8690bc1SWilliam Huang TEST(TestOutputSizeLimit, TestOutputSizeLimitExtBinaryCompressed) {
164d8690bc1SWilliam Huang   for (size_t OutputSizeLimit :
165d8690bc1SWilliam Huang        {507, 506, 505, 494, 493, 492, 483, 482, 481, 480})
166d8690bc1SWilliam Huang     ASSERT_THAT_EXPECTED(RunTest(Input1, OutputSizeLimit,
167d8690bc1SWilliam Huang                                  llvm::sampleprof::SPF_Ext_Binary, true),
168d8690bc1SWilliam Huang                          Succeeded());
169d8690bc1SWilliam Huang }
170406e2fbfSDouglas Yung #endif
171