xref: /freebsd/contrib/llvm-project/compiler-rt/lib/ctx_profile/tests/CtxInstrProfilingTest.cpp (revision b64c5a0ace59af62eff52bfe110a521dc73c937b)
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 {
8   void SetUp() override { memset(&Root, 0, sizeof(ContextRoot)); }
9   void TearDown() override { __llvm_ctx_profile_free(); }
10 
11 public:
12   ContextRoot Root;
13 };
14 
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 
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 
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 
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 
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 
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 
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 
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 
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