aboutsummaryrefslogtreecommitdiff
path: root/llvm/unittests/tools/llvm-profdata/OutputSizeLimitTest.cpp
blob: d21c378e8ce3b226a35b2a4217c75aa8e6f5928d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//===- 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