1 //===- ProfileTest.cpp - XRay Profile unit tests ----------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 #include "llvm/XRay/Profile.h"
10 #include "gmock/gmock.h"
11 #include "gtest/gtest.h"
12 
13 #include <numeric>
14 
15 namespace llvm {
16 namespace xray {
17 namespace {
18 
19 using ::testing::AllOf;
20 using ::testing::ElementsAre;
21 using ::testing::Eq;
22 using ::testing::Field;
23 using ::testing::Not;
24 using ::testing::Pair;
25 using ::testing::UnorderedElementsAre;
26 
27 TEST(ProfileTest, CreateProfile) { Profile P; }
28 
29 TEST(ProfileTest, InternPath) {
30   Profile P;
31   auto Path0 = P.internPath({3, 2, 1});
32   auto Path1 = P.internPath({3, 2, 1});
33   auto Path2 = P.internPath({2, 1});
34   EXPECT_THAT(Path0, Eq(Path1));
35   EXPECT_THAT(Path0, Not(Eq(Path2)));
36 }
37 
38 TEST(ProfileTest, ExpandPath) {
39   Profile P;
40   auto PathID = P.internPath({3, 2, 1});
41   auto PathOrError = P.expandPath(PathID);
42   if (!PathOrError)
43     FAIL() << "Error: " << PathOrError.takeError();
44   EXPECT_THAT(PathOrError.get(), ElementsAre(3, 2, 1));
45 }
46 
47 TEST(ProfileTest, AddBlocks) {
48   Profile P;
49   // Expect an error on adding empty blocks.
50   EXPECT_TRUE(errorToBool(P.addBlock({})));
51 
52   // Thread blocks may not be empty.
53   EXPECT_TRUE(errorToBool(P.addBlock({1, {}})));
54 
55   // Thread blocks with data must succeed.
56   EXPECT_FALSE(errorToBool(P.addBlock(
57       Profile::Block{Profile::ThreadID{1},
58                      {
59                          {P.internPath({2, 1}), Profile::Data{1, 1000}},
60                          {P.internPath({3, 2, 1}), Profile::Data{10, 100}},
61                      }})));
62 }
63 
64 TEST(ProfileTest, CopyProfile) {
65   Profile P0, P1;
66   EXPECT_FALSE(errorToBool(P0.addBlock(
67       Profile::Block{Profile::ThreadID{1},
68                      {
69                          {P0.internPath({2, 1}), Profile::Data{1, 1000}},
70                          {P0.internPath({3, 2, 1}), Profile::Data{10, 100}},
71                      }})));
72   P1 = P0;
73   EXPECT_THAT(
74       P1, UnorderedElementsAre(AllOf(
75               Field(&Profile::Block::Thread, Eq(Profile::ThreadID{1})),
76               Field(&Profile::Block::PathData,
77                     UnorderedElementsAre(
78                         Pair(P1.internPath({2, 1}),
79                              AllOf(Field(&Profile::Data::CallCount, Eq(1u)),
80                                    Field(&Profile::Data::CumulativeLocalTime,
81                                          Eq(1000u)))),
82                         Pair(P1.internPath({3, 2, 1}),
83                              AllOf(Field(&Profile::Data::CallCount, Eq(10u)),
84                                    Field(&Profile::Data::CumulativeLocalTime,
85                                          Eq(100u)))))))));
86 }
87 
88 TEST(ProfileTest, MoveProfile) {
89   Profile P0, P1;
90   EXPECT_FALSE(errorToBool(P0.addBlock(
91       Profile::Block{Profile::ThreadID{1},
92                      {
93                          {P0.internPath({2, 1}), Profile::Data{1, 1000}},
94                          {P0.internPath({3, 2, 1}), Profile::Data{10, 100}},
95                      }})));
96   P1 = std::move(P0);
97   EXPECT_THAT(
98       P1, UnorderedElementsAre(AllOf(
99               Field(&Profile::Block::Thread, Eq(Profile::ThreadID{1})),
100               Field(&Profile::Block::PathData,
101                     UnorderedElementsAre(
102                         Pair(P1.internPath({2, 1}),
103                              AllOf(Field(&Profile::Data::CallCount, Eq(1u)),
104                                    Field(&Profile::Data::CumulativeLocalTime,
105                                          Eq(1000u)))),
106                         Pair(P1.internPath({3, 2, 1}),
107                              AllOf(Field(&Profile::Data::CallCount, Eq(10u)),
108                                    Field(&Profile::Data::CumulativeLocalTime,
109                                          Eq(100u)))))))));
110   EXPECT_THAT(P0, UnorderedElementsAre());
111 }
112 
113 TEST(ProfileTest, MergeProfilesByThread) {
114   Profile P0, P1;
115 
116   // Set up the blocks for two different threads in P0.
117   EXPECT_FALSE(errorToBool(P0.addBlock(
118       Profile::Block{Profile::ThreadID{1},
119                      {{P0.internPath({2, 1}), Profile::Data{1, 1000}},
120                       {P0.internPath({4, 1}), Profile::Data{1, 1000}}}})));
121   EXPECT_FALSE(errorToBool(P0.addBlock(
122       Profile::Block{Profile::ThreadID{2},
123                      {{P0.internPath({3, 1}), Profile::Data{1, 1000}}}})));
124 
125   // Set up the blocks for two different threads in P1.
126   EXPECT_FALSE(errorToBool(P1.addBlock(
127       Profile::Block{Profile::ThreadID{1},
128                      {{P1.internPath({2, 1}), Profile::Data{1, 1000}}}})));
129   EXPECT_FALSE(errorToBool(P1.addBlock(
130       Profile::Block{Profile::ThreadID{2},
131                      {{P1.internPath({3, 1}), Profile::Data{1, 1000}},
132                       {P1.internPath({4, 1}), Profile::Data{1, 1000}}}})));
133 
134   Profile Merged = mergeProfilesByThread(P0, P1);
135   EXPECT_THAT(
136       Merged,
137       UnorderedElementsAre(
138           // We want to see two threads after the merge.
139           AllOf(Field(&Profile::Block::Thread, Eq(Profile::ThreadID{1})),
140                 Field(&Profile::Block::PathData,
141                       UnorderedElementsAre(
142                           Pair(Merged.internPath({2, 1}),
143                                AllOf(Field(&Profile::Data::CallCount, Eq(2u)),
144                                      Field(&Profile::Data::CumulativeLocalTime,
145                                            Eq(2000u)))),
146                           Pair(Merged.internPath({4, 1}),
147                                AllOf(Field(&Profile::Data::CallCount, Eq(1u)),
148                                      Field(&Profile::Data::CumulativeLocalTime,
149                                            Eq(1000u))))))),
150           AllOf(Field(&Profile::Block::Thread, Eq(Profile::ThreadID{2})),
151                 Field(&Profile::Block::PathData,
152                       UnorderedElementsAre(
153                           Pair(Merged.internPath({3, 1}),
154                                AllOf(Field(&Profile::Data::CallCount, Eq(2u)),
155                                      Field(&Profile::Data::CumulativeLocalTime,
156                                            Eq(2000u)))),
157                           Pair(Merged.internPath({4, 1}),
158                                AllOf(Field(&Profile::Data::CallCount, Eq(1u)),
159                                      Field(&Profile::Data::CumulativeLocalTime,
160                                            Eq(1000u)))))))));
161 }
162 
163 TEST(ProfileTest, MergeProfilesByStack) {
164   Profile P0, P1;
165   EXPECT_FALSE(errorToBool(P0.addBlock(
166       Profile::Block{Profile::ThreadID{1},
167                      {{P0.internPath({2, 1}), Profile::Data{1, 1000}}}})));
168   EXPECT_FALSE(errorToBool(P1.addBlock(
169       Profile::Block{Profile::ThreadID{2},
170                      {{P1.internPath({2, 1}), Profile::Data{1, 1000}}}})));
171 
172   Profile Merged = mergeProfilesByStack(P0, P1);
173   EXPECT_THAT(Merged,
174               ElementsAre(AllOf(
175                   // We expect that we lose the ThreadID dimension in this
176                   // algorithm.
177                   Field(&Profile::Block::Thread, Eq(Profile::ThreadID{0})),
178                   Field(&Profile::Block::PathData,
179                         ElementsAre(Pair(
180                             Merged.internPath({2, 1}),
181                             AllOf(Field(&Profile::Data::CallCount, Eq(2u)),
182                                   Field(&Profile::Data::CumulativeLocalTime,
183                                         Eq(2000u)))))))));
184 }
185 
186 TEST(ProfileTest, MergeProfilesByStackAccumulate) {
187   std::vector<Profile> Profiles(3);
188   EXPECT_FALSE(errorToBool(Profiles[0].addBlock(Profile::Block{
189       Profile::ThreadID{1},
190       {{Profiles[0].internPath({2, 1}), Profile::Data{1, 1000}}}})));
191   EXPECT_FALSE(errorToBool(Profiles[1].addBlock(Profile::Block{
192       Profile::ThreadID{2},
193       {{Profiles[1].internPath({2, 1}), Profile::Data{1, 1000}}}})));
194   EXPECT_FALSE(errorToBool(Profiles[2].addBlock(Profile::Block{
195       Profile::ThreadID{3},
196       {{Profiles[2].internPath({2, 1}), Profile::Data{1, 1000}}}})));
197   Profile Merged = std::accumulate(Profiles.begin(), Profiles.end(), Profile(),
198                                    mergeProfilesByStack);
199   EXPECT_THAT(Merged,
200               ElementsAre(AllOf(
201                   // We expect that we lose the ThreadID dimension in this
202                   // algorithm.
203                   Field(&Profile::Block::Thread, Eq(Profile::ThreadID{0})),
204                   Field(&Profile::Block::PathData,
205                         ElementsAre(Pair(
206                             Merged.internPath({2, 1}),
207                             AllOf(Field(&Profile::Data::CallCount, Eq(3u)),
208                                   Field(&Profile::Data::CumulativeLocalTime,
209                                         Eq(3000u)))))))));
210 }
211 
212 TEST(ProfileTest, MergeProfilesByThreadAccumulate) {
213   std::vector<Profile> Profiles(2);
214 
215   // Set up the blocks for two different threads in Profiles[0].
216   EXPECT_FALSE(errorToBool(Profiles[0].addBlock(Profile::Block{
217       Profile::ThreadID{1},
218       {{Profiles[0].internPath({2, 1}), Profile::Data{1, 1000}},
219        {Profiles[0].internPath({4, 1}), Profile::Data{1, 1000}}}})));
220   EXPECT_FALSE(errorToBool(Profiles[0].addBlock(Profile::Block{
221       Profile::ThreadID{2},
222       {{Profiles[0].internPath({3, 1}), Profile::Data{1, 1000}}}})));
223 
224   // Set up the blocks for two different threads in Profiles[1].
225   EXPECT_FALSE(errorToBool(Profiles[1].addBlock(Profile::Block{
226       Profile::ThreadID{1},
227       {{Profiles[1].internPath({2, 1}), Profile::Data{1, 1000}}}})));
228   EXPECT_FALSE(errorToBool(Profiles[1].addBlock(Profile::Block{
229       Profile::ThreadID{2},
230       {{Profiles[1].internPath({3, 1}), Profile::Data{1, 1000}},
231        {Profiles[1].internPath({4, 1}), Profile::Data{1, 1000}}}})));
232 
233   Profile Merged = std::accumulate(Profiles.begin(), Profiles.end(), Profile(),
234                                    mergeProfilesByThread);
235   EXPECT_THAT(
236       Merged,
237       UnorderedElementsAre(
238           // We want to see two threads after the merge.
239           AllOf(Field(&Profile::Block::Thread, Eq(Profile::ThreadID{1})),
240                 Field(&Profile::Block::PathData,
241                       UnorderedElementsAre(
242                           Pair(Merged.internPath({2, 1}),
243                                AllOf(Field(&Profile::Data::CallCount, Eq(2u)),
244                                      Field(&Profile::Data::CumulativeLocalTime,
245                                            Eq(2000u)))),
246                           Pair(Merged.internPath({4, 1}),
247                                AllOf(Field(&Profile::Data::CallCount, Eq(1u)),
248                                      Field(&Profile::Data::CumulativeLocalTime,
249                                            Eq(1000u))))))),
250           AllOf(Field(&Profile::Block::Thread, Eq(Profile::ThreadID{2})),
251                 Field(&Profile::Block::PathData,
252                       UnorderedElementsAre(
253                           Pair(Merged.internPath({3, 1}),
254                                AllOf(Field(&Profile::Data::CallCount, Eq(2u)),
255                                      Field(&Profile::Data::CumulativeLocalTime,
256                                            Eq(2000u)))),
257                           Pair(Merged.internPath({4, 1}),
258                                AllOf(Field(&Profile::Data::CallCount, Eq(1u)),
259                                      Field(&Profile::Data::CumulativeLocalTime,
260                                            Eq(1000u)))))))));
261 }
262 // FIXME: Add a test creating a Trace and generating a Profile
263 // FIXME: Add tests for ranking/sorting profile blocks by dimension
264 
265 } // namespace
266 } // namespace xray
267 } // namespace llvm
268