xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/ctx_profile/CtxInstrProfiling.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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