1*700637cbSDimitry Andric #include "../RootAutoDetector.h"
2*700637cbSDimitry Andric #include "sanitizer_common/sanitizer_array_ref.h"
3*700637cbSDimitry Andric #include "gmock/gmock.h"
4*700637cbSDimitry Andric #include "gtest/gtest.h"
5*700637cbSDimitry Andric
6*700637cbSDimitry Andric using namespace __ctx_profile;
7*700637cbSDimitry Andric using ::testing::IsEmpty;
8*700637cbSDimitry Andric using ::testing::Not;
9*700637cbSDimitry Andric using ::testing::SizeIs;
10*700637cbSDimitry Andric
11*700637cbSDimitry Andric // Utility for describing a preorder traversal. By default it captures the
12*700637cbSDimitry Andric // address and count at a callsite node. Implicitly nodes are expected to have 1
13*700637cbSDimitry Andric // child. If they have none, we place a Marker::term and if they have more than
14*700637cbSDimitry Andric // one, we place a Marker::split(nr_of_children) For example, using a list
15*700637cbSDimitry Andric // notation, and letters to denote a pair of address and count:
16*700637cbSDimitry Andric // (A (B C) (D (E F))) is a list of markers: A, split(2), B, term, C,
17*700637cbSDimitry Andric // term, D, split(2), E, term, F, term
18*700637cbSDimitry Andric class Marker {
19*700637cbSDimitry Andric enum class Kind { End, Value, Split };
20*700637cbSDimitry Andric const uptr Value;
21*700637cbSDimitry Andric const uptr Count;
22*700637cbSDimitry Andric const Kind K;
Marker(uptr V,uptr C,Kind S)23*700637cbSDimitry Andric Marker(uptr V, uptr C, Kind S) : Value(V), Count(C), K(S) {}
24*700637cbSDimitry Andric
25*700637cbSDimitry Andric public:
Marker(uptr V,uptr C)26*700637cbSDimitry Andric Marker(uptr V, uptr C) : Marker(V, C, Kind::Value) {}
27*700637cbSDimitry Andric
split(uptr V)28*700637cbSDimitry Andric static Marker split(uptr V) { return Marker(V, 0, Kind::Split); }
term()29*700637cbSDimitry Andric static Marker term() { return Marker(0, 0, Kind::End); }
30*700637cbSDimitry Andric
isSplit() const31*700637cbSDimitry Andric bool isSplit() const { return K == Kind::Split; }
isTerm() const32*700637cbSDimitry Andric bool isTerm() const { return K == Kind::End; }
isVal() const33*700637cbSDimitry Andric bool isVal() const { return K == Kind::Value; }
34*700637cbSDimitry Andric
operator ==(const Marker & M) const35*700637cbSDimitry Andric bool operator==(const Marker &M) const {
36*700637cbSDimitry Andric return Value == M.Value && Count == M.Count && K == M.K;
37*700637cbSDimitry Andric }
38*700637cbSDimitry Andric };
39*700637cbSDimitry Andric
40*700637cbSDimitry Andric class MockCallsiteTrie final : public PerThreadCallsiteTrie {
41*700637cbSDimitry Andric // Return the first multiple of 100.
getFctStartAddr(uptr CallsiteAddress) const42*700637cbSDimitry Andric uptr getFctStartAddr(uptr CallsiteAddress) const override {
43*700637cbSDimitry Andric return (CallsiteAddress / 100) * 100;
44*700637cbSDimitry Andric }
45*700637cbSDimitry Andric
popAndCheck(ArrayRef<Marker> & Preorder,Marker M)46*700637cbSDimitry Andric static void popAndCheck(ArrayRef<Marker> &Preorder, Marker M) {
47*700637cbSDimitry Andric ASSERT_THAT(Preorder, Not(IsEmpty()));
48*700637cbSDimitry Andric ASSERT_EQ(Preorder[0], M);
49*700637cbSDimitry Andric Preorder = Preorder.drop_front();
50*700637cbSDimitry Andric }
51*700637cbSDimitry Andric
checkSameImpl(const Trie & T,ArrayRef<Marker> & Preorder)52*700637cbSDimitry Andric static void checkSameImpl(const Trie &T, ArrayRef<Marker> &Preorder) {
53*700637cbSDimitry Andric popAndCheck(Preorder, {T.CallsiteAddress, T.Count});
54*700637cbSDimitry Andric
55*700637cbSDimitry Andric if (T.Children.empty()) {
56*700637cbSDimitry Andric popAndCheck(Preorder, Marker::term());
57*700637cbSDimitry Andric return;
58*700637cbSDimitry Andric }
59*700637cbSDimitry Andric
60*700637cbSDimitry Andric if (T.Children.size() > 1)
61*700637cbSDimitry Andric popAndCheck(Preorder, Marker::split(T.Children.size()));
62*700637cbSDimitry Andric
63*700637cbSDimitry Andric T.Children.forEach([&](const auto &KVP) {
64*700637cbSDimitry Andric checkSameImpl(KVP.second, Preorder);
65*700637cbSDimitry Andric return true;
66*700637cbSDimitry Andric });
67*700637cbSDimitry Andric }
68*700637cbSDimitry Andric
69*700637cbSDimitry Andric public:
checkSame(ArrayRef<Marker> Preorder) const70*700637cbSDimitry Andric void checkSame(ArrayRef<Marker> Preorder) const {
71*700637cbSDimitry Andric checkSameImpl(TheTrie, Preorder);
72*700637cbSDimitry Andric ASSERT_THAT(Preorder, IsEmpty());
73*700637cbSDimitry Andric }
74*700637cbSDimitry Andric };
75*700637cbSDimitry Andric
TEST(PerThreadCallsiteTrieTest,Insert)76*700637cbSDimitry Andric TEST(PerThreadCallsiteTrieTest, Insert) {
77*700637cbSDimitry Andric MockCallsiteTrie R;
78*700637cbSDimitry Andric uptr Stack1[]{4, 3, 2, 1};
79*700637cbSDimitry Andric R.insertStack(StackTrace(Stack1, 4));
80*700637cbSDimitry Andric R.checkSame(ArrayRef<Marker>(
81*700637cbSDimitry Andric {{0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}, Marker::term()}));
82*700637cbSDimitry Andric
83*700637cbSDimitry Andric uptr Stack2[]{5, 4, 3, 2, 1};
84*700637cbSDimitry Andric R.insertStack(StackTrace(Stack2, 5));
85*700637cbSDimitry Andric R.checkSame(ArrayRef<Marker>(
86*700637cbSDimitry Andric {{0, 2}, {1, 2}, {2, 2}, {3, 2}, {4, 2}, {5, 1}, Marker::term()}));
87*700637cbSDimitry Andric
88*700637cbSDimitry Andric uptr Stack3[]{6, 3, 2, 1};
89*700637cbSDimitry Andric R.insertStack(StackTrace(Stack3, 4));
90*700637cbSDimitry Andric R.checkSame(ArrayRef<Marker>({{0, 3},
91*700637cbSDimitry Andric {1, 3},
92*700637cbSDimitry Andric {2, 3},
93*700637cbSDimitry Andric {3, 3},
94*700637cbSDimitry Andric Marker::split(2),
95*700637cbSDimitry Andric {4, 2},
96*700637cbSDimitry Andric {5, 1},
97*700637cbSDimitry Andric Marker::term(),
98*700637cbSDimitry Andric {6, 1},
99*700637cbSDimitry Andric Marker::term()}));
100*700637cbSDimitry Andric uptr Stack4[]{7, 2, 1};
101*700637cbSDimitry Andric R.insertStack(StackTrace(Stack4, 3));
102*700637cbSDimitry Andric R.checkSame(ArrayRef<Marker>({{0, 4},
103*700637cbSDimitry Andric {1, 4},
104*700637cbSDimitry Andric {2, 4},
105*700637cbSDimitry Andric Marker::split(2),
106*700637cbSDimitry Andric {7, 1},
107*700637cbSDimitry Andric Marker::term(),
108*700637cbSDimitry Andric {3, 3},
109*700637cbSDimitry Andric Marker::split(2),
110*700637cbSDimitry Andric {4, 2},
111*700637cbSDimitry Andric {5, 1},
112*700637cbSDimitry Andric Marker::term(),
113*700637cbSDimitry Andric {6, 1},
114*700637cbSDimitry Andric Marker::term()}));
115*700637cbSDimitry Andric }
116*700637cbSDimitry Andric
TEST(PerThreadCallsiteTrieTest,DetectRoots)117*700637cbSDimitry Andric TEST(PerThreadCallsiteTrieTest, DetectRoots) {
118*700637cbSDimitry Andric MockCallsiteTrie T;
119*700637cbSDimitry Andric
120*700637cbSDimitry Andric uptr Stack1[]{501, 302, 202, 102};
121*700637cbSDimitry Andric uptr Stack2[]{601, 402, 203, 102};
122*700637cbSDimitry Andric T.insertStack({Stack1, 4});
123*700637cbSDimitry Andric T.insertStack({Stack2, 4});
124*700637cbSDimitry Andric
125*700637cbSDimitry Andric auto R = T.determineRoots();
126*700637cbSDimitry Andric EXPECT_THAT(R, SizeIs(2U));
127*700637cbSDimitry Andric EXPECT_TRUE(R.contains(300));
128*700637cbSDimitry Andric EXPECT_TRUE(R.contains(400));
129*700637cbSDimitry Andric }
130*700637cbSDimitry Andric
TEST(PerThreadCallsiteTrieTest,DetectRootsNoBranches)131*700637cbSDimitry Andric TEST(PerThreadCallsiteTrieTest, DetectRootsNoBranches) {
132*700637cbSDimitry Andric MockCallsiteTrie T;
133*700637cbSDimitry Andric
134*700637cbSDimitry Andric uptr Stack1[]{501, 302, 202, 102};
135*700637cbSDimitry Andric T.insertStack({Stack1, 4});
136*700637cbSDimitry Andric
137*700637cbSDimitry Andric auto R = T.determineRoots();
138*700637cbSDimitry Andric EXPECT_THAT(R, IsEmpty());
139*700637cbSDimitry Andric }
140*700637cbSDimitry Andric
TEST(PerThreadCallsiteTrieTest,DetectRootsUnknownFct)141*700637cbSDimitry Andric TEST(PerThreadCallsiteTrieTest, DetectRootsUnknownFct) {
142*700637cbSDimitry Andric MockCallsiteTrie T;
143*700637cbSDimitry Andric
144*700637cbSDimitry Andric uptr Stack1[]{501, 302, 202, 102};
145*700637cbSDimitry Andric // The MockCallsiteTree address resolver resolves addresses over 100, so 40
146*700637cbSDimitry Andric // will be mapped to 0.
147*700637cbSDimitry Andric uptr Stack2[]{601, 40, 203, 102};
148*700637cbSDimitry Andric T.insertStack({Stack1, 4});
149*700637cbSDimitry Andric T.insertStack({Stack2, 4});
150*700637cbSDimitry Andric
151*700637cbSDimitry Andric auto R = T.determineRoots();
152*700637cbSDimitry Andric ASSERT_THAT(R, SizeIs(2U));
153*700637cbSDimitry Andric EXPECT_TRUE(R.contains(300));
154*700637cbSDimitry Andric EXPECT_TRUE(R.contains(0));
155*700637cbSDimitry Andric }
156