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