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

source code of llvm/unittests/XRay/ProfileTest.cpp