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