1 //===- ProfileTest.cpp - XRay Profile unit tests ----------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
8 #include "llvm/XRay/Profile.h"
9 #include "gmock/gmock.h"
10 #include "gtest/gtest.h"
18 using ::testing::AllOf
;
19 using ::testing::ElementsAre
;
21 using ::testing::Field
;
23 using ::testing::Pair
;
24 using ::testing::UnorderedElementsAre
;
26 TEST(ProfileTest
, CreateProfile
) { Profile P
; }
28 TEST(ProfileTest
, InternPath
) {
30 auto Path0
= P
.internPath({3, 2, 1});
31 auto Path1
= P
.internPath({3, 2, 1});
32 auto Path2
= P
.internPath({2, 1});
33 EXPECT_THAT(Path0
, Eq(Path1
));
34 EXPECT_THAT(Path0
, Not(Eq(Path2
)));
37 TEST(ProfileTest
, ExpandPath
) {
39 auto PathID
= P
.internPath({3, 2, 1});
40 auto PathOrError
= P
.expandPath(PathID
);
42 FAIL() << "Error: " << PathOrError
.takeError();
43 EXPECT_THAT(PathOrError
.get(), ElementsAre(3, 2, 1));
46 TEST(ProfileTest
, AddBlocks
) {
48 // Expect an error on adding empty blocks.
49 EXPECT_TRUE(errorToBool(P
.addBlock({})));
51 // Thread blocks may not be empty.
52 EXPECT_TRUE(errorToBool(P
.addBlock({1, {}})));
54 // Thread blocks with data must succeed.
55 EXPECT_FALSE(errorToBool(P
.addBlock(
56 Profile::Block
{Profile::ThreadID
{1},
58 {P
.internPath({2, 1}), Profile::Data
{1, 1000}},
59 {P
.internPath({3, 2, 1}), Profile::Data
{10, 100}},
63 TEST(ProfileTest
, CopyProfile
) {
65 EXPECT_FALSE(errorToBool(P0
.addBlock(
66 Profile::Block
{Profile::ThreadID
{1},
68 {P0
.internPath({2, 1}), Profile::Data
{1, 1000}},
69 {P0
.internPath({3, 2, 1}), Profile::Data
{10, 100}},
73 P1
, UnorderedElementsAre(AllOf(
74 Field(&Profile::Block::Thread
, Eq(Profile::ThreadID
{1})),
75 Field(&Profile::Block::PathData
,
77 Pair(P1
.internPath({2, 1}),
78 AllOf(Field(&Profile::Data::CallCount
, Eq(1u)),
79 Field(&Profile::Data::CumulativeLocalTime
,
81 Pair(P1
.internPath({3, 2, 1}),
82 AllOf(Field(&Profile::Data::CallCount
, Eq(10u)),
83 Field(&Profile::Data::CumulativeLocalTime
,
87 TEST(ProfileTest
, MoveProfile
) {
89 EXPECT_FALSE(errorToBool(P0
.addBlock(
90 Profile::Block
{Profile::ThreadID
{1},
92 {P0
.internPath({2, 1}), Profile::Data
{1, 1000}},
93 {P0
.internPath({3, 2, 1}), Profile::Data
{10, 100}},
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
,
105 Pair(P1
.internPath({3, 2, 1}),
106 AllOf(Field(&Profile::Data::CallCount
, Eq(10u)),
107 Field(&Profile::Data::CumulativeLocalTime
,
109 EXPECT_THAT(P0
, UnorderedElementsAre());
112 TEST(ProfileTest
, MergeProfilesByThread
) {
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}}}})));
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}}}})));
133 Profile Merged
= mergeProfilesByThread(P0
, P1
);
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
,
145 Pair(Merged
.internPath({4, 1}),
146 AllOf(Field(&Profile::Data::CallCount
, Eq(1u)),
147 Field(&Profile::Data::CumulativeLocalTime
,
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
,
156 Pair(Merged
.internPath({4, 1}),
157 AllOf(Field(&Profile::Data::CallCount
, Eq(1u)),
158 Field(&Profile::Data::CumulativeLocalTime
,
162 TEST(ProfileTest
, MergeProfilesByStack
) {
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}}}})));
171 Profile Merged
= mergeProfilesByStack(P0
, P1
);
174 // We expect that we lose the ThreadID dimension in this
176 Field(&Profile::Block::Thread
, Eq(Profile::ThreadID
{0})),
177 Field(&Profile::Block::PathData
,
179 Merged
.internPath({2, 1}),
180 AllOf(Field(&Profile::Data::CallCount
, Eq(2u)),
181 Field(&Profile::Data::CumulativeLocalTime
,
185 TEST(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(Profiles
.begin(), Profiles
.end(), Profile(),
197 mergeProfilesByStack
);
200 // We expect that we lose the ThreadID dimension in this
202 Field(&Profile::Block::Thread
, Eq(Profile::ThreadID
{0})),
203 Field(&Profile::Block::PathData
,
205 Merged
.internPath({2, 1}),
206 AllOf(Field(&Profile::Data::CallCount
, Eq(3u)),
207 Field(&Profile::Data::CumulativeLocalTime
,
211 TEST(ProfileTest
, MergeProfilesByThreadAccumulate
) {
212 std::vector
<Profile
> Profiles(2);
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}}}})));
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}}}})));
232 Profile Merged
= std::accumulate(Profiles
.begin(), Profiles
.end(), Profile(),
233 mergeProfilesByThread
);
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
,
245 Pair(Merged
.internPath({4, 1}),
246 AllOf(Field(&Profile::Data::CallCount
, Eq(1u)),
247 Field(&Profile::Data::CumulativeLocalTime
,
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
,
256 Pair(Merged
.internPath({4, 1}),
257 AllOf(Field(&Profile::Data::CallCount
, Eq(1u)),
258 Field(&Profile::Data::CumulativeLocalTime
,
261 // FIXME: Add a test creating a Trace and generating a Profile
262 // FIXME: Add tests for ranking/sorting profile blocks by dimension