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)
markAsScratch(const ContextNode * Ctx)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.
consume(T & V)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.
getArenaAllocSize(size_t Needed)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
validate(const ContextRoot * Root)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
allocContextNode(char * Place,GUID Guid,uint32_t NrCounters,uint32_t NrCallsites,ContextNode * Next=nullptr)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
resetContextNode(ContextNode & Node)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
onContextEnter(ContextNode & Node)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
Arena(uint32_t Size)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.
allocateNewArena(size_t Size,Arena * Prev)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
freeArenaList(Arena * & A)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.
getCallsiteSlow(GUID Guid,ContextNode ** InsertionPoint,uint32_t NrCounters,uint32_t NrCallsites)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
__llvm_ctx_profile_get_context(void * Callee,GUID Guid,uint32_t NrCounters,uint32_t NrCallsites)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.
setupContext(ContextRoot * Root,GUID Guid,uint32_t NrCounters,uint32_t NrCallsites)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
__llvm_ctx_profile_start_context(ContextRoot * Root,GUID Guid,uint32_t Counters,uint32_t Callsites)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
__llvm_ctx_profile_release_context(ContextRoot * Root)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
__llvm_ctx_profile_start_collection()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
__llvm_ctx_profile_fetch(void * Data,bool (* Writer)(void * W,const ContextNode &))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
__llvm_ctx_profile_free()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