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