267 lines
12 KiB
C++
267 lines
12 KiB
C++
|
//===- ProfileTest.cpp - XRay Profile unit tests ----------------*- C++ -*-===//
|
||
|
//
|
||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
#include "llvm/XRay/Profile.h"
|
||
|
#include "gmock/gmock.h"
|
||
|
#include "gtest/gtest.h"
|
||
|
|
||
|
#include <numeric>
|
||
|
|
||
|
namespace llvm {
|
||
|
namespace xray {
|
||
|
namespace {
|
||
|
|
||
|
using ::testing::AllOf;
|
||
|
using ::testing::ElementsAre;
|
||
|
using ::testing::Eq;
|
||
|
using ::testing::Field;
|
||
|
using ::testing::Not;
|
||
|
using ::testing::Pair;
|
||
|
using ::testing::UnorderedElementsAre;
|
||
|
|
||
|
TEST(ProfileTest, CreateProfile) { Profile P; }
|
||
|
|
||
|
TEST(ProfileTest, InternPath) {
|
||
|
Profile P;
|
||
|
auto Path0 = P.internPath({3, 2, 1});
|
||
|
auto Path1 = P.internPath({3, 2, 1});
|
||
|
auto Path2 = P.internPath({2, 1});
|
||
|
EXPECT_THAT(Path0, Eq(Path1));
|
||
|
EXPECT_THAT(Path0, Not(Eq(Path2)));
|
||
|
}
|
||
|
|
||
|
TEST(ProfileTest, ExpandPath) {
|
||
|
Profile P;
|
||
|
auto PathID = P.internPath({3, 2, 1});
|
||
|
auto PathOrError = P.expandPath(PathID);
|
||
|
if (!PathOrError)
|
||
|
FAIL() << "Error: " << PathOrError.takeError();
|
||
|
EXPECT_THAT(PathOrError.get(), ElementsAre(3, 2, 1));
|
||
|
}
|
||
|
|
||
|
TEST(ProfileTest, AddBlocks) {
|
||
|
Profile P;
|
||
|
// Expect an error on adding empty blocks.
|
||
|
EXPECT_TRUE(errorToBool(P.addBlock({})));
|
||
|
|
||
|
// Thread blocks may not be empty.
|
||
|
EXPECT_TRUE(errorToBool(P.addBlock({1, {}})));
|
||
|
|
||
|
// Thread blocks with data must succeed.
|
||
|
EXPECT_FALSE(errorToBool(P.addBlock(
|
||
|
Profile::Block{Profile::ThreadID{1},
|
||
|
{
|
||
|
{P.internPath({2, 1}), Profile::Data{1, 1000}},
|
||
|
{P.internPath({3, 2, 1}), Profile::Data{10, 100}},
|
||
|
}})));
|
||
|
}
|
||
|
|
||
|
TEST(ProfileTest, CopyProfile) {
|
||
|
Profile P0, P1;
|
||
|
EXPECT_FALSE(errorToBool(P0.addBlock(
|
||
|
Profile::Block{Profile::ThreadID{1},
|
||
|
{
|
||
|
{P0.internPath({2, 1}), Profile::Data{1, 1000}},
|
||
|
{P0.internPath({3, 2, 1}), Profile::Data{10, 100}},
|
||
|
}})));
|
||
|
P1 = P0;
|
||
|
EXPECT_THAT(
|
||
|
P1, UnorderedElementsAre(AllOf(
|
||
|
Field(&Profile::Block::Thread, Eq(Profile::ThreadID{1})),
|
||
|
Field(&Profile::Block::PathData,
|
||
|
UnorderedElementsAre(
|
||
|
Pair(P1.internPath({2, 1}),
|
||
|
AllOf(Field(&Profile::Data::CallCount, Eq(1u)),
|
||
|
Field(&Profile::Data::CumulativeLocalTime,
|
||
|
Eq(1000u)))),
|
||
|
Pair(P1.internPath({3, 2, 1}),
|
||
|
AllOf(Field(&Profile::Data::CallCount, Eq(10u)),
|
||
|
Field(&Profile::Data::CumulativeLocalTime,
|
||
|
Eq(100u)))))))));
|
||
|
}
|
||
|
|
||
|
TEST(ProfileTest, MoveProfile) {
|
||
|
Profile P0, P1;
|
||
|
EXPECT_FALSE(errorToBool(P0.addBlock(
|
||
|
Profile::Block{Profile::ThreadID{1},
|
||
|
{
|
||
|
{P0.internPath({2, 1}), Profile::Data{1, 1000}},
|
||
|
{P0.internPath({3, 2, 1}), Profile::Data{10, 100}},
|
||
|
}})));
|
||
|
P1 = std::move(P0);
|
||
|
EXPECT_THAT(
|
||
|
P1, UnorderedElementsAre(AllOf(
|
||
|
Field(&Profile::Block::Thread, Eq(Profile::ThreadID{1})),
|
||
|
Field(&Profile::Block::PathData,
|
||
|
UnorderedElementsAre(
|
||
|
Pair(P1.internPath({2, 1}),
|
||
|
AllOf(Field(&Profile::Data::CallCount, Eq(1u)),
|
||
|
Field(&Profile::Data::CumulativeLocalTime,
|
||
|
Eq(1000u)))),
|
||
|
Pair(P1.internPath({3, 2, 1}),
|
||
|
AllOf(Field(&Profile::Data::CallCount, Eq(10u)),
|
||
|
Field(&Profile::Data::CumulativeLocalTime,
|
||
|
Eq(100u)))))))));
|
||
|
EXPECT_THAT(P0, UnorderedElementsAre());
|
||
|
}
|
||
|
|
||
|
TEST(ProfileTest, MergeProfilesByThread) {
|
||
|
Profile P0, P1;
|
||
|
|
||
|
// Set up the blocks for two different threads in P0.
|
||
|
EXPECT_FALSE(errorToBool(P0.addBlock(
|
||
|
Profile::Block{Profile::ThreadID{1},
|
||
|
{{P0.internPath({2, 1}), Profile::Data{1, 1000}},
|
||
|
{P0.internPath({4, 1}), Profile::Data{1, 1000}}}})));
|
||
|
EXPECT_FALSE(errorToBool(P0.addBlock(
|
||
|
Profile::Block{Profile::ThreadID{2},
|
||
|
{{P0.internPath({3, 1}), Profile::Data{1, 1000}}}})));
|
||
|
|
||
|
// Set up the blocks for two different threads in P1.
|
||
|
EXPECT_FALSE(errorToBool(P1.addBlock(
|
||
|
Profile::Block{Profile::ThreadID{1},
|
||
|
{{P1.internPath({2, 1}), Profile::Data{1, 1000}}}})));
|
||
|
EXPECT_FALSE(errorToBool(P1.addBlock(
|
||
|
Profile::Block{Profile::ThreadID{2},
|
||
|
{{P1.internPath({3, 1}), Profile::Data{1, 1000}},
|
||
|
{P1.internPath({4, 1}), Profile::Data{1, 1000}}}})));
|
||
|
|
||
|
Profile Merged = mergeProfilesByThread(P0, P1);
|
||
|
EXPECT_THAT(
|
||
|
Merged,
|
||
|
UnorderedElementsAre(
|
||
|
// We want to see two threads after the merge.
|
||
|
AllOf(Field(&Profile::Block::Thread, Eq(Profile::ThreadID{1})),
|
||
|
Field(&Profile::Block::PathData,
|
||
|
UnorderedElementsAre(
|
||
|
Pair(Merged.internPath({2, 1}),
|
||
|
AllOf(Field(&Profile::Data::CallCount, Eq(2u)),
|
||
|
Field(&Profile::Data::CumulativeLocalTime,
|
||
|
Eq(2000u)))),
|
||
|
Pair(Merged.internPath({4, 1}),
|
||
|
AllOf(Field(&Profile::Data::CallCount, Eq(1u)),
|
||
|
Field(&Profile::Data::CumulativeLocalTime,
|
||
|
Eq(1000u))))))),
|
||
|
AllOf(Field(&Profile::Block::Thread, Eq(Profile::ThreadID{2})),
|
||
|
Field(&Profile::Block::PathData,
|
||
|
UnorderedElementsAre(
|
||
|
Pair(Merged.internPath({3, 1}),
|
||
|
AllOf(Field(&Profile::Data::CallCount, Eq(2u)),
|
||
|
Field(&Profile::Data::CumulativeLocalTime,
|
||
|
Eq(2000u)))),
|
||
|
Pair(Merged.internPath({4, 1}),
|
||
|
AllOf(Field(&Profile::Data::CallCount, Eq(1u)),
|
||
|
Field(&Profile::Data::CumulativeLocalTime,
|
||
|
Eq(1000u)))))))));
|
||
|
}
|
||
|
|
||
|
TEST(ProfileTest, MergeProfilesByStack) {
|
||
|
Profile P0, P1;
|
||
|
EXPECT_FALSE(errorToBool(P0.addBlock(
|
||
|
Profile::Block{Profile::ThreadID{1},
|
||
|
{{P0.internPath({2, 1}), Profile::Data{1, 1000}}}})));
|
||
|
EXPECT_FALSE(errorToBool(P1.addBlock(
|
||
|
Profile::Block{Profile::ThreadID{2},
|
||
|
{{P1.internPath({2, 1}), Profile::Data{1, 1000}}}})));
|
||
|
|
||
|
Profile Merged = mergeProfilesByStack(P0, P1);
|
||
|
EXPECT_THAT(Merged,
|
||
|
ElementsAre(AllOf(
|
||
|
// We expect that we lose the ThreadID dimension in this
|
||
|
// algorithm.
|
||
|
Field(&Profile::Block::Thread, Eq(Profile::ThreadID{0})),
|
||
|
Field(&Profile::Block::PathData,
|
||
|
ElementsAre(Pair(
|
||
|
Merged.internPath({2, 1}),
|
||
|
AllOf(Field(&Profile::Data::CallCount, Eq(2u)),
|
||
|
Field(&Profile::Data::CumulativeLocalTime,
|
||
|
Eq(2000u)))))))));
|
||
|
}
|
||
|
|
||
|
TEST(ProfileTest, MergeProfilesByStackAccumulate) {
|
||
|
std::vector<Profile> Profiles(3);
|
||
|
EXPECT_FALSE(errorToBool(Profiles[0].addBlock(Profile::Block{
|
||
|
Profile::ThreadID{1},
|
||
|
{{Profiles[0].internPath({2, 1}), Profile::Data{1, 1000}}}})));
|
||
|
EXPECT_FALSE(errorToBool(Profiles[1].addBlock(Profile::Block{
|
||
|
Profile::ThreadID{2},
|
||
|
{{Profiles[1].internPath({2, 1}), Profile::Data{1, 1000}}}})));
|
||
|
EXPECT_FALSE(errorToBool(Profiles[2].addBlock(Profile::Block{
|
||
|
Profile::ThreadID{3},
|
||
|
{{Profiles[2].internPath({2, 1}), Profile::Data{1, 1000}}}})));
|
||
|
Profile Merged = std::accumulate(Profiles.begin(), Profiles.end(), Profile(),
|
||
|
mergeProfilesByStack);
|
||
|
EXPECT_THAT(Merged,
|
||
|
ElementsAre(AllOf(
|
||
|
// We expect that we lose the ThreadID dimension in this
|
||
|
// algorithm.
|
||
|
Field(&Profile::Block::Thread, Eq(Profile::ThreadID{0})),
|
||
|
Field(&Profile::Block::PathData,
|
||
|
ElementsAre(Pair(
|
||
|
Merged.internPath({2, 1}),
|
||
|
AllOf(Field(&Profile::Data::CallCount, Eq(3u)),
|
||
|
Field(&Profile::Data::CumulativeLocalTime,
|
||
|
Eq(3000u)))))))));
|
||
|
}
|
||
|
|
||
|
TEST(ProfileTest, MergeProfilesByThreadAccumulate) {
|
||
|
std::vector<Profile> Profiles(2);
|
||
|
|
||
|
// Set up the blocks for two different threads in Profiles[0].
|
||
|
EXPECT_FALSE(errorToBool(Profiles[0].addBlock(Profile::Block{
|
||
|
Profile::ThreadID{1},
|
||
|
{{Profiles[0].internPath({2, 1}), Profile::Data{1, 1000}},
|
||
|
{Profiles[0].internPath({4, 1}), Profile::Data{1, 1000}}}})));
|
||
|
EXPECT_FALSE(errorToBool(Profiles[0].addBlock(Profile::Block{
|
||
|
Profile::ThreadID{2},
|
||
|
{{Profiles[0].internPath({3, 1}), Profile::Data{1, 1000}}}})));
|
||
|
|
||
|
// Set up the blocks for two different threads in Profiles[1].
|
||
|
EXPECT_FALSE(errorToBool(Profiles[1].addBlock(Profile::Block{
|
||
|
Profile::ThreadID{1},
|
||
|
{{Profiles[1].internPath({2, 1}), Profile::Data{1, 1000}}}})));
|
||
|
EXPECT_FALSE(errorToBool(Profiles[1].addBlock(Profile::Block{
|
||
|
Profile::ThreadID{2},
|
||
|
{{Profiles[1].internPath({3, 1}), Profile::Data{1, 1000}},
|
||
|
{Profiles[1].internPath({4, 1}), Profile::Data{1, 1000}}}})));
|
||
|
|
||
|
Profile Merged = std::accumulate(Profiles.begin(), Profiles.end(), Profile(),
|
||
|
mergeProfilesByThread);
|
||
|
EXPECT_THAT(
|
||
|
Merged,
|
||
|
UnorderedElementsAre(
|
||
|
// We want to see two threads after the merge.
|
||
|
AllOf(Field(&Profile::Block::Thread, Eq(Profile::ThreadID{1})),
|
||
|
Field(&Profile::Block::PathData,
|
||
|
UnorderedElementsAre(
|
||
|
Pair(Merged.internPath({2, 1}),
|
||
|
AllOf(Field(&Profile::Data::CallCount, Eq(2u)),
|
||
|
Field(&Profile::Data::CumulativeLocalTime,
|
||
|
Eq(2000u)))),
|
||
|
Pair(Merged.internPath({4, 1}),
|
||
|
AllOf(Field(&Profile::Data::CallCount, Eq(1u)),
|
||
|
Field(&Profile::Data::CumulativeLocalTime,
|
||
|
Eq(1000u))))))),
|
||
|
AllOf(Field(&Profile::Block::Thread, Eq(Profile::ThreadID{2})),
|
||
|
Field(&Profile::Block::PathData,
|
||
|
UnorderedElementsAre(
|
||
|
Pair(Merged.internPath({3, 1}),
|
||
|
AllOf(Field(&Profile::Data::CallCount, Eq(2u)),
|
||
|
Field(&Profile::Data::CumulativeLocalTime,
|
||
|
Eq(2000u)))),
|
||
|
Pair(Merged.internPath({4, 1}),
|
||
|
AllOf(Field(&Profile::Data::CallCount, Eq(1u)),
|
||
|
Field(&Profile::Data::CumulativeLocalTime,
|
||
|
Eq(1000u)))))))));
|
||
|
}
|
||
|
// FIXME: Add a test creating a Trace and generating a Profile
|
||
|
// FIXME: Add tests for ranking/sorting profile blocks by dimension
|
||
|
|
||
|
} // namespace
|
||
|
} // namespace xray
|
||
|
} // namespace llvm
|