poisson_allocation_sampler.h 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #ifndef BASE_SAMPLING_HEAP_PROFILER_POISSON_ALLOCATION_SAMPLER_H_
  5. #define BASE_SAMPLING_HEAP_PROFILER_POISSON_ALLOCATION_SAMPLER_H_
  6. #include <vector>
  7. #include "base/base_export.h"
  8. #include "base/compiler_specific.h"
  9. #include "base/gtest_prod_util.h"
  10. #include "base/no_destructor.h"
  11. #include "base/sampling_heap_profiler/lock_free_address_hash_set.h"
  12. #include "base/synchronization/lock.h"
  13. #include "base/thread_annotations.h"
  14. namespace heap_profiling {
  15. class HeapProfilerControllerTest;
  16. }
  17. namespace base {
  18. class SamplingHeapProfilerTest;
  19. // This singleton class implements Poisson sampling of the incoming allocations
  20. // stream. It hooks onto base::allocator and base::PartitionAlloc.
  21. // An extra custom allocator can be hooked via SetHooksInstallCallback method.
  22. // The only control parameter is sampling interval that controls average value
  23. // of the sampling intervals. The actual intervals between samples are
  24. // randomized using Poisson distribution to mitigate patterns in the allocation
  25. // stream.
  26. // Once accumulated allocation sizes fill up the current sample interval,
  27. // a sample is generated and sent to the observers via |SampleAdded| call.
  28. // When the corresponding memory that triggered the sample is freed observers
  29. // get notified with |SampleRemoved| call.
  30. //
  31. class BASE_EXPORT PoissonAllocationSampler {
  32. public:
  33. // The type of hooked allocator that is the source of a sample.
  34. // kManualForTesting is for unit tests calling RecordAlloc directly without
  35. // going through a hooked allocator.
  36. enum AllocatorType : uint32_t { kMalloc, kPartitionAlloc, kManualForTesting };
  37. class SamplesObserver {
  38. public:
  39. virtual ~SamplesObserver() = default;
  40. virtual void SampleAdded(void* address,
  41. size_t size,
  42. size_t total,
  43. AllocatorType type,
  44. const char* context) = 0;
  45. virtual void SampleRemoved(void* address) = 0;
  46. };
  47. // An instance of this class makes the sampler not report samples generated
  48. // within the object scope for the current thread.
  49. // It allows observers to allocate/deallocate memory while holding a lock
  50. // without a chance to get into reentrancy problems.
  51. // The current implementation doesn't support ScopedMuteThreadSamples nesting.
  52. class BASE_EXPORT ScopedMuteThreadSamples {
  53. public:
  54. ScopedMuteThreadSamples();
  55. ~ScopedMuteThreadSamples();
  56. ScopedMuteThreadSamples(const ScopedMuteThreadSamples&) = delete;
  57. ScopedMuteThreadSamples& operator=(const ScopedMuteThreadSamples&) = delete;
  58. static bool IsMuted();
  59. };
  60. // An instance of this class makes the sampler behave deterministically to
  61. // ensure test results are repeatable. Does not support nesting.
  62. class BASE_EXPORT ScopedSuppressRandomnessForTesting {
  63. public:
  64. ScopedSuppressRandomnessForTesting();
  65. ~ScopedSuppressRandomnessForTesting();
  66. ScopedSuppressRandomnessForTesting(
  67. const ScopedSuppressRandomnessForTesting&) = delete;
  68. ScopedSuppressRandomnessForTesting& operator=(
  69. const ScopedSuppressRandomnessForTesting&) = delete;
  70. static bool IsSuppressed();
  71. };
  72. // Must be called early during the process initialization. It creates and
  73. // reserves a TLS slot.
  74. static void Init();
  75. // This is an entry point for plugging in an external allocator.
  76. // Profiler will invoke the provided callback upon initialization.
  77. // The callback should install hooks onto the corresponding memory allocator
  78. // and make them invoke PoissonAllocationSampler::RecordAlloc and
  79. // PoissonAllocationSampler::RecordFree upon corresponding allocation events.
  80. //
  81. // If the method is called after profiler is initialized, the callback
  82. // is invoked right away.
  83. static void SetHooksInstallCallback(void (*hooks_install_callback)());
  84. void AddSamplesObserver(SamplesObserver*);
  85. // Note: After an observer is removed it is still possible to receive
  86. // a notification to that observer. This is not a problem currently as
  87. // the only client of this interface is the base::SamplingHeapProfiler,
  88. // which is a singleton.
  89. // If there's a need for this functionality in the future, one might
  90. // want to put observers notification loop under a reader-writer lock.
  91. void RemoveSamplesObserver(SamplesObserver*);
  92. // Sets the mean number of bytes that will be allocated before taking a
  93. // sample.
  94. void SetSamplingInterval(size_t sampling_interval_bytes);
  95. // Returns the current mean sampling interval, in bytes.
  96. size_t SamplingInterval() const;
  97. static void RecordAlloc(void* address,
  98. size_t,
  99. AllocatorType,
  100. const char* context);
  101. ALWAYS_INLINE static void RecordFree(void* address);
  102. static PoissonAllocationSampler* Get();
  103. PoissonAllocationSampler(const PoissonAllocationSampler&) = delete;
  104. PoissonAllocationSampler& operator=(const PoissonAllocationSampler&) = delete;
  105. // Returns true if a ScopedMuteHookedSamplesForTesting exists. Only friends
  106. // can create a ScopedMuteHookedSamplesForTesting but anyone can check the
  107. // status of this. This can be read from any thread.
  108. static bool AreHookedSamplesMuted();
  109. private:
  110. // An instance of this class makes the sampler only report samples with
  111. // AllocatorType kManualForTesting, not those from hooked allocators. This
  112. // allows unit tests to set test expectations based on only explicit calls to
  113. // RecordAlloc and RecordFree.
  114. //
  115. // The accumulated bytes on the thread that creates a
  116. // ScopedMuteHookedSamplesForTesting will also be reset to 0, and restored
  117. // when the object leaves scope. This gives tests a known state to start
  118. // recording samples on one thread: a full interval must pass to record a
  119. // sample. Other threads will still have a random number of accumulated bytes.
  120. //
  121. // Only one instance may exist at a time.
  122. class BASE_EXPORT ScopedMuteHookedSamplesForTesting {
  123. public:
  124. ScopedMuteHookedSamplesForTesting();
  125. ~ScopedMuteHookedSamplesForTesting();
  126. ScopedMuteHookedSamplesForTesting(
  127. const ScopedMuteHookedSamplesForTesting&) = delete;
  128. ScopedMuteHookedSamplesForTesting& operator=(
  129. const ScopedMuteHookedSamplesForTesting&) = delete;
  130. private:
  131. intptr_t accumulated_bytes_snapshot_;
  132. };
  133. PoissonAllocationSampler();
  134. ~PoissonAllocationSampler() = delete;
  135. // Installs allocator hooks if they weren't already installed. This is not
  136. // static to ensure that allocator hooks can't be installed unless the
  137. // PoissonAllocationSampler singleton exists.
  138. void InstallAllocatorHooksOnce();
  139. static size_t GetNextSampleInterval(size_t base_interval);
  140. // Return the set of sampled addresses. This is only valid to call after
  141. // Init().
  142. static LockFreeAddressHashSet& sampled_addresses_set();
  143. void DoRecordAlloc(intptr_t accumulated_bytes,
  144. size_t size,
  145. void* address,
  146. AllocatorType type,
  147. const char* context);
  148. void DoRecordFree(void* address);
  149. void BalanceAddressesHashSet();
  150. Lock mutex_;
  151. // The |observers_| list is guarded by |mutex_|, however a copy of it
  152. // is made before invoking the observers (to avoid performing expensive
  153. // operations under the lock) as such the SamplesObservers themselves need
  154. // to be thread-safe and support being invoked racily after
  155. // RemoveSamplesObserver().
  156. std::vector<SamplesObserver*> observers_ GUARDED_BY(mutex_);
  157. static PoissonAllocationSampler* instance_;
  158. friend class heap_profiling::HeapProfilerControllerTest;
  159. friend class NoDestructor<PoissonAllocationSampler>;
  160. friend class SamplingHeapProfilerTest;
  161. FRIEND_TEST_ALL_PREFIXES(PoissonAllocationSamplerTest, MuteHooksWithoutInit);
  162. FRIEND_TEST_ALL_PREFIXES(SamplingHeapProfilerTest, HookedAllocatorMuted);
  163. };
  164. // static
  165. ALWAYS_INLINE void PoissonAllocationSampler::RecordFree(void* address) {
  166. if (UNLIKELY(address == nullptr))
  167. return;
  168. if (UNLIKELY(sampled_addresses_set().Contains(address)))
  169. instance_->DoRecordFree(address);
  170. }
  171. } // namespace base
  172. #endif // BASE_SAMPLING_HEAP_PROFILER_POISSON_ALLOCATION_SAMPLER_H_