| //===- llvm/unittests/tools/llvm-profdata/OutputSizeLimitTest.cpp ---------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/ProfileData/SampleProfReader.h" |
| #include "llvm/ProfileData/SampleProfWriter.h" |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/VirtualFileSystem.h" |
| #include "llvm/Testing/Support/Error.h" |
| #include "gtest/gtest.h" |
| |
| using namespace llvm; |
| using llvm::unittest::TempFile; |
| |
| std::string Input1 = R"(main:184019:0 |
| 4: 534 |
| 4.2: 534 |
| 5: 1075 |
| 5.1: 1075 |
| 6: 2080 |
| 7: 534 |
| 9: 2064 _Z3bari:1471 _Z3fooi:631 |
| 10: inline1:1000 |
| 1: 1000 |
| 10: inline2:2000 |
| 1: 2000 |
| _Z3bari:20301:1437 |
| 1: 1437 |
| _Z3fooi:7711:610 |
| 1: 610)"; |
| |
| const char EmptyProfile[18] = "\xff\xe5\xd0\xb1\xf4\xc9\x94\xa8\x53\x67"; |
| |
| /// sys::fs and SampleProf mix Error and error_code, making an adapter class |
| /// to keep code elegant. |
| template <typename T> class ExpectedErrorOr : public Expected<T> { |
| public: |
| ExpectedErrorOr(T &&Obj) : Expected<T>(Obj) {} |
| |
| ExpectedErrorOr(std::error_code EC) : Expected<T>(errorCodeToError(EC)) {} |
| |
| ExpectedErrorOr(Error &&E) : Expected<T>(std::move(E)) {} |
| |
| template <typename U> |
| ExpectedErrorOr(ErrorOr<U> &&E) |
| : Expected<T>(errorCodeToError(E.getError())) {} |
| |
| template <typename U> |
| ExpectedErrorOr(Expected<U> &&E) : Expected<T>(E.takeError()) {} |
| }; |
| |
| #define DEF_VAR_RETURN_IF_ERROR(Var, Value) \ |
| auto Var##OrErr = Value; \ |
| if (!Var##OrErr) \ |
| return Var##OrErr; \ |
| auto Var = std::move(Var##OrErr.get()) |
| |
| #define VAR_RETURN_IF_ERROR(Var, Value) \ |
| Var##OrErr = Value; \ |
| if (!Var##OrErr) \ |
| return Var##OrErr; \ |
| Var = std::move(Var##OrErr.get()) |
| |
| #define RETURN_IF_ERROR(Value) \ |
| if (auto E = Value) \ |
| return std::move(E) |
| |
| /// The main testing routine. After rewriting profiles with size limit, check |
| /// the following: |
| /// 1. The file size of the new profile is within the size limit. |
| /// 2. The new profile is a subset of the old profile, and the content of every |
| /// sample in the new profile is unchanged. |
| /// Note that even though by default samples with fewest total count are dropped |
| /// first, this is not a requirement. Samples can be dropped by any order. |
| static ExpectedErrorOr<void *> RunTest(StringRef Input, size_t SizeLimit, |
| SampleProfileFormat Format, |
| bool Compress = false) { |
| // Read Input profile. |
| auto FS = vfs::getRealFileSystem(); |
| LLVMContext Context; |
| auto InputBuffer = MemoryBuffer::getMemBuffer(Input); |
| DEF_VAR_RETURN_IF_ERROR( |
| Reader, SampleProfileReader::create(InputBuffer, Context, *FS)); |
| RETURN_IF_ERROR(Reader->read()); |
| SampleProfileMap OldProfiles = Reader->getProfiles(); |
| |
| // Rewrite it to a temp file with size limit. |
| TempFile Temp("profile", "afdo", "", true); |
| bool isEmpty = false; |
| { |
| DEF_VAR_RETURN_IF_ERROR(Writer, |
| SampleProfileWriter::create(Temp.path(), Format)); |
| if (Compress) |
| Writer->setToCompressAllSections(); |
| std::error_code EC = Writer->writeWithSizeLimit(OldProfiles, SizeLimit); |
| // too_large means no sample could be written because SizeLimit is too |
| // small. Otherwise any other error code indicates unexpected failure. |
| if (EC == sampleprof_error::too_large) |
| isEmpty = true; |
| else if (EC) |
| return EC; |
| } |
| |
| // Read the temp file to get new profiles. Use the default empty profile if |
| // temp file was not written because size limit is too small. |
| SampleProfileMap NewProfiles; |
| InputBuffer = MemoryBuffer::getMemBuffer(StringRef(EmptyProfile, 17)); |
| DEF_VAR_RETURN_IF_ERROR( |
| NewReader, SampleProfileReader::create(InputBuffer, Context, *FS)); |
| if (!isEmpty) { |
| VAR_RETURN_IF_ERROR(NewReader, SampleProfileReader::create( |
| Temp.path().str(), Context, *FS)); |
| RETURN_IF_ERROR(NewReader->read()); |
| NewProfiles = NewReader->getProfiles(); |
| } |
| |
| // Check temp file is actually within size limit. |
| uint64_t FileSize; |
| RETURN_IF_ERROR(sys::fs::file_size(Temp.path(), FileSize)); |
| EXPECT_LE(FileSize, SizeLimit); |
| |
| // For every sample in the new profile, confirm it is in the old profile and |
| // unchanged. |
| for (auto Sample : NewProfiles) { |
| auto FindResult = OldProfiles.find(Sample.second.getContext()); |
| EXPECT_NE(FindResult, OldProfiles.end()); |
| if (FindResult != OldProfiles.end()) { |
| EXPECT_EQ(Sample.second.getHeadSamples(), |
| FindResult->second.getHeadSamples()); |
| EXPECT_EQ(Sample.second, FindResult->second); |
| } |
| } |
| return nullptr; |
| } |
| |
| TEST(TestOutputSizeLimit, TestOutputSizeLimitExtBinary) { |
| for (size_t OutputSizeLimit : {490, 489, 488, 475, 474, 459, 400}) |
| ASSERT_THAT_EXPECTED( |
| RunTest(Input1, OutputSizeLimit, llvm::sampleprof::SPF_Ext_Binary), |
| Succeeded()); |
| } |
| |
| TEST(TestOutputSizeLimit, TestOutputSizeLimitBinary) { |
| for (size_t OutputSizeLimit : {250, 249, 248, 237, 236, 223, 200}) |
| ASSERT_THAT_EXPECTED( |
| RunTest(Input1, OutputSizeLimit, llvm::sampleprof::SPF_Binary), |
| Succeeded()); |
| } |
| |
| TEST(TestOutputSizeLimit, TestOutputSizeLimitText) { |
| for (size_t OutputSizeLimit : |
| {229, 228, 227, 213, 212, 211, 189, 188, 187, 186, 150}) |
| ASSERT_THAT_EXPECTED( |
| RunTest(Input1, OutputSizeLimit, llvm::sampleprof::SPF_Text), |
| Succeeded()); |
| } |
| |
| #if LLVM_ENABLE_ZLIB |
| TEST(TestOutputSizeLimit, TestOutputSizeLimitExtBinaryCompressed) { |
| for (size_t OutputSizeLimit : |
| {507, 506, 505, 494, 493, 492, 483, 482, 481, 480}) |
| ASSERT_THAT_EXPECTED(RunTest(Input1, OutputSizeLimit, |
| llvm::sampleprof::SPF_Ext_Binary, true), |
| Succeeded()); |
| } |
| #endif |