Path: blob/main/contrib/llvm-project/compiler-rt/lib/ctx_profile/tests/CtxInstrProfilingTest.cpp
35269 views
#include "../CtxInstrProfiling.h"1#include "gtest/gtest.h"2#include <thread>34using namespace __ctx_profile;56class ContextTest : public ::testing::Test {7void SetUp() override { memset(&Root, 0, sizeof(ContextRoot)); }8void TearDown() override { __llvm_ctx_profile_free(); }910public:11ContextRoot Root;12};1314TEST(ArenaTest, ZeroInit) {15char Buffer[1024];16memset(Buffer, 1, 1024);17Arena *A = new (Buffer) Arena(10);18for (auto I = 0U; I < A->size(); ++I)19EXPECT_EQ(A->pos()[I], static_cast<char>(0));20EXPECT_EQ(A->size(), 10U);21}2223TEST(ArenaTest, Basic) {24Arena *A = Arena::allocateNewArena(1024);25EXPECT_EQ(A->size(), 1024U);26EXPECT_EQ(A->next(), nullptr);2728auto *M1 = A->tryBumpAllocate(1020);29EXPECT_NE(M1, nullptr);30auto *M2 = A->tryBumpAllocate(4);31EXPECT_NE(M2, nullptr);32EXPECT_EQ(M1 + 1020, M2);33EXPECT_EQ(A->tryBumpAllocate(1), nullptr);34Arena *A2 = Arena::allocateNewArena(2024, A);35EXPECT_EQ(A->next(), A2);36EXPECT_EQ(A2->next(), nullptr);37Arena::freeArenaList(A);38EXPECT_EQ(A, nullptr);39}4041TEST_F(ContextTest, Basic) {42auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4);43ASSERT_NE(Ctx, nullptr);44EXPECT_NE(Root.CurrentMem, nullptr);45EXPECT_EQ(Root.FirstMemBlock, Root.CurrentMem);46EXPECT_EQ(Ctx->size(), sizeof(ContextNode) + 10 * sizeof(uint64_t) +474 * sizeof(ContextNode *));48EXPECT_EQ(Ctx->counters_size(), 10U);49EXPECT_EQ(Ctx->callsites_size(), 4U);50EXPECT_EQ(__llvm_ctx_profile_current_context_root, &Root);51Root.Taken.CheckLocked();52EXPECT_FALSE(Root.Taken.TryLock());53__llvm_ctx_profile_release_context(&Root);54EXPECT_EQ(__llvm_ctx_profile_current_context_root, nullptr);55EXPECT_TRUE(Root.Taken.TryLock());56Root.Taken.Unlock();57}5859TEST_F(ContextTest, Callsite) {60auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4);61int FakeCalleeAddress = 0;62const bool IsScratch = isScratch(Ctx);63EXPECT_FALSE(IsScratch);64// This is the sequence the caller performs - it's the lowering of the65// instrumentation of the callsite "2". "2" is arbitrary here.66__llvm_ctx_profile_expected_callee[0] = &FakeCalleeAddress;67__llvm_ctx_profile_callsite[0] = &Ctx->subContexts()[2];68// This is what the callee does69auto *Subctx = __llvm_ctx_profile_get_context(&FakeCalleeAddress, 2, 3, 1);70// We expect the subcontext to be appropriately placed and dimensioned71EXPECT_EQ(Ctx->subContexts()[2], Subctx);72EXPECT_EQ(Subctx->counters_size(), 3U);73EXPECT_EQ(Subctx->callsites_size(), 1U);74// We reset these in _get_context.75EXPECT_EQ(__llvm_ctx_profile_expected_callee[0], nullptr);76EXPECT_EQ(__llvm_ctx_profile_callsite[0], nullptr);7778EXPECT_EQ(Subctx->size(), sizeof(ContextNode) + 3 * sizeof(uint64_t) +791 * sizeof(ContextNode *));80__llvm_ctx_profile_release_context(&Root);81}8283TEST_F(ContextTest, ScratchNoCollection) {84EXPECT_EQ(__llvm_ctx_profile_current_context_root, nullptr);85int FakeCalleeAddress = 0;86// this would be the very first function executing this. the TLS is empty,87// too.88auto *Ctx = __llvm_ctx_profile_get_context(&FakeCalleeAddress, 2, 3, 1);89// We never entered a context (_start_context was never called) - so the90// returned context must be scratch.91EXPECT_TRUE(isScratch(Ctx));92}9394TEST_F(ContextTest, ScratchDuringCollection) {95auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4);96int FakeCalleeAddress = 0;97int OtherFakeCalleeAddress = 0;98__llvm_ctx_profile_expected_callee[0] = &FakeCalleeAddress;99__llvm_ctx_profile_callsite[0] = &Ctx->subContexts()[2];100auto *Subctx =101__llvm_ctx_profile_get_context(&OtherFakeCalleeAddress, 2, 3, 1);102// We expected a different callee - so return scratch. It mimics what happens103// in the case of a signal handler - in this case, OtherFakeCalleeAddress is104// the signal handler.105EXPECT_TRUE(isScratch(Subctx));106EXPECT_EQ(__llvm_ctx_profile_expected_callee[0], nullptr);107EXPECT_EQ(__llvm_ctx_profile_callsite[0], nullptr);108109int ThirdFakeCalleeAddress = 0;110__llvm_ctx_profile_expected_callee[1] = &ThirdFakeCalleeAddress;111__llvm_ctx_profile_callsite[1] = &Subctx->subContexts()[0];112113auto *Subctx2 =114__llvm_ctx_profile_get_context(&ThirdFakeCalleeAddress, 3, 0, 0);115// We again expect scratch because the '0' position is where the runtime116// looks, so it doesn't matter the '1' position is populated correctly.117EXPECT_TRUE(isScratch(Subctx2));118119__llvm_ctx_profile_expected_callee[0] = &ThirdFakeCalleeAddress;120__llvm_ctx_profile_callsite[0] = &Subctx->subContexts()[0];121auto *Subctx3 =122__llvm_ctx_profile_get_context(&ThirdFakeCalleeAddress, 3, 0, 0);123// We expect scratch here, too, because the value placed in124// __llvm_ctx_profile_callsite is scratch125EXPECT_TRUE(isScratch(Subctx3));126127__llvm_ctx_profile_release_context(&Root);128}129130TEST_F(ContextTest, NeedMoreMemory) {131auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4);132int FakeCalleeAddress = 0;133const bool IsScratch = isScratch(Ctx);134EXPECT_FALSE(IsScratch);135const auto *CurrentMem = Root.CurrentMem;136__llvm_ctx_profile_expected_callee[0] = &FakeCalleeAddress;137__llvm_ctx_profile_callsite[0] = &Ctx->subContexts()[2];138// Allocate a massive subcontext to force new arena allocation139auto *Subctx =140__llvm_ctx_profile_get_context(&FakeCalleeAddress, 3, 1 << 20, 1);141EXPECT_EQ(Ctx->subContexts()[2], Subctx);142EXPECT_NE(CurrentMem, Root.CurrentMem);143EXPECT_NE(Root.CurrentMem, nullptr);144}145146TEST_F(ContextTest, ConcurrentRootCollection) {147std::atomic<int> NonScratch = 0;148std::atomic<int> Executions = 0;149150__sanitizer::Semaphore GotCtx;151152auto Entrypoint = [&]() {153++Executions;154auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4);155GotCtx.Post();156const bool IS = isScratch(Ctx);157NonScratch += (!IS);158if (!IS) {159GotCtx.Wait();160GotCtx.Wait();161}162__llvm_ctx_profile_release_context(&Root);163};164std::thread T1(Entrypoint);165std::thread T2(Entrypoint);166T1.join();167T2.join();168EXPECT_EQ(NonScratch, 1);169EXPECT_EQ(Executions, 2);170}171172TEST_F(ContextTest, Dump) {173auto *Ctx = __llvm_ctx_profile_start_context(&Root, 1, 10, 4);174int FakeCalleeAddress = 0;175__llvm_ctx_profile_expected_callee[0] = &FakeCalleeAddress;176__llvm_ctx_profile_callsite[0] = &Ctx->subContexts()[2];177auto *Subctx = __llvm_ctx_profile_get_context(&FakeCalleeAddress, 2, 3, 1);178(void)Subctx;179__llvm_ctx_profile_release_context(&Root);180181struct Writer {182ContextRoot *const Root;183const size_t Entries;184bool State = false;185Writer(ContextRoot *Root, size_t Entries) : Root(Root), Entries(Entries) {}186187bool write(const ContextNode &Node) {188EXPECT_FALSE(Root->Taken.TryLock());189EXPECT_EQ(Node.guid(), 1U);190EXPECT_EQ(Node.counters()[0], Entries);191EXPECT_EQ(Node.counters_size(), 10U);192EXPECT_EQ(Node.callsites_size(), 4U);193EXPECT_EQ(Node.subContexts()[0], nullptr);194EXPECT_EQ(Node.subContexts()[1], nullptr);195EXPECT_NE(Node.subContexts()[2], nullptr);196EXPECT_EQ(Node.subContexts()[3], nullptr);197const auto &SN = *Node.subContexts()[2];198EXPECT_EQ(SN.guid(), 2U);199EXPECT_EQ(SN.counters()[0], Entries);200EXPECT_EQ(SN.counters_size(), 3U);201EXPECT_EQ(SN.callsites_size(), 1U);202EXPECT_EQ(SN.subContexts()[0], nullptr);203State = true;204return true;205}206};207Writer W(&Root, 1);208EXPECT_FALSE(W.State);209__llvm_ctx_profile_fetch(&W, [](void *W, const ContextNode &Node) -> bool {210return reinterpret_cast<Writer *>(W)->write(Node);211});212EXPECT_TRUE(W.State);213214// this resets all counters but not the internal structure.215__llvm_ctx_profile_start_collection();216Writer W2(&Root, 0);217EXPECT_FALSE(W2.State);218__llvm_ctx_profile_fetch(&W2, [](void *W, const ContextNode &Node) -> bool {219return reinterpret_cast<Writer *>(W)->write(Node);220});221EXPECT_TRUE(W2.State);222}223224225