314 lines · cpp
1#include "../CtxInstrProfiling.h"2#include "gtest/gtest.h"3#include <thread>4 5using namespace __ctx_profile;6 7class ContextTest : public ::testing::Test {8 void SetUp() override { Root.getOrAllocateContextRoot(); }9 void TearDown() override { __llvm_ctx_profile_free(); }10 11public:12 FunctionData Root;13};14 15TEST(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 24TEST(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 42TEST_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 62TEST_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 the69 // 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 does73 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 dimensioned79 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 91TEST_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 the101 // returned context must be a tagged pointer.102 EXPECT_TRUE(isScratch(Ctx));103 // Because we didn't start collection, no flat profile should have been104 // allocated.105 EXPECT_EQ(FData.FlatCtx, nullptr);106}107 108TEST_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 the119 // 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 context122 EXPECT_NE(FData.FlatCtx, nullptr);123 EXPECT_EQ(reinterpret_cast<uintptr_t>(FData.FlatCtx) + 1,124 reinterpret_cast<uintptr_t>(Ctx));125}126 127TEST_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 happens138 // in the case of a signal handler - in this case, OtherFakeCalleeAddress is139 // the signal handler.140 EXPECT_TRUE(isScratch(Subctx));141 // We shouldn't have tried to return a flat context because we're under a142 // 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 runtime154 // 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 in163 // __llvm_ctx_profile_callsite is scratch164 EXPECT_TRUE(isScratch(Subctx3));165 EXPECT_EQ(FData[2].FlatCtx, nullptr);166 167 __llvm_ctx_profile_release_context(&Root);168}169 170TEST_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 allocation182 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 190TEST_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 216TEST_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 305TEST_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