poisson_allocation_sampler.h 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  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/macros.h"
  10. #include "base/sampling_heap_profiler/lock_free_address_hash_set.h"
  11. #include "base/synchronization/lock.h"
  12. #include "base/thread_annotations.h"
  13. namespace base {
  14. template <typename T>
  15. class NoDestructor;
  16. // This singleton class implements Poisson sampling of the incoming allocations
  17. // stream. It hooks onto base::allocator and base::PartitionAlloc.
  18. // An extra custom allocator can be hooked via SetHooksInstallCallback method.
  19. // The only control parameter is sampling interval that controls average value
  20. // of the sampling intervals. The actual intervals between samples are
  21. // randomized using Poisson distribution to mitigate patterns in the allocation
  22. // stream.
  23. // Once accumulated allocation sizes fill up the current sample interval,
  24. // a sample is generated and sent to the observers via |SampleAdded| call.
  25. // When the corresponding memory that triggered the sample is freed observers
  26. // get notified with |SampleRemoved| call.
  27. //
  28. class BASE_EXPORT PoissonAllocationSampler {
  29. public:
  30. enum AllocatorType : uint32_t { kMalloc, kPartitionAlloc, kBlinkGC };
  31. class SamplesObserver {
  32. public:
  33. virtual ~SamplesObserver() = default;
  34. virtual void SampleAdded(void* address,
  35. size_t size,
  36. size_t total,
  37. AllocatorType type,
  38. const char* context) = 0;
  39. virtual void SampleRemoved(void* address) = 0;
  40. };
  41. // The instance of this class makes sampler do not report samples generated
  42. // within the object scope for the current thread.
  43. // It allows observers to allocate/deallocate memory while holding a lock
  44. // without a chance to get into reentrancy problems.
  45. // The current implementation doesn't support ScopedMuteThreadSamples nesting.
  46. class BASE_EXPORT ScopedMuteThreadSamples {
  47. public:
  48. ScopedMuteThreadSamples();
  49. ~ScopedMuteThreadSamples();
  50. static bool IsMuted();
  51. };
  52. // Must be called early during the process initialization. It creates and
  53. // reserves a TLS slot.
  54. static void Init();
  55. // This is an entry point for plugging in an external allocator.
  56. // Profiler will invoke the provided callback upon initialization.
  57. // The callback should install hooks onto the corresponding memory allocator
  58. // and make them invoke PoissonAllocationSampler::RecordAlloc and
  59. // PoissonAllocationSampler::RecordFree upon corresponding allocation events.
  60. //
  61. // If the method is called after profiler is initialized, the callback
  62. // is invoked right away.
  63. static void SetHooksInstallCallback(void (*hooks_install_callback)());
  64. void AddSamplesObserver(SamplesObserver*);
  65. // Note: After an observer is removed it is still possible to receive
  66. // a notification to that observer. This is not a problem currently as
  67. // the only client of this interface is the base::SamplingHeapProfiler,
  68. // which is a singleton.
  69. // If there's a need for this functionality in the future, one might
  70. // want to put observers notification loop under a reader-writer lock.
  71. void RemoveSamplesObserver(SamplesObserver*);
  72. void SetSamplingInterval(size_t sampling_interval);
  73. void SuppressRandomnessForTest(bool suppress);
  74. static void RecordAlloc(void* address,
  75. size_t,
  76. AllocatorType,
  77. const char* context);
  78. ALWAYS_INLINE static void RecordFree(void* address);
  79. static PoissonAllocationSampler* Get();
  80. private:
  81. PoissonAllocationSampler();
  82. ~PoissonAllocationSampler() = delete;
  83. static void InstallAllocatorHooksOnce();
  84. static bool InstallAllocatorHooks();
  85. static size_t GetNextSampleInterval(size_t base_interval);
  86. static LockFreeAddressHashSet& sampled_addresses_set();
  87. void DoRecordAlloc(intptr_t accumulated_bytes,
  88. size_t size,
  89. void* address,
  90. AllocatorType type,
  91. const char* context);
  92. void DoRecordFree(void* address);
  93. void BalanceAddressesHashSet();
  94. Lock mutex_;
  95. // The |observers_| list is guarded by |mutex_|, however a copy of it
  96. // is made before invoking the observers (to avoid performing expensive
  97. // operations under the lock) as such the SamplesObservers themselves need
  98. // to be thread-safe and support being invoked racily after
  99. // RemoveSamplesObserver().
  100. std::vector<SamplesObserver*> observers_ GUARDED_BY(mutex_);
  101. static PoissonAllocationSampler* instance_;
  102. friend class NoDestructor<PoissonAllocationSampler>;
  103. friend class SamplingHeapProfilerTest;
  104. friend class ScopedMuteThreadSamples;
  105. DISALLOW_COPY_AND_ASSIGN(PoissonAllocationSampler);
  106. };
  107. // static
  108. ALWAYS_INLINE void PoissonAllocationSampler::RecordFree(void* address) {
  109. if (UNLIKELY(address == nullptr))
  110. return;
  111. if (UNLIKELY(sampled_addresses_set().Contains(address)))
  112. instance_->DoRecordFree(address);
  113. }
  114. } // namespace base
  115. #endif // BASE_SAMPLING_HEAP_PROFILER_POISSON_ALLOCATION_SAMPLER_H_