xref: /aosp_15_r20/external/cronet/base/profiler/stack_sampler.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2015 The Chromium Authors
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_PROFILER_STACK_SAMPLER_H_
6 #define BASE_PROFILER_STACK_SAMPLER_H_
7 
8 #include <memory>
9 #include <vector>
10 
11 #include "base/base_export.h"
12 #include "base/containers/circular_deque.h"
13 #include "base/functional/callback.h"
14 #include "base/memory/raw_ptr.h"
15 #include "base/profiler/frame.h"
16 #include "base/profiler/register_context.h"
17 #include "base/profiler/sampling_profiler_thread_token.h"
18 #include "base/profiler/stack_copier.h"
19 #include "base/profiler/stack_sampler.h"
20 #include "base/threading/platform_thread.h"
21 #include "build/build_config.h"
22 
23 namespace base {
24 
25 class Unwinder;
26 class ModuleCache;
27 class ProfileBuilder;
28 class StackBuffer;
29 class StackSamplerTestDelegate;
30 
31 // StackSampler is an implementation detail of StackSamplingProfiler. It
32 // abstracts the native implementation required to record a set of stack frames
33 // for a given thread. It delegates to StackCopier for the
34 // platform-specific stack copying implementation.
35 class BASE_EXPORT StackSampler {
36  public:
37   // Factory for generating a set of Unwinders for use by the profiler.
38   using UnwindersFactory =
39       OnceCallback<std::vector<std::unique_ptr<Unwinder>>()>;
40 
41   // Creates a stack sampler that records samples for thread with
42   // |thread_token|. Unwinders in |unwinders| must be stored in increasing
43   // priority to guide unwind attempts. Only the unwinder with the lowest
44   // priority is allowed to return with UnwindResult::kCompleted. Returns null
45   // if this platform does not support stack sampling.
46   static std::unique_ptr<StackSampler> Create(
47       SamplingProfilerThreadToken thread_token,
48       ModuleCache* module_cache,
49       UnwindersFactory core_unwinders_factory,
50       RepeatingClosure record_sample_callback,
51       StackSamplerTestDelegate* test_delegate);
52 
53   ~StackSampler();
54 
55   StackSampler(const StackSampler&) = delete;
56   StackSampler& operator=(const StackSampler&) = delete;
57 
58   // Gets the required size of the stack buffer.
59   static size_t GetStackBufferSize();
60 
61   // Creates an instance of the a stack buffer that can be used for calls to
62   // any StackSampler object.
63   static std::unique_ptr<StackBuffer> CreateStackBuffer();
64 
65   // The following functions are all called on the SamplingThread (not the
66   // thread being sampled).
67 
68   // Performs post-construction initialization on the SamplingThread.
69   void Initialize();
70 
71   // Adds an auxiliary unwinder to handle additional, non-native-code unwind
72   // scenarios. Unwinders must be inserted in increasing priority, following
73   // |unwinders| provided in Create(), to guide unwind attempts.
74   void AddAuxUnwinder(std::unique_ptr<Unwinder> unwinder);
75 
76   // Records a set of frames and returns them.
77   void RecordStackFrames(StackBuffer* stack_buffer,
78                          ProfileBuilder* profile_builder,
79                          PlatformThreadId thread_id);
80 
81   // Exposes the internal function for unit testing.
82   static std::vector<Frame> WalkStackForTesting(
83       ModuleCache* module_cache,
84       RegisterContext* thread_context,
85       uintptr_t stack_top,
86       const base::circular_deque<std::unique_ptr<Unwinder>>& unwinders);
87 
88   // Create a StackSampler, overriding the platform-specific components.
89   static std::unique_ptr<StackSampler> CreateForTesting(
90       std::unique_ptr<StackCopier> stack_copier,
91       UnwindersFactory core_unwinders_factory,
92       ModuleCache* module_cache,
93       RepeatingClosure record_sample_callback = RepeatingClosure(),
94       StackSamplerTestDelegate* test_delegate = nullptr);
95 
96 #if BUILDFLAG(IS_CHROMEOS)
97   // How often to record the "Memory.StackSamplingProfiler.StackSampleSize2" UMA
98   // histogram. Specifically, only 1 in kUMAHistogramDownsampleAmount calls to
99   // RecordStackFrames will add a sample to the histogram. RecordStackFrames is
100   // called many times a second. We don't need multiple samples per second to
101   // get a good understanding of average stack sizes, and it's a lot of data to
102   // record. kUMAHistogramDownsampleAmount should give us about 1 sample per 10
103   // seconds per process, which is plenty. 199 is prime which should avoid any
104   // aliasing issues (e.g. if stacks are larger on second boundaries or some
105   // such weirdness).
106   static constexpr uint32_t kUMAHistogramDownsampleAmount = 199;
107 #endif
108 
109  private:
110   StackSampler(std::unique_ptr<StackCopier> stack_copier,
111                UnwindersFactory core_unwinders_factory,
112                ModuleCache* module_cache,
113                RepeatingClosure record_sample_callback,
114                StackSamplerTestDelegate* test_delegate);
115 
116   static std::vector<Frame> WalkStack(
117       ModuleCache* module_cache,
118       RegisterContext* thread_context,
119       uintptr_t stack_top,
120       const base::circular_deque<std::unique_ptr<Unwinder>>& unwinders);
121 
122   const std::unique_ptr<StackCopier> stack_copier_;
123   UnwindersFactory unwinders_factory_;
124 
125   // Unwinders are stored in decreasing priority order.
126   base::circular_deque<std::unique_ptr<Unwinder>> unwinders_;
127 
128   const raw_ptr<ModuleCache> module_cache_;
129   const RepeatingClosure record_sample_callback_;
130   const raw_ptr<StackSamplerTestDelegate> test_delegate_;
131 
132 #if BUILDFLAG(IS_CHROMEOS)
133   // Counter for "Memory.StackSamplingProfiler.StackSampleSize2" UMA histogram.
134   // See comments above kUMAHistogramDownsampleAmount. Unsigned so that overflow
135   // isn't undefined behavior.
136   uint32_t stack_size_histogram_sampling_counter_ = 0;
137 #endif
138 
139   // True if ownership of the object has been passed to the profiling thread and
140   // initialization has occurred there. If that's the case then any further aux
141   // unwinder that's provided needs to be set up within AddAuxUnwinder().
142   bool was_initialized_ = false;
143 };
144 
145 // StackSamplerTestDelegate provides seams for test code to execute during stack
146 // collection.
147 class BASE_EXPORT StackSamplerTestDelegate {
148  public:
149   StackSamplerTestDelegate(const StackSamplerTestDelegate&) = delete;
150   StackSamplerTestDelegate& operator=(const StackSamplerTestDelegate&) = delete;
151 
152   virtual ~StackSamplerTestDelegate();
153 
154   // Called after copying the stack and resuming the target thread, but prior to
155   // walking the stack. Invoked on the SamplingThread.
156   virtual void OnPreStackWalk() = 0;
157 
158  protected:
159   StackSamplerTestDelegate();
160 };
161 
162 }  // namespace base
163 
164 #endif  // BASE_PROFILER_STACK_SAMPLER_H_
165