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 { Root.getOrAllocateContextRoot(); } 9 void TearDown() override { __llvm_ctx_profile_free(); } 10 11 public: 12 FunctionData 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 __llvm_ctx_profile_start_collection(); 44 auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4); 45 ASSERT_NE(Ctx, nullptr); 46 auto &CtxRoot = *Root.CtxRoot; 47 EXPECT_NE(CtxRoot.CurrentMem, nullptr); 48 EXPECT_EQ(CtxRoot.FirstMemBlock, CtxRoot.CurrentMem); 49 EXPECT_EQ(Ctx->size(), sizeof(ContextNode) + 10 * sizeof(uint64_t) + 50 4 * sizeof(ContextNode *)); 51 EXPECT_EQ(Ctx->counters_size(), 10U); 52 EXPECT_EQ(Ctx->callsites_size(), 4U); 53 EXPECT_EQ(__llvm_ctx_profile_current_context_root, &CtxRoot); 54 CtxRoot.Taken.CheckLocked(); 55 EXPECT_FALSE(CtxRoot.Taken.TryLock()); 56 __llvm_ctx_profile_release_context(&Root); 57 EXPECT_EQ(__llvm_ctx_profile_current_context_root, nullptr); 58 EXPECT_TRUE(CtxRoot.Taken.TryLock()); 59 CtxRoot.Taken.Unlock(); 60 } 61 62 TEST_F(ContextTest, Callsite) { 63 __llvm_ctx_profile_start_collection(); 64 auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4); 65 int FakeCalleeAddress = 0; 66 const bool IsScratch = isScratch(Ctx); 67 EXPECT_FALSE(IsScratch); 68 // This is the sequence the caller performs - it's the lowering of the 69 // instrumentation of the callsite "2". "2" is arbitrary here. 70 __llvm_ctx_profile_expected_callee[0] = &FakeCalleeAddress; 71 __llvm_ctx_profile_callsite[0] = &Ctx->subContexts()[2]; 72 // This is what the callee does 73 FunctionData FData; 74 auto *Subctx = 75 __llvm_ctx_profile_get_context(&FData, &FakeCalleeAddress, 2, 3, 1); 76 // This should not have required creating a flat context. 77 EXPECT_EQ(FData.FlatCtx, nullptr); 78 // We expect the subcontext to be appropriately placed and dimensioned 79 EXPECT_EQ(Ctx->subContexts()[2], Subctx); 80 EXPECT_EQ(Subctx->counters_size(), 3U); 81 EXPECT_EQ(Subctx->callsites_size(), 1U); 82 // We reset these in _get_context. 83 EXPECT_EQ(__llvm_ctx_profile_expected_callee[0], nullptr); 84 EXPECT_EQ(__llvm_ctx_profile_callsite[0], nullptr); 85 86 EXPECT_EQ(Subctx->size(), sizeof(ContextNode) + 3 * sizeof(uint64_t) + 87 1 * sizeof(ContextNode *)); 88 __llvm_ctx_profile_release_context(&Root); 89 } 90 91 TEST_F(ContextTest, ScratchNoCollectionProfilingNotStarted) { 92 // This test intentionally does not call __llvm_ctx_profile_start_collection. 93 EXPECT_EQ(__llvm_ctx_profile_current_context_root, nullptr); 94 int FakeCalleeAddress = 0; 95 // this would be the very first function executing this. the TLS is empty, 96 // too. 97 FunctionData FData; 98 auto *Ctx = 99 __llvm_ctx_profile_get_context(&FData, &FakeCalleeAddress, 2, 3, 1); 100 // We never entered a context (_start_context was never called) - so the 101 // returned context must be a tagged pointer. 102 EXPECT_TRUE(isScratch(Ctx)); 103 // Because we didn't start collection, no flat profile should have been 104 // allocated. 105 EXPECT_EQ(FData.FlatCtx, nullptr); 106 } 107 108 TEST_F(ContextTest, ScratchNoCollectionProfilingStarted) { 109 ASSERT_EQ(__llvm_ctx_profile_current_context_root, nullptr); 110 int FakeCalleeAddress = 0; 111 // Start collection, so the function gets a flat profile instead of scratch. 112 __llvm_ctx_profile_start_collection(); 113 // this would be the very first function executing this. the TLS is empty, 114 // too. 115 FunctionData FData; 116 auto *Ctx = 117 __llvm_ctx_profile_get_context(&FData, &FakeCalleeAddress, 2, 3, 1); 118 // We never entered a context (_start_context was never called) - so the 119 // returned context must be a tagged pointer. 120 EXPECT_TRUE(isScratch(Ctx)); 121 // Because we never entered a context, we should have allocated a flat context 122 EXPECT_NE(FData.FlatCtx, nullptr); 123 EXPECT_EQ(reinterpret_cast<uintptr_t>(FData.FlatCtx) + 1, 124 reinterpret_cast<uintptr_t>(Ctx)); 125 } 126 127 TEST_F(ContextTest, ScratchDuringCollection) { 128 __llvm_ctx_profile_start_collection(); 129 auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4); 130 int FakeCalleeAddress = 0; 131 int OtherFakeCalleeAddress = 0; 132 __llvm_ctx_profile_expected_callee[0] = &FakeCalleeAddress; 133 __llvm_ctx_profile_callsite[0] = &Ctx->subContexts()[2]; 134 FunctionData FData[3]; 135 auto *Subctx = __llvm_ctx_profile_get_context( 136 &FData[0], &OtherFakeCalleeAddress, 2, 3, 1); 137 // We expected a different callee - so return scratch. It mimics what happens 138 // in the case of a signal handler - in this case, OtherFakeCalleeAddress is 139 // the signal handler. 140 EXPECT_TRUE(isScratch(Subctx)); 141 // We shouldn't have tried to return a flat context because we're under a 142 // root. 143 EXPECT_EQ(FData[0].FlatCtx, nullptr); 144 EXPECT_EQ(__llvm_ctx_profile_expected_callee[0], nullptr); 145 EXPECT_EQ(__llvm_ctx_profile_callsite[0], nullptr); 146 147 int ThirdFakeCalleeAddress = 0; 148 __llvm_ctx_profile_expected_callee[1] = &ThirdFakeCalleeAddress; 149 __llvm_ctx_profile_callsite[1] = &Subctx->subContexts()[0]; 150 151 auto *Subctx2 = __llvm_ctx_profile_get_context( 152 &FData[1], &ThirdFakeCalleeAddress, 3, 0, 0); 153 // We again expect scratch because the '0' position is where the runtime 154 // looks, so it doesn't matter the '1' position is populated correctly. 155 EXPECT_TRUE(isScratch(Subctx2)); 156 EXPECT_EQ(FData[1].FlatCtx, nullptr); 157 158 __llvm_ctx_profile_expected_callee[0] = &ThirdFakeCalleeAddress; 159 __llvm_ctx_profile_callsite[0] = &Subctx->subContexts()[0]; 160 auto *Subctx3 = __llvm_ctx_profile_get_context( 161 &FData[2], &ThirdFakeCalleeAddress, 3, 0, 0); 162 // We expect scratch here, too, because the value placed in 163 // __llvm_ctx_profile_callsite is scratch 164 EXPECT_TRUE(isScratch(Subctx3)); 165 EXPECT_EQ(FData[2].FlatCtx, nullptr); 166 167 __llvm_ctx_profile_release_context(&Root); 168 } 169 170 TEST_F(ContextTest, NeedMoreMemory) { 171 __llvm_ctx_profile_start_collection(); 172 auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4); 173 int FakeCalleeAddress = 0; 174 const bool IsScratch = isScratch(Ctx); 175 EXPECT_FALSE(IsScratch); 176 auto &CtxRoot = *Root.CtxRoot; 177 const auto *CurrentMem = CtxRoot.CurrentMem; 178 __llvm_ctx_profile_expected_callee[0] = &FakeCalleeAddress; 179 __llvm_ctx_profile_callsite[0] = &Ctx->subContexts()[2]; 180 FunctionData FData; 181 // Allocate a massive subcontext to force new arena allocation 182 auto *Subctx = 183 __llvm_ctx_profile_get_context(&FData, &FakeCalleeAddress, 3, 1 << 20, 1); 184 EXPECT_EQ(FData.FlatCtx, nullptr); 185 EXPECT_EQ(Ctx->subContexts()[2], Subctx); 186 EXPECT_NE(CurrentMem, CtxRoot.CurrentMem); 187 EXPECT_NE(CtxRoot.CurrentMem, nullptr); 188 } 189 190 TEST_F(ContextTest, ConcurrentRootCollection) { 191 std::atomic<int> NonScratch = 0; 192 std::atomic<int> Executions = 0; 193 194 __sanitizer::Semaphore GotCtx; 195 196 auto Entrypoint = [&]() { 197 ++Executions; 198 auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4); 199 GotCtx.Post(); 200 const bool IS = isScratch(Ctx); 201 NonScratch += (!IS); 202 if (!IS) { 203 GotCtx.Wait(); 204 GotCtx.Wait(); 205 } 206 __llvm_ctx_profile_release_context(&Root); 207 }; 208 std::thread T1(Entrypoint); 209 std::thread T2(Entrypoint); 210 T1.join(); 211 T2.join(); 212 EXPECT_EQ(NonScratch, 1); 213 EXPECT_EQ(Executions, 2); 214 } 215 216 TEST_F(ContextTest, Dump) { 217 auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4); 218 int FakeCalleeAddress = 0; 219 __llvm_ctx_profile_expected_callee[0] = &FakeCalleeAddress; 220 __llvm_ctx_profile_callsite[0] = &Ctx->subContexts()[2]; 221 FunctionData FData; 222 auto *Subctx = 223 __llvm_ctx_profile_get_context(&FData, &FakeCalleeAddress, 2, 3, 1); 224 (void)Subctx; 225 __llvm_ctx_profile_release_context(&Root); 226 227 class TestProfileWriter : public ProfileWriter { 228 public: 229 ContextRoot *const Root; 230 const size_t Entries; 231 232 int EnteredSectionCount = 0; 233 int ExitedSectionCount = 0; 234 int EnteredFlatCount = 0; 235 int ExitedFlatCount = 0; 236 int FlatsWritten = 0; 237 238 bool State = false; 239 240 TestProfileWriter(ContextRoot *Root, size_t Entries) 241 : Root(Root), Entries(Entries) {} 242 243 void writeContextual(const ContextNode &Node, const ContextNode *Unhandled, 244 uint64_t TotalRootEntryCount) override { 245 EXPECT_EQ(TotalRootEntryCount, Entries); 246 EXPECT_EQ(EnteredSectionCount, 1); 247 EXPECT_EQ(ExitedSectionCount, 0); 248 EXPECT_FALSE(Root->Taken.TryLock()); 249 EXPECT_EQ(Node.guid(), 1U); 250 EXPECT_EQ(Node.counters()[0], Entries); 251 EXPECT_EQ(Node.counters_size(), 10U); 252 EXPECT_EQ(Node.callsites_size(), 4U); 253 EXPECT_EQ(Node.subContexts()[0], nullptr); 254 EXPECT_EQ(Node.subContexts()[1], nullptr); 255 EXPECT_NE(Node.subContexts()[2], nullptr); 256 EXPECT_EQ(Node.subContexts()[3], nullptr); 257 const auto &SN = *Node.subContexts()[2]; 258 EXPECT_EQ(SN.guid(), 2U); 259 EXPECT_EQ(SN.counters()[0], Entries); 260 EXPECT_EQ(SN.counters_size(), 3U); 261 EXPECT_EQ(SN.callsites_size(), 1U); 262 EXPECT_EQ(SN.subContexts()[0], nullptr); 263 State = true; 264 } 265 void startContextSection() override { ++EnteredSectionCount; } 266 void endContextSection() override { 267 EXPECT_EQ(EnteredSectionCount, 1); 268 ++ExitedSectionCount; 269 } 270 void startFlatSection() override { ++EnteredFlatCount; } 271 void writeFlat(GUID Guid, const uint64_t *Buffer, 272 size_t BufferSize) override { 273 ++FlatsWritten; 274 EXPECT_EQ(BufferSize, 3U); 275 EXPECT_EQ(Buffer[0], 15U); 276 EXPECT_EQ(Buffer[1], 0U); 277 EXPECT_EQ(Buffer[2], 0U); 278 } 279 void endFlatSection() override { ++ExitedFlatCount; } 280 }; 281 282 TestProfileWriter W(Root.CtxRoot, 1); 283 EXPECT_FALSE(W.State); 284 __llvm_ctx_profile_fetch(W); 285 EXPECT_TRUE(W.State); 286 287 // this resets all counters but not the internal structure. 288 __llvm_ctx_profile_start_collection(); 289 auto *Flat = 290 __llvm_ctx_profile_get_context(&FData, &FakeCalleeAddress, 2, 3, 1); 291 (void)Flat; 292 EXPECT_NE(FData.FlatCtx, nullptr); 293 FData.FlatCtx->counters()[0] = 15U; 294 TestProfileWriter W2(Root.CtxRoot, 0); 295 EXPECT_FALSE(W2.State); 296 __llvm_ctx_profile_fetch(W2); 297 EXPECT_TRUE(W2.State); 298 EXPECT_EQ(W2.EnteredSectionCount, 1); 299 EXPECT_EQ(W2.ExitedSectionCount, 1); 300 EXPECT_EQ(W2.EnteredFlatCount, 1); 301 EXPECT_EQ(W2.FlatsWritten, 1); 302 EXPECT_EQ(W2.ExitedFlatCount, 1); 303 } 304 305 TEST_F(ContextTest, MustNotBeRoot) { 306 FunctionData FData; 307 FData.CtxRoot = reinterpret_cast<ContextRoot *>(1U); 308 int FakeCalleeAddress = 0; 309 __llvm_ctx_profile_start_collection(); 310 auto *Subctx = 311 __llvm_ctx_profile_get_context(&FData, &FakeCalleeAddress, 2, 3, 1); 312 EXPECT_TRUE(isScratch(Subctx)); 313 } 314