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