267 lines · cpp
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-exception6//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({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)));35}36 37TEST(ProfileTest, ExpandPath) {38 Profile P;39 auto PathID = P.internPath({3, 2, 1});40 auto PathOrError = P.expandPath(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(P0, 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(P0, P1);172 EXPECT_THAT(Merged,173 ElementsAre(AllOf(174 // We expect that we lose the ThreadID dimension in this175 // 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(Profiles.begin(), Profiles.end(), Profile(),197 mergeProfilesByStack);198 EXPECT_THAT(Merged,199 ElementsAre(AllOf(200 // We expect that we lose the ThreadID dimension in this201 // 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(Profiles.begin(), Profiles.end(), Profile(),233 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 Profile262// FIXME: Add tests for ranking/sorting profile blocks by dimension263 264} // namespace265} // namespace xray266} // namespace llvm267