Flutter Engine
The Flutter Engine
sampler.h
Go to the documentation of this file.
1// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#ifndef RUNTIME_VM_HEAP_SAMPLER_H_
6#define RUNTIME_VM_HEAP_SAMPLER_H_
7
8#if !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
9
10#include <atomic>
11
12#include "include/dart_api.h"
13#include "vm/allocation.h"
14#include "vm/globals.h"
15
16namespace dart {
17
18// Forward declarations.
19class RwLock;
20class Thread;
21
22// Poisson sampler for memory allocations. We apply sampling individually to
23// each byte. The whole allocation gets accounted as often as the number of
24// sampled bytes it contains.
25//
26// Heavily inspired by Perfetto's Sampler class:
27// https://source.chromium.org/chromium/chromium/src/+/531db6ec90bd7194d4d8064588966a0118d1495c:third_party/perfetto/src/profiling/memory/sampler.h;l=34
29 public:
30 explicit HeapProfileSampler(Thread* thread);
31
32 // Enables or disables heap profiling for all threads.
33 //
34 // NOTE: the enabled state will update on a thread-by-thread basis once
35 // the thread performs an interrupt check. There is no guarantee that the
36 // enabled state will be updated for each thread by the time this method
37 // returns.
38 static void Enable(bool enabled);
39 static bool enabled() { return enabled_; }
40
41 // Updates the heap profiling sampling interval for all threads.
42 //
43 // NOTE: the sampling interval will update on a thread-by-thread basis once
44 // the thread performs an interrupt check. There is no guarantee that the
45 // sampling interval will be updated for each thread by the time this method
46 // returns.
47 static void SetSamplingInterval(intptr_t bytes_interval);
48
49 // Updates the callback that's invoked when a sample is collected.
50 static void SetSamplingCallback(
51 Dart_HeapSamplingCreateCallback create_callback,
53
55 return delete_callback_;
56 }
57
58 void Initialize();
59 void Cleanup() {
60 ResetState();
61 last_sample_size_ = kUninitialized;
62 }
63
64 // Notifies the thread that it needs to update the enabled state of the heap
65 // sampling profiler.
66 //
67 // This method is safe to call from any thread.
69
70 // Returns true if [ScheduleUpdateThreadEnable()] has been invoked.
71 //
72 // Calling this method will clear the state set by
73 // [ScheduleUpdateThreadEnable()].
74 //
75 // This method is safe to call from any thread.
77 return schedule_thread_enable_.exchange(false);
78 }
79
80 // Updates the enabled state of the thread's heap sampling profiler.
81 //
82 // WARNING: This method can only be called by the thread associated with this
83 // profiler instance to avoid concurrent modification of the thread's TLAB.
84 void UpdateThreadEnable();
85
86 // Notifies the thread that it needs to update the sampling interval of its
87 // heap sampling profiler.
88 //
89 // This method is safe to call from any thread.
91
92 // Returns true if [ScheduleSetThreadSamplingInterval()] has been invoked.
93 //
94 // Calling this method will clear the state set by
95 // [ScheduleSetThreadEnable()].
96 //
97 // This method is safe to call from any thread.
99 return schedule_thread_set_sampling_interval_.exchange(false);
100 }
101
102 // Updates the sampling interval of the thread's heap sampling profiler.
103 //
104 // WARNING: This method can only be called by the thread associated with this
105 // profiler instance to avoid concurrent modification of the thread's TLAB.
107
108 // Updates internal book keeping tracking the remaining size of the sampling
109 // interval. This method must be called when a TLAB is torn down to ensure
110 // that a future TLAB is initialized with the correct sampling interval.
111 void HandleReleasedTLAB(Thread* thread);
112
113 // Handles the creation of a new TLAB by updating its boundaries based on the
114 // remaining sampling interval.
115 //
116 // is_first_tlab should be set to true if this is the first TLAB associated
117 // with thread_ in order to correctly set the TLAB boundaries to match the
118 // remaining sampling interval that's been used to keep track of old space
119 // allocations.
120 void HandleNewTLAB(intptr_t old_tlab_remaining_space, bool is_first_tlab);
121
122 void* InvokeCallbackForLastSample(intptr_t cid);
123
124 bool HasOutstandingSample() const {
125 return last_sample_size_ != kUninitialized;
126 }
127
128 void SampleNewSpaceAllocation(intptr_t allocation_size);
129 void SampleOldSpaceAllocation(intptr_t allocation_size);
130
131 private:
132 void ResetState();
133 void ResetIntervalState() { interval_to_next_sample_ = kUninitialized; }
134
135 void UpdateThreadEnableLocked();
136
137 void SetThreadSamplingIntervalLocked();
138 void SetNextSamplingIntervalLocked(intptr_t next_interval);
139
140 intptr_t GetNextSamplingIntervalLocked();
141 intptr_t NumberOfSamplesLocked(intptr_t allocation_size);
142
143 // Helper to calculate the remaining sampling interval based on TLAB
144 // boundaries. Returns kUninitialized if there's no active TLAB.
145 intptr_t remaining_TLAB_interval() const;
146
147 std::atomic<bool> schedule_thread_enable_ = false;
148 std::atomic<bool> schedule_thread_set_sampling_interval_ = false;
149
150 // Protects sampling logic from modifications of callback_, sampling_interval,
151 // and enabled_ while collecting a sample.
152 //
153 // This lock should be acquired using WriteRwLocker when modifying static
154 // state, and should be acquired using ReadRwLocker when accessing static
155 // state from instances of HeapProfileSampler.
156 static RwLock* lock_;
157 static bool enabled_;
158 static Dart_HeapSamplingCreateCallback create_callback_;
159 static Dart_HeapSamplingDeleteCallback delete_callback_;
160 static intptr_t sampling_interval_;
161
162 static constexpr intptr_t kUninitialized = -1;
163 static constexpr intptr_t kDefaultSamplingInterval = 512 * KB;
164
165 bool thread_enabled_ = false;
166 intptr_t interval_to_next_sample_ = kUninitialized;
167 intptr_t next_tlab_offset_ = kUninitialized;
168 intptr_t last_sample_size_ = kUninitialized;
169
170 Thread* thread_;
171
172 DISALLOW_COPY_AND_ASSIGN(HeapProfileSampler);
173};
174
175} // namespace dart
176
177#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
178#endif // RUNTIME_VM_HEAP_SAMPLER_H_
static void SetSamplingCallback(Dart_HeapSamplingCreateCallback create_callback, Dart_HeapSamplingDeleteCallback delete_callback)
Definition: sampler.cc:81
HeapProfileSampler(Thread *thread)
Definition: sampler.cc:44
void SetThreadSamplingInterval()
Definition: sampler.cc:136
void HandleNewTLAB(intptr_t old_tlab_remaining_space, bool is_first_tlab)
Definition: sampler.cc:162
void ScheduleUpdateThreadEnable()
Definition: sampler.cc:109
void HandleReleasedTLAB(Thread *thread)
Definition: sampler.cc:153
static void SetSamplingInterval(intptr_t bytes_interval)
Definition: sampler.cc:60
void ScheduleSetThreadSamplingInterval()
Definition: sampler.cc:131
static bool enabled()
Definition: sampler.h:39
bool ShouldSetThreadSamplingInterval()
Definition: sampler.h:98
bool ShouldUpdateThreadEnable()
Definition: sampler.h:76
static Dart_HeapSamplingDeleteCallback delete_callback()
Definition: sampler.h:54
bool HasOutstandingSample() const
Definition: sampler.h:124
static void Enable(bool enabled)
Definition: sampler.cc:47
void SampleNewSpaceAllocation(intptr_t allocation_size)
Definition: sampler.cc:213
void SampleOldSpaceAllocation(intptr_t allocation_size)
Definition: sampler.cc:237
void * InvokeCallbackForLastSample(intptr_t cid)
Definition: sampler.cc:200
void *(* Dart_HeapSamplingCreateCallback)(Dart_Isolate isolate, Dart_IsolateGroup isolate_group, const char *cls_name, intptr_t allocation_size)
Definition: dart_api.h:1283
void(* Dart_HeapSamplingDeleteCallback)(void *data)
Definition: dart_api.h:1288
Definition: dart_vm.cc:33
constexpr intptr_t KB
Definition: globals.h:528
const intptr_t cid