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
16using namespace llvm;
17using llvm::unittest::TempFile;
18
19std::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
36const 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.
40template <typename T> class ExpectedErrorOr : public Expected<T> {
41public:
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.
79static 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(InputData: 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(ProfileMap&: OldProfiles, OutputSizeLimit: 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(InputData: 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 every sample in the new profile, confirm it is in the old profile and
127 // unchanged.
128 for (auto Sample : NewProfiles) {
129 auto FindResult = OldProfiles.find(Ctx: Sample.second.getContext());
130 EXPECT_NE(FindResult, OldProfiles.end());
131 if (FindResult != OldProfiles.end()) {
132 EXPECT_EQ(Sample.second.getHeadSamples(),
133 FindResult->second.getHeadSamples());
134 EXPECT_EQ(Sample.second, FindResult->second);
135 }
136 }
137 return nullptr;
138}
139
140TEST(TestOutputSizeLimit, TestOutputSizeLimitExtBinary) {
141 for (size_t OutputSizeLimit : {490, 489, 488, 475, 474, 459, 400})
142 ASSERT_THAT_EXPECTED(
143 RunTest(Input1, OutputSizeLimit, llvm::sampleprof::SPF_Ext_Binary),
144 Succeeded());
145}
146
147TEST(TestOutputSizeLimit, TestOutputSizeLimitBinary) {
148 for (size_t OutputSizeLimit : {250, 249, 248, 237, 236, 223, 200})
149 ASSERT_THAT_EXPECTED(
150 RunTest(Input1, OutputSizeLimit, llvm::sampleprof::SPF_Binary),
151 Succeeded());
152}
153
154TEST(TestOutputSizeLimit, TestOutputSizeLimitText) {
155 for (size_t OutputSizeLimit :
156 {229, 228, 227, 213, 212, 211, 189, 188, 187, 186, 150})
157 ASSERT_THAT_EXPECTED(
158 RunTest(Input1, OutputSizeLimit, llvm::sampleprof::SPF_Text),
159 Succeeded());
160}
161
162#if LLVM_ENABLE_ZLIB
163TEST(TestOutputSizeLimit, TestOutputSizeLimitExtBinaryCompressed) {
164 for (size_t OutputSizeLimit :
165 {507, 506, 505, 494, 493, 492, 483, 482, 481, 480})
166 ASSERT_THAT_EXPECTED(RunTest(Input1, OutputSizeLimit,
167 llvm::sampleprof::SPF_Ext_Binary, true),
168 Succeeded());
169}
170#endif
171

source code of llvm/unittests/tools/llvm-profdata/OutputSizeLimitTest.cpp