xref: /aosp_15_r20/external/perfetto/src/profiling/memory/sampler.h (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker /*
2*6dbdd20aSAndroid Build Coastguard Worker  * Copyright (C) 2018 The Android Open Source Project
3*6dbdd20aSAndroid Build Coastguard Worker  *
4*6dbdd20aSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*6dbdd20aSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*6dbdd20aSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*6dbdd20aSAndroid Build Coastguard Worker  *
8*6dbdd20aSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*6dbdd20aSAndroid Build Coastguard Worker  *
10*6dbdd20aSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*6dbdd20aSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*6dbdd20aSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6dbdd20aSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*6dbdd20aSAndroid Build Coastguard Worker  * limitations under the License.
15*6dbdd20aSAndroid Build Coastguard Worker  */
16*6dbdd20aSAndroid Build Coastguard Worker 
17*6dbdd20aSAndroid Build Coastguard Worker #ifndef SRC_PROFILING_MEMORY_SAMPLER_H_
18*6dbdd20aSAndroid Build Coastguard Worker #define SRC_PROFILING_MEMORY_SAMPLER_H_
19*6dbdd20aSAndroid Build Coastguard Worker 
20*6dbdd20aSAndroid Build Coastguard Worker #include <stdint.h>
21*6dbdd20aSAndroid Build Coastguard Worker 
22*6dbdd20aSAndroid Build Coastguard Worker #include <atomic>
23*6dbdd20aSAndroid Build Coastguard Worker #include <random>
24*6dbdd20aSAndroid Build Coastguard Worker 
25*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/utils.h"
26*6dbdd20aSAndroid Build Coastguard Worker 
27*6dbdd20aSAndroid Build Coastguard Worker namespace perfetto {
28*6dbdd20aSAndroid Build Coastguard Worker namespace profiling {
29*6dbdd20aSAndroid Build Coastguard Worker 
30*6dbdd20aSAndroid Build Coastguard Worker constexpr uint64_t kSamplerSeed = 1;
31*6dbdd20aSAndroid Build Coastguard Worker 
32*6dbdd20aSAndroid Build Coastguard Worker std::default_random_engine& GetGlobalRandomEngineLocked();
33*6dbdd20aSAndroid Build Coastguard Worker 
34*6dbdd20aSAndroid Build Coastguard Worker // Poisson sampler for memory allocations. We apply sampling individually to
35*6dbdd20aSAndroid Build Coastguard Worker // each byte. The whole allocation gets accounted as often as the number of
36*6dbdd20aSAndroid Build Coastguard Worker // sampled bytes it contains.
37*6dbdd20aSAndroid Build Coastguard Worker //
38*6dbdd20aSAndroid Build Coastguard Worker // The algorithm is inspired by the Chromium sampling algorithm at
39*6dbdd20aSAndroid Build Coastguard Worker // https://cs.chromium.org/search/?q=f:cc+symbol:AllocatorShimLogAlloc+package:%5Echromium$&type=cs
40*6dbdd20aSAndroid Build Coastguard Worker // Googlers: see go/chrome-shp for more details.
41*6dbdd20aSAndroid Build Coastguard Worker //
42*6dbdd20aSAndroid Build Coastguard Worker // NB: not thread-safe, requires external synchronization.
43*6dbdd20aSAndroid Build Coastguard Worker class Sampler {
44*6dbdd20aSAndroid Build Coastguard Worker  public:
SetSamplingInterval(uint64_t sampling_interval)45*6dbdd20aSAndroid Build Coastguard Worker   void SetSamplingInterval(uint64_t sampling_interval) {
46*6dbdd20aSAndroid Build Coastguard Worker     sampling_interval_ = sampling_interval;
47*6dbdd20aSAndroid Build Coastguard Worker     sampling_rate_ = 1.0 / static_cast<double>(sampling_interval_);
48*6dbdd20aSAndroid Build Coastguard Worker     interval_to_next_sample_ = NextSampleInterval();
49*6dbdd20aSAndroid Build Coastguard Worker   }
50*6dbdd20aSAndroid Build Coastguard Worker 
51*6dbdd20aSAndroid Build Coastguard Worker   // Returns number of bytes that should be be attributed to the sample.
52*6dbdd20aSAndroid Build Coastguard Worker   // If returned size is 0, the allocation should not be sampled.
53*6dbdd20aSAndroid Build Coastguard Worker   //
54*6dbdd20aSAndroid Build Coastguard Worker   // Due to how the poission sampling works, some samples should be accounted
55*6dbdd20aSAndroid Build Coastguard Worker   // multiple times.
SampleSize(size_t alloc_sz)56*6dbdd20aSAndroid Build Coastguard Worker   size_t SampleSize(size_t alloc_sz) {
57*6dbdd20aSAndroid Build Coastguard Worker     if (PERFETTO_UNLIKELY(alloc_sz >= sampling_interval_))
58*6dbdd20aSAndroid Build Coastguard Worker       return alloc_sz;
59*6dbdd20aSAndroid Build Coastguard Worker     return static_cast<size_t>(sampling_interval_ * NumberOfSamples(alloc_sz));
60*6dbdd20aSAndroid Build Coastguard Worker   }
61*6dbdd20aSAndroid Build Coastguard Worker 
sampling_interval()62*6dbdd20aSAndroid Build Coastguard Worker   uint64_t sampling_interval() const { return sampling_interval_; }
63*6dbdd20aSAndroid Build Coastguard Worker 
64*6dbdd20aSAndroid Build Coastguard Worker  private:
NextSampleInterval()65*6dbdd20aSAndroid Build Coastguard Worker   int64_t NextSampleInterval() {
66*6dbdd20aSAndroid Build Coastguard Worker     std::exponential_distribution<double> dist(sampling_rate_);
67*6dbdd20aSAndroid Build Coastguard Worker     int64_t next = static_cast<int64_t>(dist(GetGlobalRandomEngineLocked()));
68*6dbdd20aSAndroid Build Coastguard Worker     // We approximate the geometric distribution using an exponential
69*6dbdd20aSAndroid Build Coastguard Worker     // distribution.
70*6dbdd20aSAndroid Build Coastguard Worker     // We need to add 1 because that gives us the number of failures before
71*6dbdd20aSAndroid Build Coastguard Worker     // the next success, while our interval includes the next success.
72*6dbdd20aSAndroid Build Coastguard Worker     return next + 1;
73*6dbdd20aSAndroid Build Coastguard Worker   }
74*6dbdd20aSAndroid Build Coastguard Worker 
75*6dbdd20aSAndroid Build Coastguard Worker   // Returns number of times a sample should be accounted. Due to how the
76*6dbdd20aSAndroid Build Coastguard Worker   // poission sampling works, some samples should be accounted multiple times.
NumberOfSamples(size_t alloc_sz)77*6dbdd20aSAndroid Build Coastguard Worker   size_t NumberOfSamples(size_t alloc_sz) {
78*6dbdd20aSAndroid Build Coastguard Worker     interval_to_next_sample_ -= alloc_sz;
79*6dbdd20aSAndroid Build Coastguard Worker     size_t num_samples = 0;
80*6dbdd20aSAndroid Build Coastguard Worker     while (PERFETTO_UNLIKELY(interval_to_next_sample_ <= 0)) {
81*6dbdd20aSAndroid Build Coastguard Worker       interval_to_next_sample_ += NextSampleInterval();
82*6dbdd20aSAndroid Build Coastguard Worker       ++num_samples;
83*6dbdd20aSAndroid Build Coastguard Worker     }
84*6dbdd20aSAndroid Build Coastguard Worker     return num_samples;
85*6dbdd20aSAndroid Build Coastguard Worker   }
86*6dbdd20aSAndroid Build Coastguard Worker 
87*6dbdd20aSAndroid Build Coastguard Worker   uint64_t sampling_interval_;
88*6dbdd20aSAndroid Build Coastguard Worker   double sampling_rate_;
89*6dbdd20aSAndroid Build Coastguard Worker   int64_t interval_to_next_sample_;
90*6dbdd20aSAndroid Build Coastguard Worker };
91*6dbdd20aSAndroid Build Coastguard Worker 
92*6dbdd20aSAndroid Build Coastguard Worker }  // namespace profiling
93*6dbdd20aSAndroid Build Coastguard Worker }  // namespace perfetto
94*6dbdd20aSAndroid Build Coastguard Worker 
95*6dbdd20aSAndroid Build Coastguard Worker #endif  // SRC_PROFILING_MEMORY_SAMPLER_H_
96