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