//===- 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 class ExpectedErrorOr : public Expected { public: ExpectedErrorOr(T &&Obj) : Expected(Obj) {} ExpectedErrorOr(std::error_code EC) : Expected(errorCodeToError(EC)) {} ExpectedErrorOr(Error &&E) : Expected(std::move(E)) {} template ExpectedErrorOr(ErrorOr &&E) : Expected(errorCodeToError(E.getError())) {} template ExpectedErrorOr(Expected &&E) : Expected(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 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