1 #include "../CtxInstrProfiling.h"
2 #include "gtest/gtest.h"
3 #include <thread>
4
5 using namespace __ctx_profile;
6
7 class ContextTest : public ::testing::Test {
SetUp()8 void SetUp() override { memset(&Root, 0, sizeof(ContextRoot)); }
TearDown()9 void TearDown() override { __llvm_ctx_profile_free(); }
10
11 public:
12 ContextRoot Root;
13 };
14
TEST(ArenaTest,ZeroInit)15 TEST(ArenaTest, ZeroInit) {
16 char Buffer[1024];
17 memset(Buffer, 1, 1024);
18 Arena *A = new (Buffer) Arena(10);
19 for (auto I = 0U; I < A->size(); ++I)
20 EXPECT_EQ(A->pos()[I], static_cast<char>(0));
21 EXPECT_EQ(A->size(), 10U);
22 }
23
TEST(ArenaTest,Basic)24 TEST(ArenaTest, Basic) {
25 Arena *A = Arena::allocateNewArena(1024);
26 EXPECT_EQ(A->size(), 1024U);
27 EXPECT_EQ(A->next(), nullptr);
28
29 auto *M1 = A->tryBumpAllocate(1020);
30 EXPECT_NE(M1, nullptr);
31 auto *M2 = A->tryBumpAllocate(4);
32 EXPECT_NE(M2, nullptr);
33 EXPECT_EQ(M1 + 1020, M2);
34 EXPECT_EQ(A->tryBumpAllocate(1), nullptr);
35 Arena *A2 = Arena::allocateNewArena(2024, A);
36 EXPECT_EQ(A->next(), A2);
37 EXPECT_EQ(A2->next(), nullptr);
38 Arena::freeArenaList(A);
39 EXPECT_EQ(A, nullptr);
40 }
41
TEST_F(ContextTest,Basic)42 TEST_F(ContextTest, Basic) {
43 auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4);
44 ASSERT_NE(Ctx, nullptr);
45 EXPECT_NE(Root.CurrentMem, nullptr);
46 EXPECT_EQ(Root.FirstMemBlock, Root.CurrentMem);
47 EXPECT_EQ(Ctx->size(), sizeof(ContextNode) + 10 * sizeof(uint64_t) +
48 4 * sizeof(ContextNode *));
49 EXPECT_EQ(Ctx->counters_size(), 10U);
50 EXPECT_EQ(Ctx->callsites_size(), 4U);
51 EXPECT_EQ(__llvm_ctx_profile_current_context_root, &Root);
52 Root.Taken.CheckLocked();
53 EXPECT_FALSE(Root.Taken.TryLock());
54 __llvm_ctx_profile_release_context(&Root);
55 EXPECT_EQ(__llvm_ctx_profile_current_context_root, nullptr);
56 EXPECT_TRUE(Root.Taken.TryLock());
57 Root.Taken.Unlock();
58 }
59
TEST_F(ContextTest,Callsite)60 TEST_F(ContextTest, Callsite) {
61 auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4);
62 int FakeCalleeAddress = 0;
63 const bool IsScratch = isScratch(Ctx);
64 EXPECT_FALSE(IsScratch);
65 // This is the sequence the caller performs - it's the lowering of the
66 // instrumentation of the callsite "2". "2" is arbitrary here.
67 __llvm_ctx_profile_expected_callee[0] = &FakeCalleeAddress;
68 __llvm_ctx_profile_callsite[0] = &Ctx->subContexts()[2];
69 // This is what the callee does
70 auto *Subctx = __llvm_ctx_profile_get_context(&FakeCalleeAddress, 2, 3, 1);
71 // We expect the subcontext to be appropriately placed and dimensioned
72 EXPECT_EQ(Ctx->subContexts()[2], Subctx);
73 EXPECT_EQ(Subctx->counters_size(), 3U);
74 EXPECT_EQ(Subctx->callsites_size(), 1U);
75 // We reset these in _get_context.
76 EXPECT_EQ(__llvm_ctx_profile_expected_callee[0], nullptr);
77 EXPECT_EQ(__llvm_ctx_profile_callsite[0], nullptr);
78
79 EXPECT_EQ(Subctx->size(), sizeof(ContextNode) + 3 * sizeof(uint64_t) +
80 1 * sizeof(ContextNode *));
81 __llvm_ctx_profile_release_context(&Root);
82 }
83
TEST_F(ContextTest,ScratchNoCollection)84 TEST_F(ContextTest, ScratchNoCollection) {
85 EXPECT_EQ(__llvm_ctx_profile_current_context_root, nullptr);
86 int FakeCalleeAddress = 0;
87 // this would be the very first function executing this. the TLS is empty,
88 // too.
89 auto *Ctx = __llvm_ctx_profile_get_context(&FakeCalleeAddress, 2, 3, 1);
90 // We never entered a context (_start_context was never called) - so the
91 // returned context must be scratch.
92 EXPECT_TRUE(isScratch(Ctx));
93 }
94
TEST_F(ContextTest,ScratchDuringCollection)95 TEST_F(ContextTest, ScratchDuringCollection) {
96 auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4);
97 int FakeCalleeAddress = 0;
98 int OtherFakeCalleeAddress = 0;
99 __llvm_ctx_profile_expected_callee[0] = &FakeCalleeAddress;
100 __llvm_ctx_profile_callsite[0] = &Ctx->subContexts()[2];
101 auto *Subctx =
102 __llvm_ctx_profile_get_context(&OtherFakeCalleeAddress, 2, 3, 1);
103 // We expected a different callee - so return scratch. It mimics what happens
104 // in the case of a signal handler - in this case, OtherFakeCalleeAddress is
105 // the signal handler.
106 EXPECT_TRUE(isScratch(Subctx));
107 EXPECT_EQ(__llvm_ctx_profile_expected_callee[0], nullptr);
108 EXPECT_EQ(__llvm_ctx_profile_callsite[0], nullptr);
109
110 int ThirdFakeCalleeAddress = 0;
111 __llvm_ctx_profile_expected_callee[1] = &ThirdFakeCalleeAddress;
112 __llvm_ctx_profile_callsite[1] = &Subctx->subContexts()[0];
113
114 auto *Subctx2 =
115 __llvm_ctx_profile_get_context(&ThirdFakeCalleeAddress, 3, 0, 0);
116 // We again expect scratch because the '0' position is where the runtime
117 // looks, so it doesn't matter the '1' position is populated correctly.
118 EXPECT_TRUE(isScratch(Subctx2));
119
120 __llvm_ctx_profile_expected_callee[0] = &ThirdFakeCalleeAddress;
121 __llvm_ctx_profile_callsite[0] = &Subctx->subContexts()[0];
122 auto *Subctx3 =
123 __llvm_ctx_profile_get_context(&ThirdFakeCalleeAddress, 3, 0, 0);
124 // We expect scratch here, too, because the value placed in
125 // __llvm_ctx_profile_callsite is scratch
126 EXPECT_TRUE(isScratch(Subctx3));
127
128 __llvm_ctx_profile_release_context(&Root);
129 }
130
TEST_F(ContextTest,NeedMoreMemory)131 TEST_F(ContextTest, NeedMoreMemory) {
132 auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4);
133 int FakeCalleeAddress = 0;
134 const bool IsScratch = isScratch(Ctx);
135 EXPECT_FALSE(IsScratch);
136 const auto *CurrentMem = Root.CurrentMem;
137 __llvm_ctx_profile_expected_callee[0] = &FakeCalleeAddress;
138 __llvm_ctx_profile_callsite[0] = &Ctx->subContexts()[2];
139 // Allocate a massive subcontext to force new arena allocation
140 auto *Subctx =
141 __llvm_ctx_profile_get_context(&FakeCalleeAddress, 3, 1 << 20, 1);
142 EXPECT_EQ(Ctx->subContexts()[2], Subctx);
143 EXPECT_NE(CurrentMem, Root.CurrentMem);
144 EXPECT_NE(Root.CurrentMem, nullptr);
145 }
146
TEST_F(ContextTest,ConcurrentRootCollection)147 TEST_F(ContextTest, ConcurrentRootCollection) {
148 std::atomic<int> NonScratch = 0;
149 std::atomic<int> Executions = 0;
150
151 __sanitizer::Semaphore GotCtx;
152
153 auto Entrypoint = [&]() {
154 ++Executions;
155 auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4);
156 GotCtx.Post();
157 const bool IS = isScratch(Ctx);
158 NonScratch += (!IS);
159 if (!IS) {
160 GotCtx.Wait();
161 GotCtx.Wait();
162 }
163 __llvm_ctx_profile_release_context(&Root);
164 };
165 std::thread T1(Entrypoint);
166 std::thread T2(Entrypoint);
167 T1.join();
168 T2.join();
169 EXPECT_EQ(NonScratch, 1);
170 EXPECT_EQ(Executions, 2);
171 }
172
TEST_F(ContextTest,Dump)173 TEST_F(ContextTest, Dump) {
174 auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4);
175 int FakeCalleeAddress = 0;
176 __llvm_ctx_profile_expected_callee[0] = &FakeCalleeAddress;
177 __llvm_ctx_profile_callsite[0] = &Ctx->subContexts()[2];
178 auto *Subctx = __llvm_ctx_profile_get_context(&FakeCalleeAddress, 2, 3, 1);
179 (void)Subctx;
180 __llvm_ctx_profile_release_context(&Root);
181
182 struct Writer {
183 ContextRoot *const Root;
184 const size_t Entries;
185 bool State = false;
186 Writer(ContextRoot *Root, size_t Entries) : Root(Root), Entries(Entries) {}
187
188 bool write(const ContextNode &Node) {
189 EXPECT_FALSE(Root->Taken.TryLock());
190 EXPECT_EQ(Node.guid(), 1U);
191 EXPECT_EQ(Node.counters()[0], Entries);
192 EXPECT_EQ(Node.counters_size(), 10U);
193 EXPECT_EQ(Node.callsites_size(), 4U);
194 EXPECT_EQ(Node.subContexts()[0], nullptr);
195 EXPECT_EQ(Node.subContexts()[1], nullptr);
196 EXPECT_NE(Node.subContexts()[2], nullptr);
197 EXPECT_EQ(Node.subContexts()[3], nullptr);
198 const auto &SN = *Node.subContexts()[2];
199 EXPECT_EQ(SN.guid(), 2U);
200 EXPECT_EQ(SN.counters()[0], Entries);
201 EXPECT_EQ(SN.counters_size(), 3U);
202 EXPECT_EQ(SN.callsites_size(), 1U);
203 EXPECT_EQ(SN.subContexts()[0], nullptr);
204 State = true;
205 return true;
206 }
207 };
208 Writer W(&Root, 1);
209 EXPECT_FALSE(W.State);
210 __llvm_ctx_profile_fetch(&W, [](void *W, const ContextNode &Node) -> bool {
211 return reinterpret_cast<Writer *>(W)->write(Node);
212 });
213 EXPECT_TRUE(W.State);
214
215 // this resets all counters but not the internal structure.
216 __llvm_ctx_profile_start_collection();
217 Writer W2(&Root, 0);
218 EXPECT_FALSE(W2.State);
219 __llvm_ctx_profile_fetch(&W2, [](void *W, const ContextNode &Node) -> bool {
220 return reinterpret_cast<Writer *>(W)->write(Node);
221 });
222 EXPECT_TRUE(W2.State);
223 }
224