1*0fca6ea1SDimitry Andric //===- CtxInstrProfiling.cpp - contextual instrumented PGO ----------------===// 2*0fca6ea1SDimitry Andric // 3*0fca6ea1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0fca6ea1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0fca6ea1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0fca6ea1SDimitry Andric // 7*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 8*0fca6ea1SDimitry Andric 9*0fca6ea1SDimitry Andric #include "CtxInstrProfiling.h" 10*0fca6ea1SDimitry Andric #include "sanitizer_common/sanitizer_allocator_internal.h" 11*0fca6ea1SDimitry Andric #include "sanitizer_common/sanitizer_common.h" 12*0fca6ea1SDimitry Andric #include "sanitizer_common/sanitizer_dense_map.h" 13*0fca6ea1SDimitry Andric #include "sanitizer_common/sanitizer_libc.h" 14*0fca6ea1SDimitry Andric #include "sanitizer_common/sanitizer_mutex.h" 15*0fca6ea1SDimitry Andric #include "sanitizer_common/sanitizer_placement_new.h" 16*0fca6ea1SDimitry Andric #include "sanitizer_common/sanitizer_thread_safety.h" 17*0fca6ea1SDimitry Andric #include "sanitizer_common/sanitizer_vector.h" 18*0fca6ea1SDimitry Andric 19*0fca6ea1SDimitry Andric #include <assert.h> 20*0fca6ea1SDimitry Andric 21*0fca6ea1SDimitry Andric using namespace __ctx_profile; 22*0fca6ea1SDimitry Andric 23*0fca6ea1SDimitry Andric namespace { 24*0fca6ea1SDimitry Andric // Keep track of all the context roots we actually saw, so we can then traverse 25*0fca6ea1SDimitry Andric // them when the user asks for the profile in __llvm_ctx_profile_fetch 26*0fca6ea1SDimitry Andric __sanitizer::SpinMutex AllContextsMutex; 27*0fca6ea1SDimitry Andric SANITIZER_GUARDED_BY(AllContextsMutex) 28*0fca6ea1SDimitry Andric __sanitizer::Vector<ContextRoot *> AllContextRoots; 29*0fca6ea1SDimitry Andric 30*0fca6ea1SDimitry Andric // utility to taint a pointer by setting the LSB. There is an assumption 31*0fca6ea1SDimitry Andric // throughout that the addresses of contexts are even (really, they should be 32*0fca6ea1SDimitry Andric // align(8), but "even"-ness is the minimum assumption) 33*0fca6ea1SDimitry Andric // "scratch contexts" are buffers that we return in certain cases - they are 34*0fca6ea1SDimitry Andric // large enough to allow for memory safe counter access, but they don't link 35*0fca6ea1SDimitry Andric // subcontexts below them (the runtime recognizes them and enforces that) 36*0fca6ea1SDimitry Andric ContextNode *markAsScratch(const ContextNode *Ctx) { 37*0fca6ea1SDimitry Andric return reinterpret_cast<ContextNode *>(reinterpret_cast<uint64_t>(Ctx) | 1); 38*0fca6ea1SDimitry Andric } 39*0fca6ea1SDimitry Andric 40*0fca6ea1SDimitry Andric // Used when getting the data from TLS. We don't *really* need to reset, but 41*0fca6ea1SDimitry Andric // it's a simpler system if we do. 42*0fca6ea1SDimitry Andric template <typename T> inline T consume(T &V) { 43*0fca6ea1SDimitry Andric auto R = V; 44*0fca6ea1SDimitry Andric V = {0}; 45*0fca6ea1SDimitry Andric return R; 46*0fca6ea1SDimitry Andric } 47*0fca6ea1SDimitry Andric 48*0fca6ea1SDimitry Andric // We allocate at least kBuffSize Arena pages. The scratch buffer is also that 49*0fca6ea1SDimitry Andric // large. 50*0fca6ea1SDimitry Andric constexpr size_t kPower = 20; 51*0fca6ea1SDimitry Andric constexpr size_t kBuffSize = 1 << kPower; 52*0fca6ea1SDimitry Andric 53*0fca6ea1SDimitry Andric // Highly unlikely we need more than kBuffSize for a context. 54*0fca6ea1SDimitry Andric size_t getArenaAllocSize(size_t Needed) { 55*0fca6ea1SDimitry Andric if (Needed >= kBuffSize) 56*0fca6ea1SDimitry Andric return 2 * Needed; 57*0fca6ea1SDimitry Andric return kBuffSize; 58*0fca6ea1SDimitry Andric } 59*0fca6ea1SDimitry Andric 60*0fca6ea1SDimitry Andric // verify the structural integrity of the context 61*0fca6ea1SDimitry Andric bool validate(const ContextRoot *Root) { 62*0fca6ea1SDimitry Andric // all contexts should be laid out in some arena page. Go over each arena 63*0fca6ea1SDimitry Andric // allocated for this Root, and jump over contained contexts based on 64*0fca6ea1SDimitry Andric // self-reported sizes. 65*0fca6ea1SDimitry Andric __sanitizer::DenseMap<uint64_t, bool> ContextStartAddrs; 66*0fca6ea1SDimitry Andric for (const auto *Mem = Root->FirstMemBlock; Mem; Mem = Mem->next()) { 67*0fca6ea1SDimitry Andric const auto *Pos = Mem->start(); 68*0fca6ea1SDimitry Andric while (Pos < Mem->pos()) { 69*0fca6ea1SDimitry Andric const auto *Ctx = reinterpret_cast<const ContextNode *>(Pos); 70*0fca6ea1SDimitry Andric if (!ContextStartAddrs.insert({reinterpret_cast<uint64_t>(Ctx), true}) 71*0fca6ea1SDimitry Andric .second) 72*0fca6ea1SDimitry Andric return false; 73*0fca6ea1SDimitry Andric Pos += Ctx->size(); 74*0fca6ea1SDimitry Andric } 75*0fca6ea1SDimitry Andric } 76*0fca6ea1SDimitry Andric 77*0fca6ea1SDimitry Andric // Now traverse the contexts again the same way, but validate all nonull 78*0fca6ea1SDimitry Andric // subcontext addresses appear in the set computed above. 79*0fca6ea1SDimitry Andric for (const auto *Mem = Root->FirstMemBlock; Mem; Mem = Mem->next()) { 80*0fca6ea1SDimitry Andric const auto *Pos = Mem->start(); 81*0fca6ea1SDimitry Andric while (Pos < Mem->pos()) { 82*0fca6ea1SDimitry Andric const auto *Ctx = reinterpret_cast<const ContextNode *>(Pos); 83*0fca6ea1SDimitry Andric for (uint32_t I = 0; I < Ctx->callsites_size(); ++I) 84*0fca6ea1SDimitry Andric for (auto *Sub = Ctx->subContexts()[I]; Sub; Sub = Sub->next()) 85*0fca6ea1SDimitry Andric if (!ContextStartAddrs.find(reinterpret_cast<uint64_t>(Sub))) 86*0fca6ea1SDimitry Andric return false; 87*0fca6ea1SDimitry Andric 88*0fca6ea1SDimitry Andric Pos += Ctx->size(); 89*0fca6ea1SDimitry Andric } 90*0fca6ea1SDimitry Andric } 91*0fca6ea1SDimitry Andric return true; 92*0fca6ea1SDimitry Andric } 93*0fca6ea1SDimitry Andric 94*0fca6ea1SDimitry Andric inline ContextNode *allocContextNode(char *Place, GUID Guid, 95*0fca6ea1SDimitry Andric uint32_t NrCounters, uint32_t NrCallsites, 96*0fca6ea1SDimitry Andric ContextNode *Next = nullptr) { 97*0fca6ea1SDimitry Andric assert(reinterpret_cast<uint64_t>(Place) % ExpectedAlignment == 0); 98*0fca6ea1SDimitry Andric return new (Place) ContextNode(Guid, NrCounters, NrCallsites, Next); 99*0fca6ea1SDimitry Andric } 100*0fca6ea1SDimitry Andric 101*0fca6ea1SDimitry Andric void resetContextNode(ContextNode &Node) { 102*0fca6ea1SDimitry Andric // FIXME(mtrofin): this is std::memset, which we can probably use if we 103*0fca6ea1SDimitry Andric // drop/reduce the dependency on sanitizer_common. 104*0fca6ea1SDimitry Andric for (uint32_t I = 0; I < Node.counters_size(); ++I) 105*0fca6ea1SDimitry Andric Node.counters()[I] = 0; 106*0fca6ea1SDimitry Andric for (uint32_t I = 0; I < Node.callsites_size(); ++I) 107*0fca6ea1SDimitry Andric for (auto *Next = Node.subContexts()[I]; Next; Next = Next->next()) 108*0fca6ea1SDimitry Andric resetContextNode(*Next); 109*0fca6ea1SDimitry Andric } 110*0fca6ea1SDimitry Andric 111*0fca6ea1SDimitry Andric void onContextEnter(ContextNode &Node) { ++Node.counters()[0]; } 112*0fca6ea1SDimitry Andric 113*0fca6ea1SDimitry Andric } // namespace 114*0fca6ea1SDimitry Andric 115*0fca6ea1SDimitry Andric // the scratch buffer - what we give when we can't produce a real context (the 116*0fca6ea1SDimitry Andric // scratch isn't "real" in that it's expected to be clobbered carelessly - we 117*0fca6ea1SDimitry Andric // don't read it). The other important thing is that the callees from a scratch 118*0fca6ea1SDimitry Andric // context also get a scratch context. 119*0fca6ea1SDimitry Andric // Eventually this can be replaced with per-function buffers, a'la the typical 120*0fca6ea1SDimitry Andric // (flat) instrumented FDO buffers. The clobbering aspect won't apply there, but 121*0fca6ea1SDimitry Andric // the part about determining the nature of the subcontexts does. 122*0fca6ea1SDimitry Andric __thread char __Buffer[kBuffSize] = {0}; 123*0fca6ea1SDimitry Andric 124*0fca6ea1SDimitry Andric #define TheScratchContext \ 125*0fca6ea1SDimitry Andric markAsScratch(reinterpret_cast<ContextNode *>(__Buffer)) 126*0fca6ea1SDimitry Andric 127*0fca6ea1SDimitry Andric // init the TLSes 128*0fca6ea1SDimitry Andric __thread void *volatile __llvm_ctx_profile_expected_callee[2] = {nullptr, 129*0fca6ea1SDimitry Andric nullptr}; 130*0fca6ea1SDimitry Andric __thread ContextNode **volatile __llvm_ctx_profile_callsite[2] = {0, 0}; 131*0fca6ea1SDimitry Andric 132*0fca6ea1SDimitry Andric __thread ContextRoot *volatile __llvm_ctx_profile_current_context_root = 133*0fca6ea1SDimitry Andric nullptr; 134*0fca6ea1SDimitry Andric 135*0fca6ea1SDimitry Andric Arena::Arena(uint32_t Size) : Size(Size) { 136*0fca6ea1SDimitry Andric __sanitizer::internal_memset(start(), 0, Size); 137*0fca6ea1SDimitry Andric } 138*0fca6ea1SDimitry Andric 139*0fca6ea1SDimitry Andric // FIXME(mtrofin): use malloc / mmap instead of sanitizer common APIs to reduce 140*0fca6ea1SDimitry Andric // the dependency on the latter. 141*0fca6ea1SDimitry Andric Arena *Arena::allocateNewArena(size_t Size, Arena *Prev) { 142*0fca6ea1SDimitry Andric assert(!Prev || Prev->Next == nullptr); 143*0fca6ea1SDimitry Andric Arena *NewArena = new (__sanitizer::InternalAlloc( 144*0fca6ea1SDimitry Andric Size + sizeof(Arena), /*cache=*/nullptr, /*alignment=*/ExpectedAlignment)) 145*0fca6ea1SDimitry Andric Arena(Size); 146*0fca6ea1SDimitry Andric if (Prev) 147*0fca6ea1SDimitry Andric Prev->Next = NewArena; 148*0fca6ea1SDimitry Andric return NewArena; 149*0fca6ea1SDimitry Andric } 150*0fca6ea1SDimitry Andric 151*0fca6ea1SDimitry Andric void Arena::freeArenaList(Arena *&A) { 152*0fca6ea1SDimitry Andric assert(A); 153*0fca6ea1SDimitry Andric for (auto *I = A; I != nullptr;) { 154*0fca6ea1SDimitry Andric auto *Current = I; 155*0fca6ea1SDimitry Andric I = I->Next; 156*0fca6ea1SDimitry Andric __sanitizer::InternalFree(Current); 157*0fca6ea1SDimitry Andric } 158*0fca6ea1SDimitry Andric A = nullptr; 159*0fca6ea1SDimitry Andric } 160*0fca6ea1SDimitry Andric 161*0fca6ea1SDimitry Andric // If this is the first time we hit a callsite with this (Guid) particular 162*0fca6ea1SDimitry Andric // callee, we need to allocate. 163*0fca6ea1SDimitry Andric ContextNode *getCallsiteSlow(GUID Guid, ContextNode **InsertionPoint, 164*0fca6ea1SDimitry Andric uint32_t NrCounters, uint32_t NrCallsites) { 165*0fca6ea1SDimitry Andric auto AllocSize = ContextNode::getAllocSize(NrCounters, NrCallsites); 166*0fca6ea1SDimitry Andric auto *Mem = __llvm_ctx_profile_current_context_root->CurrentMem; 167*0fca6ea1SDimitry Andric char *AllocPlace = Mem->tryBumpAllocate(AllocSize); 168*0fca6ea1SDimitry Andric if (!AllocPlace) { 169*0fca6ea1SDimitry Andric // if we failed to allocate on the current arena, allocate a new arena, 170*0fca6ea1SDimitry Andric // and place it on __llvm_ctx_profile_current_context_root->CurrentMem so we 171*0fca6ea1SDimitry Andric // find it from now on for other cases when we need to getCallsiteSlow. 172*0fca6ea1SDimitry Andric // Note that allocateNewArena will link the allocated memory in the list of 173*0fca6ea1SDimitry Andric // Arenas. 174*0fca6ea1SDimitry Andric __llvm_ctx_profile_current_context_root->CurrentMem = Mem = 175*0fca6ea1SDimitry Andric Mem->allocateNewArena(getArenaAllocSize(AllocSize), Mem); 176*0fca6ea1SDimitry Andric AllocPlace = Mem->tryBumpAllocate(AllocSize); 177*0fca6ea1SDimitry Andric } 178*0fca6ea1SDimitry Andric auto *Ret = allocContextNode(AllocPlace, Guid, NrCounters, NrCallsites, 179*0fca6ea1SDimitry Andric *InsertionPoint); 180*0fca6ea1SDimitry Andric *InsertionPoint = Ret; 181*0fca6ea1SDimitry Andric return Ret; 182*0fca6ea1SDimitry Andric } 183*0fca6ea1SDimitry Andric 184*0fca6ea1SDimitry Andric ContextNode *__llvm_ctx_profile_get_context(void *Callee, GUID Guid, 185*0fca6ea1SDimitry Andric uint32_t NrCounters, 186*0fca6ea1SDimitry Andric uint32_t NrCallsites) { 187*0fca6ea1SDimitry Andric // fast "out" if we're not even doing contextual collection. 188*0fca6ea1SDimitry Andric if (!__llvm_ctx_profile_current_context_root) 189*0fca6ea1SDimitry Andric return TheScratchContext; 190*0fca6ea1SDimitry Andric 191*0fca6ea1SDimitry Andric // also fast "out" if the caller is scratch. We can see if it's scratch by 192*0fca6ea1SDimitry Andric // looking at the interior pointer into the subcontexts vector that the caller 193*0fca6ea1SDimitry Andric // provided, which, if the context is scratch, so is that interior pointer 194*0fca6ea1SDimitry Andric // (because all the address calculations are using even values. Or more 195*0fca6ea1SDimitry Andric // precisely, aligned - 8 values) 196*0fca6ea1SDimitry Andric auto **CallsiteContext = consume(__llvm_ctx_profile_callsite[0]); 197*0fca6ea1SDimitry Andric if (!CallsiteContext || isScratch(CallsiteContext)) 198*0fca6ea1SDimitry Andric return TheScratchContext; 199*0fca6ea1SDimitry Andric 200*0fca6ea1SDimitry Andric // if the callee isn't the expected one, return scratch. 201*0fca6ea1SDimitry Andric // Signal handler(s) could have been invoked at any point in the execution. 202*0fca6ea1SDimitry Andric // Should that have happened, and had it (the handler) be built with 203*0fca6ea1SDimitry Andric // instrumentation, its __llvm_ctx_profile_get_context would have failed here. 204*0fca6ea1SDimitry Andric // Its sub call graph would have then populated 205*0fca6ea1SDimitry Andric // __llvm_ctx_profile_{expected_callee | callsite} at index 1. 206*0fca6ea1SDimitry Andric // The normal call graph may be impacted in that, if the signal handler 207*0fca6ea1SDimitry Andric // happened somewhere before we read the TLS here, we'd see the TLS reset and 208*0fca6ea1SDimitry Andric // we'd also fail here. That would just mean we would loose counter values for 209*0fca6ea1SDimitry Andric // the normal subgraph, this time around. That should be very unlikely, but if 210*0fca6ea1SDimitry Andric // it happens too frequently, we should be able to detect discrepancies in 211*0fca6ea1SDimitry Andric // entry counts (caller-callee). At the moment, the design goes on the 212*0fca6ea1SDimitry Andric // assumption that is so unfrequent, though, that it's not worth doing more 213*0fca6ea1SDimitry Andric // for that case. 214*0fca6ea1SDimitry Andric auto *ExpectedCallee = consume(__llvm_ctx_profile_expected_callee[0]); 215*0fca6ea1SDimitry Andric if (ExpectedCallee != Callee) 216*0fca6ea1SDimitry Andric return TheScratchContext; 217*0fca6ea1SDimitry Andric 218*0fca6ea1SDimitry Andric auto *Callsite = *CallsiteContext; 219*0fca6ea1SDimitry Andric // in the case of indirect calls, we will have all seen targets forming a 220*0fca6ea1SDimitry Andric // linked list here. Find the one corresponding to this callee. 221*0fca6ea1SDimitry Andric while (Callsite && Callsite->guid() != Guid) { 222*0fca6ea1SDimitry Andric Callsite = Callsite->next(); 223*0fca6ea1SDimitry Andric } 224*0fca6ea1SDimitry Andric auto *Ret = Callsite ? Callsite 225*0fca6ea1SDimitry Andric : getCallsiteSlow(Guid, CallsiteContext, NrCounters, 226*0fca6ea1SDimitry Andric NrCallsites); 227*0fca6ea1SDimitry Andric if (Ret->callsites_size() != NrCallsites || 228*0fca6ea1SDimitry Andric Ret->counters_size() != NrCounters) 229*0fca6ea1SDimitry Andric __sanitizer::Printf("[ctxprof] Returned ctx differs from what's asked: " 230*0fca6ea1SDimitry Andric "Context: %p, Asked: %lu %u %u, Got: %lu %u %u \n", 231*0fca6ea1SDimitry Andric reinterpret_cast<void *>(Ret), Guid, NrCallsites, 232*0fca6ea1SDimitry Andric NrCounters, Ret->guid(), Ret->callsites_size(), 233*0fca6ea1SDimitry Andric Ret->counters_size()); 234*0fca6ea1SDimitry Andric onContextEnter(*Ret); 235*0fca6ea1SDimitry Andric return Ret; 236*0fca6ea1SDimitry Andric } 237*0fca6ea1SDimitry Andric 238*0fca6ea1SDimitry Andric // This should be called once for a Root. Allocate the first arena, set up the 239*0fca6ea1SDimitry Andric // first context. 240*0fca6ea1SDimitry Andric void setupContext(ContextRoot *Root, GUID Guid, uint32_t NrCounters, 241*0fca6ea1SDimitry Andric uint32_t NrCallsites) { 242*0fca6ea1SDimitry Andric __sanitizer::GenericScopedLock<__sanitizer::SpinMutex> Lock( 243*0fca6ea1SDimitry Andric &AllContextsMutex); 244*0fca6ea1SDimitry Andric // Re-check - we got here without having had taken a lock. 245*0fca6ea1SDimitry Andric if (Root->FirstMemBlock) 246*0fca6ea1SDimitry Andric return; 247*0fca6ea1SDimitry Andric const auto Needed = ContextNode::getAllocSize(NrCounters, NrCallsites); 248*0fca6ea1SDimitry Andric auto *M = Arena::allocateNewArena(getArenaAllocSize(Needed)); 249*0fca6ea1SDimitry Andric Root->FirstMemBlock = M; 250*0fca6ea1SDimitry Andric Root->CurrentMem = M; 251*0fca6ea1SDimitry Andric Root->FirstNode = allocContextNode(M->tryBumpAllocate(Needed), Guid, 252*0fca6ea1SDimitry Andric NrCounters, NrCallsites); 253*0fca6ea1SDimitry Andric AllContextRoots.PushBack(Root); 254*0fca6ea1SDimitry Andric } 255*0fca6ea1SDimitry Andric 256*0fca6ea1SDimitry Andric ContextNode *__llvm_ctx_profile_start_context( 257*0fca6ea1SDimitry Andric ContextRoot *Root, GUID Guid, uint32_t Counters, 258*0fca6ea1SDimitry Andric uint32_t Callsites) SANITIZER_NO_THREAD_SAFETY_ANALYSIS { 259*0fca6ea1SDimitry Andric if (!Root->FirstMemBlock) { 260*0fca6ea1SDimitry Andric setupContext(Root, Guid, Counters, Callsites); 261*0fca6ea1SDimitry Andric } 262*0fca6ea1SDimitry Andric if (Root->Taken.TryLock()) { 263*0fca6ea1SDimitry Andric __llvm_ctx_profile_current_context_root = Root; 264*0fca6ea1SDimitry Andric onContextEnter(*Root->FirstNode); 265*0fca6ea1SDimitry Andric return Root->FirstNode; 266*0fca6ea1SDimitry Andric } 267*0fca6ea1SDimitry Andric // If this thread couldn't take the lock, return scratch context. 268*0fca6ea1SDimitry Andric __llvm_ctx_profile_current_context_root = nullptr; 269*0fca6ea1SDimitry Andric return TheScratchContext; 270*0fca6ea1SDimitry Andric } 271*0fca6ea1SDimitry Andric 272*0fca6ea1SDimitry Andric void __llvm_ctx_profile_release_context(ContextRoot *Root) 273*0fca6ea1SDimitry Andric SANITIZER_NO_THREAD_SAFETY_ANALYSIS { 274*0fca6ea1SDimitry Andric if (__llvm_ctx_profile_current_context_root) { 275*0fca6ea1SDimitry Andric __llvm_ctx_profile_current_context_root = nullptr; 276*0fca6ea1SDimitry Andric Root->Taken.Unlock(); 277*0fca6ea1SDimitry Andric } 278*0fca6ea1SDimitry Andric } 279*0fca6ea1SDimitry Andric 280*0fca6ea1SDimitry Andric void __llvm_ctx_profile_start_collection() { 281*0fca6ea1SDimitry Andric size_t NrMemUnits = 0; 282*0fca6ea1SDimitry Andric __sanitizer::GenericScopedLock<__sanitizer::SpinMutex> Lock( 283*0fca6ea1SDimitry Andric &AllContextsMutex); 284*0fca6ea1SDimitry Andric for (uint32_t I = 0; I < AllContextRoots.Size(); ++I) { 285*0fca6ea1SDimitry Andric auto *Root = AllContextRoots[I]; 286*0fca6ea1SDimitry Andric __sanitizer::GenericScopedLock<__sanitizer::StaticSpinMutex> Lock( 287*0fca6ea1SDimitry Andric &Root->Taken); 288*0fca6ea1SDimitry Andric for (auto *Mem = Root->FirstMemBlock; Mem; Mem = Mem->next()) 289*0fca6ea1SDimitry Andric ++NrMemUnits; 290*0fca6ea1SDimitry Andric 291*0fca6ea1SDimitry Andric resetContextNode(*Root->FirstNode); 292*0fca6ea1SDimitry Andric } 293*0fca6ea1SDimitry Andric __sanitizer::Printf("[ctxprof] Initial NrMemUnits: %zu \n", NrMemUnits); 294*0fca6ea1SDimitry Andric } 295*0fca6ea1SDimitry Andric 296*0fca6ea1SDimitry Andric bool __llvm_ctx_profile_fetch(void *Data, 297*0fca6ea1SDimitry Andric bool (*Writer)(void *W, const ContextNode &)) { 298*0fca6ea1SDimitry Andric assert(Writer); 299*0fca6ea1SDimitry Andric __sanitizer::GenericScopedLock<__sanitizer::SpinMutex> Lock( 300*0fca6ea1SDimitry Andric &AllContextsMutex); 301*0fca6ea1SDimitry Andric 302*0fca6ea1SDimitry Andric for (int I = 0, E = AllContextRoots.Size(); I < E; ++I) { 303*0fca6ea1SDimitry Andric auto *Root = AllContextRoots[I]; 304*0fca6ea1SDimitry Andric __sanitizer::GenericScopedLock<__sanitizer::StaticSpinMutex> TakenLock( 305*0fca6ea1SDimitry Andric &Root->Taken); 306*0fca6ea1SDimitry Andric if (!validate(Root)) { 307*0fca6ea1SDimitry Andric __sanitizer::Printf("[ctxprof] Contextual Profile is %s\n", "invalid"); 308*0fca6ea1SDimitry Andric return false; 309*0fca6ea1SDimitry Andric } 310*0fca6ea1SDimitry Andric if (!Writer(Data, *Root->FirstNode)) 311*0fca6ea1SDimitry Andric return false; 312*0fca6ea1SDimitry Andric } 313*0fca6ea1SDimitry Andric return true; 314*0fca6ea1SDimitry Andric } 315*0fca6ea1SDimitry Andric 316*0fca6ea1SDimitry Andric void __llvm_ctx_profile_free() { 317*0fca6ea1SDimitry Andric __sanitizer::GenericScopedLock<__sanitizer::SpinMutex> Lock( 318*0fca6ea1SDimitry Andric &AllContextsMutex); 319*0fca6ea1SDimitry Andric for (int I = 0, E = AllContextRoots.Size(); I < E; ++I) 320*0fca6ea1SDimitry Andric for (auto *A = AllContextRoots[I]->FirstMemBlock; A;) { 321*0fca6ea1SDimitry Andric auto *C = A; 322*0fca6ea1SDimitry Andric A = A->next(); 323*0fca6ea1SDimitry Andric __sanitizer::InternalFree(C); 324*0fca6ea1SDimitry Andric } 325*0fca6ea1SDimitry Andric AllContextRoots.Reset(); 326*0fca6ea1SDimitry Andric } 327