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