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