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 #include "base/profiler/stack_sampler.h"
6
7 #include <iterator>
8 #include <utility>
9
10 #include "base/check.h"
11 #include "base/compiler_specific.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/memory/raw_ptr.h"
14 #include "base/metrics/histogram_functions.h"
15 #include "base/numerics/safe_conversions.h"
16 #include "base/profiler/metadata_recorder.h"
17 #include "base/profiler/profile_builder.h"
18 #include "base/profiler/sample_metadata.h"
19 #include "base/profiler/stack_buffer.h"
20 #include "base/profiler/stack_copier.h"
21 #include "base/profiler/suspendable_thread_delegate.h"
22 #include "base/profiler/unwinder.h"
23 #include "base/ranges/algorithm.h"
24
25 // IMPORTANT NOTE: Some functions within this implementation are invoked while
26 // the target thread is suspended so it must not do any allocation from the
27 // heap, including indirectly via use of DCHECK/CHECK or other logging
28 // statements. Otherwise this code can deadlock on heap locks acquired by the
29 // target thread before it was suspended. These functions are commented with "NO
30 // HEAP ALLOCATIONS".
31
32 namespace base {
33
34 namespace {
35
36 // Notifies the unwinders about the stack capture, and records metadata, while
37 // the thread is suspended.
38 class StackCopierDelegate : public StackCopier::Delegate {
39 public:
StackCopierDelegate(const base::circular_deque<std::unique_ptr<Unwinder>> * unwinders,ProfileBuilder * profile_builder,MetadataRecorder::MetadataProvider * metadata_provider)40 StackCopierDelegate(
41 const base::circular_deque<std::unique_ptr<Unwinder>>* unwinders,
42 ProfileBuilder* profile_builder,
43 MetadataRecorder::MetadataProvider* metadata_provider)
44 : unwinders_(unwinders),
45 profile_builder_(profile_builder),
46 metadata_provider_(metadata_provider) {}
47
48 StackCopierDelegate(const StackCopierDelegate&) = delete;
49 StackCopierDelegate& operator=(const StackCopierDelegate&) = delete;
50
51 // StackCopier::Delegate:
52 // IMPORTANT NOTE: to avoid deadlock this function must not invoke any
53 // non-reentrant code that is also invoked by the target thread. In
54 // particular, it may not perform any heap allocation or deallocation,
55 // including indirectly via use of DCHECK/CHECK or other logging statements.
OnStackCopy()56 void OnStackCopy() override {
57 for (const auto& unwinder : *unwinders_)
58 unwinder->OnStackCapture();
59
60 profile_builder_->RecordMetadata(*metadata_provider_);
61 }
62
63 private:
64 raw_ptr<const base::circular_deque<std::unique_ptr<Unwinder>>> unwinders_;
65 const raw_ptr<ProfileBuilder> profile_builder_;
66 const raw_ptr<const MetadataRecorder::MetadataProvider> metadata_provider_;
67 };
68
69 } // namespace
70
71 StackSampler::~StackSampler() = default;
72
CreateStackBuffer()73 std::unique_ptr<StackBuffer> StackSampler::CreateStackBuffer() {
74 size_t size = GetStackBufferSize();
75 if (size == 0)
76 return nullptr;
77 return std::make_unique<StackBuffer>(size);
78 }
79
Initialize()80 void StackSampler::Initialize() {
81 std::vector<std::unique_ptr<Unwinder>> unwinders =
82 std::move(unwinders_factory_).Run();
83
84 // |unwinders| is iterated backward since |unwinders_factory_| generates
85 // unwinders in increasing priority order. |unwinders_| is stored in
86 // decreasing priority order for ease of use within the class.
87 unwinders_.insert(unwinders_.end(),
88 std::make_move_iterator(unwinders.rbegin()),
89 std::make_move_iterator(unwinders.rend()));
90
91 for (const auto& unwinder : unwinders_)
92 unwinder->Initialize(module_cache_);
93
94 was_initialized_ = true;
95 }
96
AddAuxUnwinder(std::unique_ptr<Unwinder> unwinder)97 void StackSampler::AddAuxUnwinder(std::unique_ptr<Unwinder> unwinder) {
98 // Initialize() invokes Initialize() on the unwinders that are present
99 // at the time. If it hasn't occurred yet, we allow it to add the initial
100 // modules, otherwise we do it here.
101 if (was_initialized_)
102 unwinder->Initialize(module_cache_);
103 unwinders_.push_front(std::move(unwinder));
104 }
105
RecordStackFrames(StackBuffer * stack_buffer,ProfileBuilder * profile_builder,PlatformThreadId thread_id)106 void StackSampler::RecordStackFrames(StackBuffer* stack_buffer,
107 ProfileBuilder* profile_builder,
108 PlatformThreadId thread_id) {
109 DCHECK(stack_buffer);
110
111 if (record_sample_callback_)
112 record_sample_callback_.Run();
113
114 RegisterContext thread_context;
115 uintptr_t stack_top;
116 TimeTicks timestamp;
117
118 bool copy_stack_succeeded;
119 {
120 // Make this scope as small as possible because |metadata_provider| is
121 // holding a lock.
122 MetadataRecorder::MetadataProvider metadata_provider(
123 GetSampleMetadataRecorder(), thread_id);
124 StackCopierDelegate delegate(&unwinders_, profile_builder,
125 &metadata_provider);
126 copy_stack_succeeded = stack_copier_->CopyStack(
127 stack_buffer, &stack_top, ×tamp, &thread_context, &delegate);
128 }
129 if (!copy_stack_succeeded) {
130 profile_builder->OnSampleCompleted(
131 {}, timestamp.is_null() ? TimeTicks::Now() : timestamp);
132 return;
133 }
134
135 for (const auto& unwinder : unwinders_)
136 unwinder->UpdateModules();
137
138 if (test_delegate_)
139 test_delegate_->OnPreStackWalk();
140
141 profile_builder->OnSampleCompleted(
142 WalkStack(module_cache_, &thread_context, stack_top, unwinders_),
143 timestamp);
144
145 #if BUILDFLAG(IS_CHROMEOS)
146 ptrdiff_t stack_size = reinterpret_cast<uint8_t*>(stack_top) -
147 reinterpret_cast<uint8_t*>(stack_buffer->buffer());
148 constexpr int kBytesPerKilobyte = 1024;
149
150 if ((++stack_size_histogram_sampling_counter_ %
151 kUMAHistogramDownsampleAmount) == 0) {
152 // Record the size of the stack to tune kLargeStackSize.
153 // UmaHistogramMemoryKB has a min of 1000, which isn't useful for our
154 // purposes, so call UmaHistogramCustomCounts directly.
155 // Min is 4KB, since that's the normal pagesize and setting kLargeStackSize
156 // smaller than that would be pointless. Max is 8MB since that's the
157 // current ChromeOS stack size; we shouldn't be able to get a number
158 // larger than that.
159 UmaHistogramCustomCounts(
160 "Memory.StackSamplingProfiler.StackSampleSize2",
161 saturated_cast<int>(stack_size / kBytesPerKilobyte), 4, 8 * 1024, 50);
162 }
163
164 // We expect to very rarely see stacks larger than kLargeStackSize. If we see
165 // a stack larger than kLargeStackSize, we tell the kernel to discard the
166 // contents of the buffer (using madvise(MADV_DONTNEED)) after the first
167 // kLargeStackSize bytes to avoid permanently allocating memory that we won't
168 // use again. We don't want kLargeStackSize to be too small, however; for if
169 // we are constantly calling madvise(MADV_DONTNEED) and then writing to the
170 // same parts of the buffer, we're not saving memory and we'll cause extra
171 // page faults.
172 constexpr ptrdiff_t kLargeStackSize = 32 * kBytesPerKilobyte;
173 if (stack_size > kLargeStackSize) {
174 stack_buffer->MarkUpperBufferContentsAsUnneeded(kLargeStackSize);
175 }
176 #endif // #if BUILDFLAG(IS_CHROMEOS)
177 }
178
179 // static
WalkStackForTesting(ModuleCache * module_cache,RegisterContext * thread_context,uintptr_t stack_top,const base::circular_deque<std::unique_ptr<Unwinder>> & unwinders)180 std::vector<Frame> StackSampler::WalkStackForTesting(
181 ModuleCache* module_cache,
182 RegisterContext* thread_context,
183 uintptr_t stack_top,
184 const base::circular_deque<std::unique_ptr<Unwinder>>& unwinders) {
185 return WalkStack(module_cache, thread_context, stack_top, unwinders);
186 }
187
188 // static
CreateForTesting(std::unique_ptr<StackCopier> stack_copier,UnwindersFactory core_unwinders_factory,ModuleCache * module_cache,RepeatingClosure record_sample_callback,StackSamplerTestDelegate * test_delegate)189 std::unique_ptr<StackSampler> StackSampler::CreateForTesting(
190 std::unique_ptr<StackCopier> stack_copier,
191 UnwindersFactory core_unwinders_factory,
192 ModuleCache* module_cache,
193 RepeatingClosure record_sample_callback,
194 StackSamplerTestDelegate* test_delegate) {
195 return base::WrapUnique(new StackSampler(
196 std::move(stack_copier), std::move(core_unwinders_factory), module_cache,
197 record_sample_callback, test_delegate));
198 }
199
StackSampler(std::unique_ptr<StackCopier> stack_copier,UnwindersFactory core_unwinders_factory,ModuleCache * module_cache,RepeatingClosure record_sample_callback,StackSamplerTestDelegate * test_delegate)200 StackSampler::StackSampler(std::unique_ptr<StackCopier> stack_copier,
201 UnwindersFactory core_unwinders_factory,
202 ModuleCache* module_cache,
203 RepeatingClosure record_sample_callback,
204 StackSamplerTestDelegate* test_delegate)
205 : stack_copier_(std::move(stack_copier)),
206 unwinders_factory_(std::move(core_unwinders_factory)),
207 module_cache_(module_cache),
208 record_sample_callback_(std::move(record_sample_callback)),
209 test_delegate_(test_delegate) {
210 CHECK(unwinders_factory_);
211 }
212
213 // static
WalkStack(ModuleCache * module_cache,RegisterContext * thread_context,uintptr_t stack_top,const base::circular_deque<std::unique_ptr<Unwinder>> & unwinders)214 std::vector<Frame> StackSampler::WalkStack(
215 ModuleCache* module_cache,
216 RegisterContext* thread_context,
217 uintptr_t stack_top,
218 const base::circular_deque<std::unique_ptr<Unwinder>>& unwinders) {
219 std::vector<Frame> stack;
220 // Reserve enough memory for most stacks, to avoid repeated
221 // allocations. Approximately 99.9% of recorded stacks are 128 frames or
222 // fewer.
223 stack.reserve(128);
224
225 // Record the first frame from the context values.
226 stack.emplace_back(RegisterContextInstructionPointer(thread_context),
227 module_cache->GetModuleForAddress(
228 RegisterContextInstructionPointer(thread_context)));
229
230 size_t prior_stack_size;
231 UnwindResult result;
232 do {
233 // Choose an authoritative unwinder for the current module. Use the first
234 // unwinder that thinks it can unwind from the current frame.
235 auto unwinder = ranges::find_if(
236 unwinders, [&stack](const std::unique_ptr<Unwinder>& unwinder) {
237 return unwinder->CanUnwindFrom(stack.back());
238 });
239 if (unwinder == unwinders.end())
240 return stack;
241
242 prior_stack_size = stack.size();
243 result = unwinder->get()->TryUnwind(thread_context, stack_top, &stack);
244
245 // The unwinder with the lowest priority should be the only one that returns
246 // COMPLETED since the stack starts in native code.
247 DCHECK(result != UnwindResult::kCompleted ||
248 unwinder->get() == unwinders.back().get());
249 } while (result != UnwindResult::kAborted &&
250 result != UnwindResult::kCompleted &&
251 // Give up if the authoritative unwinder for the module was unable to
252 // unwind.
253 stack.size() > prior_stack_size);
254
255 return stack;
256 }
257
258 StackSamplerTestDelegate::~StackSamplerTestDelegate() = default;
259
260 StackSamplerTestDelegate::StackSamplerTestDelegate() = default;
261
262 } // namespace base
263