/*===- RootAutodetector.h- auto-detect roots for ctxprof -----------------===*\ |* |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |* See https://llvm.org/LICENSE.txt for license information. |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |* \*===----------------------------------------------------------------------===*/ #ifndef CTX_PROFILE_ROOTAUTODETECTOR_H_ #define CTX_PROFILE_ROOTAUTODETECTOR_H_ #include "sanitizer_common/sanitizer_dense_map.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_vector.h" #include #include using namespace __asan; using namespace __sanitizer; namespace __ctx_profile { /// Capture all the stack traces observed for a specific thread. The "for a /// specific thread" part is not enforced, but assumed in determineRoots. class PerThreadCallsiteTrie { protected: /// A trie. A node is the address of a callsite in a function activation. A /// child is a callsite in the activation made from the callsite /// corresponding to the parent. struct Trie final { const uptr CallsiteAddress; uint64_t Count = 0; DenseMap Children; Trie(uptr CallsiteAddress = 0) : CallsiteAddress(CallsiteAddress) {} }; Trie TheTrie; /// Return the runtime start address of the function that contains the call at /// the runtime address CallsiteAddress. May be overriden for easy testing. virtual uptr getFctStartAddr(uptr CallsiteAddress) const; public: PerThreadCallsiteTrie(const PerThreadCallsiteTrie &) = delete; PerThreadCallsiteTrie(PerThreadCallsiteTrie &&) = default; PerThreadCallsiteTrie() = default; virtual ~PerThreadCallsiteTrie() = default; void insertStack(const StackTrace &ST); /// Return the runtime address of root functions, as determined for this /// thread, together with the number of samples that included them. DenseMap determineRoots() const; }; class RootAutoDetector final { // A prime number. We may want to make this configurable at collection start. static const uint64_t SampleRate = 6113; const unsigned WaitSeconds; pthread_t WorkerThread; struct PerThreadSamples { PerThreadSamples(RootAutoDetector &Parent); PerThreadCallsiteTrie TrieRoot; SpinMutex M; }; SpinMutex AllSamplesMutex; SANITIZER_GUARDED_BY(AllSamplesMutex) Vector AllSamples; atomic_uintptr_t &FunctionDataListHead; atomic_uintptr_t &Self; void collectStack(); public: RootAutoDetector(atomic_uintptr_t &FunctionDataListHead, atomic_uintptr_t &Self, unsigned WaitSeconds) : WaitSeconds(WaitSeconds), FunctionDataListHead(FunctionDataListHead), Self(Self) {} // Samples the stack at `SampleRate` (rate observed independently on each // thread) in thread local `PerThreadCallsiteTrie`s. void sample(); // Start a thread waiting `WaitSeconds`, after which it uses the // `PerThreadCallsiteTrie` data observed so far over all threads to determine // roots. Marks those roots by traversing the linked list of FunctionData that // starts at `FunctionDataListHead`, and assigning their `CtxRoot`. Finally, // resets the `Self` atomic, so that other threads don't continue calling // `sample`. void start(); // join the waiting thread. void join(); }; } // namespace __ctx_profile #endif