diff options
author | Nico Weber <nicolasweber@gmx.de> | 2019-08-01 12:35:27 +0000 |
---|---|---|
committer | Nico Weber <nicolasweber@gmx.de> | 2019-08-01 12:35:27 +0000 |
commit | bc0d697db9e3552cebe21c3f1832c14762bd6ebd (patch) | |
tree | 4d5710c2bc717d594ccc60cc8ccfaba999543997 /compiler-rt/lib/xray/tests/unit/function_call_trie_test.cc | |
parent | caf03a41f3b876ef1ddce78c15a77dcb9ecdc125 (diff) | |
download | bcm5719-llvm-bc0d697db9e3552cebe21c3f1832c14762bd6ebd.tar.gz bcm5719-llvm-bc0d697db9e3552cebe21c3f1832c14762bd6ebd.zip |
compiler-rt: Rename .cc file in lib/xray/tests/unit to .cpp
Like r367463, but for xray/texts/unit.
llvm-svn: 367550
Diffstat (limited to 'compiler-rt/lib/xray/tests/unit/function_call_trie_test.cc')
-rw-r--r-- | compiler-rt/lib/xray/tests/unit/function_call_trie_test.cc | 343 |
1 files changed, 0 insertions, 343 deletions
diff --git a/compiler-rt/lib/xray/tests/unit/function_call_trie_test.cc b/compiler-rt/lib/xray/tests/unit/function_call_trie_test.cc deleted file mode 100644 index 6d8df9ae362..00000000000 --- a/compiler-rt/lib/xray/tests/unit/function_call_trie_test.cc +++ /dev/null @@ -1,343 +0,0 @@ -//===-- function_call_trie_test.cc ----------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// This file is a part of XRay, a function call tracing system. -// -//===----------------------------------------------------------------------===// -#include "xray_function_call_trie.h" -#include "gtest/gtest.h" -#include <cstdint> - -namespace __xray { - -namespace { - -TEST(FunctionCallTrieTest, ConstructWithTLSAllocators) { - profilingFlags()->setDefaults(); - FunctionCallTrie::Allocators Allocators = FunctionCallTrie::InitAllocators(); - FunctionCallTrie Trie(Allocators); -} - -TEST(FunctionCallTrieTest, EnterAndExitFunction) { - profilingFlags()->setDefaults(); - auto A = FunctionCallTrie::InitAllocators(); - FunctionCallTrie Trie(A); - - uint64_t TSC = 1; - uint16_t CPU = 0; - Trie.enterFunction(1, TSC++, CPU++); - Trie.exitFunction(1, TSC++, CPU++); - const auto &R = Trie.getRoots(); - - ASSERT_EQ(R.size(), 1u); - ASSERT_EQ(R.front()->FId, 1); - ASSERT_EQ(R.front()->CallCount, 1u); - ASSERT_EQ(R.front()->CumulativeLocalTime, 1u); -} - -TEST(FunctionCallTrieTest, HandleTSCOverflow) { - profilingFlags()->setDefaults(); - auto A = FunctionCallTrie::InitAllocators(); - FunctionCallTrie Trie(A); - - Trie.enterFunction(1, std::numeric_limits<uint64_t>::max(), 0); - Trie.exitFunction(1, 1, 0); - const auto &R = Trie.getRoots(); - - ASSERT_EQ(R.size(), 1u); - ASSERT_EQ(R.front()->FId, 1); - ASSERT_EQ(R.front()->CallCount, 1u); - ASSERT_EQ(R.front()->CumulativeLocalTime, 1u); -} - -TEST(FunctionCallTrieTest, MaximalCumulativeTime) { - profilingFlags()->setDefaults(); - auto A = FunctionCallTrie::InitAllocators(); - FunctionCallTrie Trie(A); - - Trie.enterFunction(1, 1, 0); - Trie.exitFunction(1, 0, 0); - const auto &R = Trie.getRoots(); - - ASSERT_EQ(R.size(), 1u); - ASSERT_EQ(R.front()->FId, 1); - ASSERT_EQ(R.front()->CallCount, 1u); - ASSERT_EQ(R.front()->CumulativeLocalTime, - std::numeric_limits<uint64_t>::max() - 1); -} - -TEST(FunctionCallTrieTest, MissingFunctionEntry) { - profilingFlags()->setDefaults(); - auto A = FunctionCallTrie::InitAllocators(); - FunctionCallTrie Trie(A); - Trie.exitFunction(1, 1, 0); - const auto &R = Trie.getRoots(); - - ASSERT_TRUE(R.empty()); -} - -TEST(FunctionCallTrieTest, NoMatchingEntersForExit) { - profilingFlags()->setDefaults(); - auto A = FunctionCallTrie::InitAllocators(); - FunctionCallTrie Trie(A); - Trie.enterFunction(2, 1, 0); - Trie.enterFunction(3, 3, 0); - Trie.exitFunction(1, 5, 0); - const auto &R = Trie.getRoots(); - - ASSERT_FALSE(R.empty()); - EXPECT_EQ(R.size(), size_t{1}); -} - -TEST(FunctionCallTrieTest, MissingFunctionExit) { - profilingFlags()->setDefaults(); - auto A = FunctionCallTrie::InitAllocators(); - FunctionCallTrie Trie(A); - Trie.enterFunction(1, 1, 0); - const auto &R = Trie.getRoots(); - - ASSERT_FALSE(R.empty()); - EXPECT_EQ(R.size(), size_t{1}); -} - -TEST(FunctionCallTrieTest, MultipleRoots) { - profilingFlags()->setDefaults(); - auto A = FunctionCallTrie::InitAllocators(); - FunctionCallTrie Trie(A); - - // Enter and exit FId = 1. - Trie.enterFunction(1, 1, 0); - Trie.exitFunction(1, 2, 0); - - // Enter and exit FId = 2. - Trie.enterFunction(2, 3, 0); - Trie.exitFunction(2, 4, 0); - - const auto &R = Trie.getRoots(); - ASSERT_FALSE(R.empty()); - ASSERT_EQ(R.size(), 2u); - - // Make sure the roots have different IDs. - const auto R0 = R[0]; - const auto R1 = R[1]; - ASSERT_NE(R0->FId, R1->FId); - - // Inspect the roots that they have the right data. - ASSERT_NE(R0, nullptr); - EXPECT_EQ(R0->CallCount, 1u); - EXPECT_EQ(R0->CumulativeLocalTime, 1u); - - ASSERT_NE(R1, nullptr); - EXPECT_EQ(R1->CallCount, 1u); - EXPECT_EQ(R1->CumulativeLocalTime, 1u); -} - -// While missing an intermediary entry may be rare in practice, we still enforce -// that we can handle the case where we've missed the entry event somehow, in -// between call entry/exits. To illustrate, imagine the following shadow call -// stack: -// -// f0@t0 -> f1@t1 -> f2@t2 -// -// If for whatever reason we see an exit for `f2` @ t3, followed by an exit for -// `f0` @ t4 (i.e. no `f1` exit in between) then we need to handle the case of -// accounting local time to `f2` from d = (t3 - t2), then local time to `f1` -// as d' = (t3 - t1) - d, and then local time to `f0` as d'' = (t3 - t0) - d'. -TEST(FunctionCallTrieTest, MissingIntermediaryExit) { - profilingFlags()->setDefaults(); - auto A = FunctionCallTrie::InitAllocators(); - FunctionCallTrie Trie(A); - - Trie.enterFunction(1, 0, 0); - Trie.enterFunction(2, 100, 0); - Trie.enterFunction(3, 200, 0); - Trie.exitFunction(3, 300, 0); - Trie.exitFunction(1, 400, 0); - - // What we should see at this point is all the functions in the trie in a - // specific order (1 -> 2 -> 3) with the appropriate count(s) and local - // latencies. - const auto &R = Trie.getRoots(); - ASSERT_FALSE(R.empty()); - ASSERT_EQ(R.size(), 1u); - - const auto &F1 = *R[0]; - ASSERT_EQ(F1.FId, 1); - ASSERT_FALSE(F1.Callees.empty()); - - const auto &F2 = *F1.Callees[0].NodePtr; - ASSERT_EQ(F2.FId, 2); - ASSERT_FALSE(F2.Callees.empty()); - - const auto &F3 = *F2.Callees[0].NodePtr; - ASSERT_EQ(F3.FId, 3); - ASSERT_TRUE(F3.Callees.empty()); - - // Now that we've established the preconditions, we check for specific aspects - // of the nodes. - EXPECT_EQ(F3.CallCount, 1u); - EXPECT_EQ(F2.CallCount, 1u); - EXPECT_EQ(F1.CallCount, 1u); - EXPECT_EQ(F3.CumulativeLocalTime, 100u); - EXPECT_EQ(F2.CumulativeLocalTime, 300u); - EXPECT_EQ(F1.CumulativeLocalTime, 100u); -} - -TEST(FunctionCallTrieTest, DeepCallStack) { - // Simulate a relatively deep call stack (32 levels) and ensure that we can - // properly pop all the way up the stack. - profilingFlags()->setDefaults(); - auto A = FunctionCallTrie::InitAllocators(); - FunctionCallTrie Trie(A); - for (int i = 0; i < 32; ++i) - Trie.enterFunction(i + 1, i, 0); - Trie.exitFunction(1, 33, 0); - - // Here, validate that we have a 32-level deep function call path from the - // root (1) down to the leaf (33). - const auto &R = Trie.getRoots(); - ASSERT_EQ(R.size(), 1u); - auto F = R[0]; - for (int i = 0; i < 32; ++i) { - EXPECT_EQ(F->FId, i + 1); - EXPECT_EQ(F->CallCount, 1u); - if (F->Callees.empty() && i != 31) - FAIL() << "Empty callees for FId " << F->FId; - if (i != 31) - F = F->Callees[0].NodePtr; - } -} - -// TODO: Test that we can handle cross-CPU migrations, where TSCs are not -// guaranteed to be synchronised. -TEST(FunctionCallTrieTest, DeepCopy) { - profilingFlags()->setDefaults(); - auto A = FunctionCallTrie::InitAllocators(); - FunctionCallTrie Trie(A); - - Trie.enterFunction(1, 0, 0); - Trie.enterFunction(2, 1, 0); - Trie.exitFunction(2, 2, 0); - Trie.enterFunction(3, 3, 0); - Trie.exitFunction(3, 4, 0); - Trie.exitFunction(1, 5, 0); - - // We want to make a deep copy and compare notes. - auto B = FunctionCallTrie::InitAllocators(); - FunctionCallTrie Copy(B); - Trie.deepCopyInto(Copy); - - ASSERT_NE(Trie.getRoots().size(), 0u); - ASSERT_EQ(Trie.getRoots().size(), Copy.getRoots().size()); - const auto &R0Orig = *Trie.getRoots()[0]; - const auto &R0Copy = *Copy.getRoots()[0]; - EXPECT_EQ(R0Orig.FId, 1); - EXPECT_EQ(R0Orig.FId, R0Copy.FId); - - ASSERT_EQ(R0Orig.Callees.size(), 2u); - ASSERT_EQ(R0Copy.Callees.size(), 2u); - - const auto &F1Orig = - *R0Orig.Callees - .find_element( - [](const FunctionCallTrie::NodeIdPair &R) { return R.FId == 2; }) - ->NodePtr; - const auto &F1Copy = - *R0Copy.Callees - .find_element( - [](const FunctionCallTrie::NodeIdPair &R) { return R.FId == 2; }) - ->NodePtr; - EXPECT_EQ(&R0Orig, F1Orig.Parent); - EXPECT_EQ(&R0Copy, F1Copy.Parent); -} - -TEST(FunctionCallTrieTest, MergeInto) { - profilingFlags()->setDefaults(); - auto A = FunctionCallTrie::InitAllocators(); - FunctionCallTrie T0(A); - FunctionCallTrie T1(A); - - // 1 -> 2 -> 3 - T0.enterFunction(1, 0, 0); - T0.enterFunction(2, 1, 0); - T0.enterFunction(3, 2, 0); - T0.exitFunction(3, 3, 0); - T0.exitFunction(2, 4, 0); - T0.exitFunction(1, 5, 0); - - // 1 -> 2 -> 3 - T1.enterFunction(1, 0, 0); - T1.enterFunction(2, 1, 0); - T1.enterFunction(3, 2, 0); - T1.exitFunction(3, 3, 0); - T1.exitFunction(2, 4, 0); - T1.exitFunction(1, 5, 0); - - // We use a different allocator here to make sure that we're able to transfer - // data into a FunctionCallTrie which uses a different allocator. This - // reflects the inteded usage scenario for when we're collecting profiles that - // aggregate across threads. - auto B = FunctionCallTrie::InitAllocators(); - FunctionCallTrie Merged(B); - - T0.mergeInto(Merged); - T1.mergeInto(Merged); - - ASSERT_EQ(Merged.getRoots().size(), 1u); - const auto &R0 = *Merged.getRoots()[0]; - EXPECT_EQ(R0.FId, 1); - EXPECT_EQ(R0.CallCount, 2u); - EXPECT_EQ(R0.CumulativeLocalTime, 10u); - EXPECT_EQ(R0.Callees.size(), 1u); - - const auto &F1 = *R0.Callees[0].NodePtr; - EXPECT_EQ(F1.FId, 2); - EXPECT_EQ(F1.CallCount, 2u); - EXPECT_EQ(F1.CumulativeLocalTime, 6u); - EXPECT_EQ(F1.Callees.size(), 1u); - - const auto &F2 = *F1.Callees[0].NodePtr; - EXPECT_EQ(F2.FId, 3); - EXPECT_EQ(F2.CallCount, 2u); - EXPECT_EQ(F2.CumulativeLocalTime, 2u); - EXPECT_EQ(F2.Callees.size(), 0u); -} - -TEST(FunctionCallTrieTest, PlacementNewOnAlignedStorage) { - profilingFlags()->setDefaults(); - typename std::aligned_storage<sizeof(FunctionCallTrie::Allocators), - alignof(FunctionCallTrie::Allocators)>::type - AllocatorsStorage; - new (&AllocatorsStorage) - FunctionCallTrie::Allocators(FunctionCallTrie::InitAllocators()); - auto *A = - reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorsStorage); - - typename std::aligned_storage<sizeof(FunctionCallTrie), - alignof(FunctionCallTrie)>::type FCTStorage; - new (&FCTStorage) FunctionCallTrie(*A); - auto *T = reinterpret_cast<FunctionCallTrie *>(&FCTStorage); - - // Put some data into it. - T->enterFunction(1, 0, 0); - T->exitFunction(1, 1, 0); - - // Re-initialize the objects in storage. - T->~FunctionCallTrie(); - A->~Allocators(); - new (A) FunctionCallTrie::Allocators(FunctionCallTrie::InitAllocators()); - new (T) FunctionCallTrie(*A); - - // Then put some data into it again. - T->enterFunction(1, 0, 0); - T->exitFunction(1, 1, 0); -} - -} // namespace - -} // namespace __xray |