xref: /freebsd/contrib/llvm-project/compiler-rt/lib/ctx_profile/RootAutoDetector.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
1*700637cbSDimitry Andric /*===- RootAutodetector.h- auto-detect roots for ctxprof  -----------------===*\
2*700637cbSDimitry Andric |*
3*700637cbSDimitry Andric |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*700637cbSDimitry Andric |* See https://llvm.org/LICENSE.txt for license information.
5*700637cbSDimitry Andric |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*700637cbSDimitry Andric |*
7*700637cbSDimitry Andric \*===----------------------------------------------------------------------===*/
8*700637cbSDimitry Andric 
9*700637cbSDimitry Andric #ifndef CTX_PROFILE_ROOTAUTODETECTOR_H_
10*700637cbSDimitry Andric #define CTX_PROFILE_ROOTAUTODETECTOR_H_
11*700637cbSDimitry Andric 
12*700637cbSDimitry Andric #include "sanitizer_common/sanitizer_dense_map.h"
13*700637cbSDimitry Andric #include "sanitizer_common/sanitizer_internal_defs.h"
14*700637cbSDimitry Andric #include "sanitizer_common/sanitizer_stacktrace.h"
15*700637cbSDimitry Andric #include "sanitizer_common/sanitizer_vector.h"
16*700637cbSDimitry Andric #include <pthread.h>
17*700637cbSDimitry Andric #include <sanitizer/common_interface_defs.h>
18*700637cbSDimitry Andric 
19*700637cbSDimitry Andric using namespace __asan;
20*700637cbSDimitry Andric using namespace __sanitizer;
21*700637cbSDimitry Andric 
22*700637cbSDimitry Andric namespace __ctx_profile {
23*700637cbSDimitry Andric 
24*700637cbSDimitry Andric /// Capture all the stack traces observed for a specific thread. The "for a
25*700637cbSDimitry Andric /// specific thread" part is not enforced, but assumed in determineRoots.
26*700637cbSDimitry Andric class PerThreadCallsiteTrie {
27*700637cbSDimitry Andric protected:
28*700637cbSDimitry Andric   /// A trie. A node is the address of a callsite in a function activation. A
29*700637cbSDimitry Andric   /// child is a callsite in the activation made from the callsite
30*700637cbSDimitry Andric   /// corresponding to the parent.
31*700637cbSDimitry Andric   struct Trie final {
32*700637cbSDimitry Andric     const uptr CallsiteAddress;
33*700637cbSDimitry Andric     uint64_t Count = 0;
34*700637cbSDimitry Andric     DenseMap<uptr, Trie> Children;
35*700637cbSDimitry Andric 
CallsiteAddressfinal36*700637cbSDimitry Andric     Trie(uptr CallsiteAddress = 0) : CallsiteAddress(CallsiteAddress) {}
37*700637cbSDimitry Andric   };
38*700637cbSDimitry Andric   Trie TheTrie;
39*700637cbSDimitry Andric 
40*700637cbSDimitry Andric   /// Return the runtime start address of the function that contains the call at
41*700637cbSDimitry Andric   /// the runtime address CallsiteAddress. May be overriden for easy testing.
42*700637cbSDimitry Andric   virtual uptr getFctStartAddr(uptr CallsiteAddress) const;
43*700637cbSDimitry Andric 
44*700637cbSDimitry Andric public:
45*700637cbSDimitry Andric   PerThreadCallsiteTrie(const PerThreadCallsiteTrie &) = delete;
46*700637cbSDimitry Andric   PerThreadCallsiteTrie(PerThreadCallsiteTrie &&) = default;
47*700637cbSDimitry Andric   PerThreadCallsiteTrie() = default;
48*700637cbSDimitry Andric 
49*700637cbSDimitry Andric   virtual ~PerThreadCallsiteTrie() = default;
50*700637cbSDimitry Andric 
51*700637cbSDimitry Andric   void insertStack(const StackTrace &ST);
52*700637cbSDimitry Andric 
53*700637cbSDimitry Andric   /// Return the runtime address of root functions, as determined for this
54*700637cbSDimitry Andric   /// thread, together with the number of samples that included them.
55*700637cbSDimitry Andric   DenseMap<uptr, uint64_t> determineRoots() const;
56*700637cbSDimitry Andric };
57*700637cbSDimitry Andric 
58*700637cbSDimitry Andric class RootAutoDetector final {
59*700637cbSDimitry Andric   // A prime number. We may want to make this configurable at collection start.
60*700637cbSDimitry Andric   static const uint64_t SampleRate = 6113;
61*700637cbSDimitry Andric   const unsigned WaitSeconds;
62*700637cbSDimitry Andric   pthread_t WorkerThread;
63*700637cbSDimitry Andric 
64*700637cbSDimitry Andric   struct PerThreadSamples {
65*700637cbSDimitry Andric     PerThreadSamples(RootAutoDetector &Parent);
66*700637cbSDimitry Andric 
67*700637cbSDimitry Andric     PerThreadCallsiteTrie TrieRoot;
68*700637cbSDimitry Andric     SpinMutex M;
69*700637cbSDimitry Andric   };
70*700637cbSDimitry Andric   SpinMutex AllSamplesMutex;
71*700637cbSDimitry Andric   SANITIZER_GUARDED_BY(AllSamplesMutex)
72*700637cbSDimitry Andric   Vector<PerThreadSamples *> AllSamples;
73*700637cbSDimitry Andric   atomic_uintptr_t &FunctionDataListHead;
74*700637cbSDimitry Andric   atomic_uintptr_t &Self;
75*700637cbSDimitry Andric   void collectStack();
76*700637cbSDimitry Andric 
77*700637cbSDimitry Andric public:
RootAutoDetector(atomic_uintptr_t & FunctionDataListHead,atomic_uintptr_t & Self,unsigned WaitSeconds)78*700637cbSDimitry Andric   RootAutoDetector(atomic_uintptr_t &FunctionDataListHead,
79*700637cbSDimitry Andric                    atomic_uintptr_t &Self, unsigned WaitSeconds)
80*700637cbSDimitry Andric       : WaitSeconds(WaitSeconds), FunctionDataListHead(FunctionDataListHead),
81*700637cbSDimitry Andric         Self(Self) {}
82*700637cbSDimitry Andric 
83*700637cbSDimitry Andric   // Samples the stack at `SampleRate` (rate observed independently on each
84*700637cbSDimitry Andric   // thread) in thread local `PerThreadCallsiteTrie`s.
85*700637cbSDimitry Andric   void sample();
86*700637cbSDimitry Andric 
87*700637cbSDimitry Andric   // Start a thread waiting `WaitSeconds`, after which it uses the
88*700637cbSDimitry Andric   // `PerThreadCallsiteTrie` data observed so far over all threads to determine
89*700637cbSDimitry Andric   // roots. Marks those roots by traversing the linked list of FunctionData that
90*700637cbSDimitry Andric   // starts at `FunctionDataListHead`, and assigning their `CtxRoot`. Finally,
91*700637cbSDimitry Andric   // resets the `Self` atomic, so that other threads don't continue calling
92*700637cbSDimitry Andric   // `sample`.
93*700637cbSDimitry Andric   void start();
94*700637cbSDimitry Andric 
95*700637cbSDimitry Andric   // join the waiting thread.
96*700637cbSDimitry Andric   void join();
97*700637cbSDimitry Andric };
98*700637cbSDimitry Andric 
99*700637cbSDimitry Andric } // namespace __ctx_profile
100*700637cbSDimitry Andric #endif
101