blob: 7fd1491275f0855b42a0fa267f99f84be1d8a3e2 [file] [log] [blame]
Alexei Filippovd6363e472018-08-27 19:31:391// 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
5#ifndef BASE_SAMPLING_HEAP_PROFILER_POISSON_ALLOCATION_SAMPLER_H_
6#define BASE_SAMPLING_HEAP_PROFILER_POISSON_ALLOCATION_SAMPLER_H_
7
Alexei Filippovd6363e472018-08-27 19:31:398#include <vector>
9
10#include "base/base_export.h"
Alexei Filippov02435742019-06-05 04:43:0111#include "base/compiler_specific.h"
Alexei Filippovd6363e472018-08-27 19:31:3912#include "base/macros.h"
Avi Drissmanded77172021-07-02 18:23:0013#include "base/no_destructor.h"
Alexei Filippov02435742019-06-05 04:43:0114#include "base/sampling_heap_profiler/lock_free_address_hash_set.h"
Alexei Filippovd6363e472018-08-27 19:31:3915#include "base/synchronization/lock.h"
Alexei Filippovf071fd42019-09-11 21:37:3416#include "base/thread_annotations.h"
Alexei Filippovd6363e472018-08-27 19:31:3917
18namespace base {
19
Alexei Filippovd6363e472018-08-27 19:31:3920// This singleton class implements Poisson sampling of the incoming allocations
21// stream. It hooks onto base::allocator and base::PartitionAlloc.
22// An extra custom allocator can be hooked via SetHooksInstallCallback method.
23// The only control parameter is sampling interval that controls average value
24// of the sampling intervals. The actual intervals between samples are
25// randomized using Poisson distribution to mitigate patterns in the allocation
26// stream.
27// Once accumulated allocation sizes fill up the current sample interval,
28// a sample is generated and sent to the observers via |SampleAdded| call.
29// When the corresponding memory that triggered the sample is freed observers
30// get notified with |SampleRemoved| call.
31//
32class BASE_EXPORT PoissonAllocationSampler {
33 public:
Michael Lippautzee0661c2021-02-24 07:41:1134 enum AllocatorType : uint32_t { kMalloc, kPartitionAlloc };
Alexei Filippovd6363e472018-08-27 19:31:3935
36 class SamplesObserver {
37 public:
38 virtual ~SamplesObserver() = default;
39 virtual void SampleAdded(void* address,
40 size_t size,
41 size_t total,
42 AllocatorType type,
43 const char* context) = 0;
44 virtual void SampleRemoved(void* address) = 0;
45 };
46
Alexei Filippov28cc68d2018-09-13 07:58:3647 // The instance of this class makes sampler do 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.
Alexei Filippovda7a05712019-03-01 21:15:2251 // The current implementation doesn't support ScopedMuteThreadSamples nesting.
Alexei Filippovbc379632018-09-14 22:29:0652 class BASE_EXPORT ScopedMuteThreadSamples {
Alexei Filippov28cc68d2018-09-13 07:58:3653 public:
Alexei Filippovbc379632018-09-14 22:29:0654 ScopedMuteThreadSamples();
55 ~ScopedMuteThreadSamples();
Alexei Filippove48985e2019-02-01 00:27:4156
57 static bool IsMuted();
Alexei Filippov28cc68d2018-09-13 07:58:3658 };
59
Alexei Filippovd6363e472018-08-27 19:31:3960 // Must be called early during the process initialization. It creates and
61 // reserves a TLS slot.
62 static void Init();
63
64 // This is an entry point for plugging in an external allocator.
65 // Profiler will invoke the provided callback upon initialization.
66 // The callback should install hooks onto the corresponding memory allocator
67 // and make them invoke PoissonAllocationSampler::RecordAlloc and
68 // PoissonAllocationSampler::RecordFree upon corresponding allocation events.
69 //
70 // If the method is called after profiler is initialized, the callback
71 // is invoked right away.
72 static void SetHooksInstallCallback(void (*hooks_install_callback)());
73
74 void AddSamplesObserver(SamplesObserver*);
Alexei Filippovf071fd42019-09-11 21:37:3475
76 // Note: After an observer is removed it is still possible to receive
77 // a notification to that observer. This is not a problem currently as
78 // the only client of this interface is the base::SamplingHeapProfiler,
79 // which is a singleton.
80 // If there's a need for this functionality in the future, one might
81 // want to put observers notification loop under a reader-writer lock.
Alexei Filippovd6363e472018-08-27 19:31:3982 void RemoveSamplesObserver(SamplesObserver*);
83
Alexei Filippovd6363e472018-08-27 19:31:3984 void SetSamplingInterval(size_t sampling_interval);
85 void SuppressRandomnessForTest(bool suppress);
86
87 static void RecordAlloc(void* address,
88 size_t,
89 AllocatorType,
90 const char* context);
Alexei Filippov02435742019-06-05 04:43:0191 ALWAYS_INLINE static void RecordFree(void* address);
Alexei Filippovd6363e472018-08-27 19:31:3992
93 static PoissonAllocationSampler* Get();
94
Peter Boström75cd3c02021-09-28 15:23:1895 PoissonAllocationSampler(const PoissonAllocationSampler&) = delete;
96 PoissonAllocationSampler& operator=(const PoissonAllocationSampler&) = delete;
97
Alexei Filippovd6363e472018-08-27 19:31:3998 private:
99 PoissonAllocationSampler();
100 ~PoissonAllocationSampler() = delete;
101
102 static void InstallAllocatorHooksOnce();
103 static bool InstallAllocatorHooks();
104 static size_t GetNextSampleInterval(size_t base_interval);
105 static LockFreeAddressHashSet& sampled_addresses_set();
106
Alexei Filippov678cb252018-09-18 01:11:15107 void DoRecordAlloc(intptr_t accumulated_bytes,
108 size_t size,
Alexei Filippovd6363e472018-08-27 19:31:39109 void* address,
110 AllocatorType type,
111 const char* context);
112 void DoRecordFree(void* address);
113
114 void BalanceAddressesHashSet();
115
Alexei Filippovd6363e472018-08-27 19:31:39116 Lock mutex_;
Alexei Filippovf071fd42019-09-11 21:37:34117 // The |observers_| list is guarded by |mutex_|, however a copy of it
118 // is made before invoking the observers (to avoid performing expensive
119 // operations under the lock) as such the SamplesObservers themselves need
120 // to be thread-safe and support being invoked racily after
121 // RemoveSamplesObserver().
122 std::vector<SamplesObserver*> observers_ GUARDED_BY(mutex_);
Alexei Filippovd6363e472018-08-27 19:31:39123
124 static PoissonAllocationSampler* instance_;
125
Alexei Filippovd6363e472018-08-27 19:31:39126 friend class NoDestructor<PoissonAllocationSampler>;
Alexei Filippov678cb252018-09-18 01:11:15127 friend class SamplingHeapProfilerTest;
128 friend class ScopedMuteThreadSamples;
Alexei Filippovd6363e472018-08-27 19:31:39129};
130
Alexei Filippov02435742019-06-05 04:43:01131// static
132ALWAYS_INLINE void PoissonAllocationSampler::RecordFree(void* address) {
133 if (UNLIKELY(address == nullptr))
134 return;
135 if (UNLIKELY(sampled_addresses_set().Contains(address)))
136 instance_->DoRecordFree(address);
137}
138
Alexei Filippovd6363e472018-08-27 19:31:39139} // namespace base
140
141#endif // BASE_SAMPLING_HEAP_PROFILER_POISSON_ALLOCATION_SAMPLER_H_