xref: /aosp_15_r20/external/perfetto/src/profiling/perf/unwinding.h (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef SRC_PROFILING_PERF_UNWINDING_H_
18 #define SRC_PROFILING_PERF_UNWINDING_H_
19 
20 #include <stdint.h>
21 #include <condition_variable>
22 #include <map>
23 #include <optional>
24 #include <thread>
25 
26 #include <linux/perf_event.h>
27 #include <unwindstack/Error.h>
28 
29 #include "perfetto/base/flat_set.h"
30 #include "perfetto/base/logging.h"
31 #include "perfetto/ext/base/thread_checker.h"
32 #include "perfetto/ext/base/unix_task_runner.h"
33 #include "perfetto/ext/tracing/core/basic_types.h"
34 #include "src/kallsyms/kernel_symbol_map.h"
35 #include "src/kallsyms/lazy_kernel_symbolizer.h"
36 #include "src/profiling/common/unwind_support.h"
37 #include "src/profiling/perf/common_types.h"
38 #include "src/profiling/perf/unwind_queue.h"
39 
40 namespace perfetto {
41 namespace profiling {
42 
43 constexpr static uint32_t kUnwindQueueCapacity = 1024;
44 
45 // Unwinds and symbolises callstacks. For userspace this uses the sampled stack
46 // and register state (see |ParsedSample|). For kernelspace, the kernel itself
47 // unwinds the stack (recording a list of instruction pointers), so only
48 // symbolisation using /proc/kallsyms is necessary. Has a single unwinding ring
49 // queue, shared across all data sources.
50 //
51 // Userspace samples cannot be unwound without having /proc/<pid>/{maps,mem}
52 // file descriptors for that process. This lookup can be asynchronous (e.g. on
53 // Android), so the unwinder might have to wait before it can process (or
54 // discard) some of the enqueued samples. To avoid blocking the entire queue,
55 // the unwinder is allowed to process the entries out of order.
56 //
57 // Besides the queue, all interactions between the unwinder and the rest of the
58 // producer logic are through posted tasks.
59 //
60 // As unwinding times are long-tailed (example measurements: median <1ms,
61 // worst-case ~1000ms), the unwinder runs on a dedicated thread to avoid
62 // starving the rest of the producer's work (including IPC and consumption of
63 // records from the kernel ring buffers).
64 //
65 // This class should not be instantiated directly, use the |UnwinderHandle|
66 // below instead.
67 //
68 // TODO(rsavitski): while the inputs to the unwinder are batched as a result of
69 // the reader posting a wakeup only after consuming a batch of kernel samples,
70 // the Unwinder might be staggering wakeups for the producer thread by posting a
71 // task every time a sample has been unwound. Evaluate how bad these wakeups are
72 // in practice, and consider also implementing a batching strategy for the
73 // unwinder->serialization handoff (which isn't very latency-sensitive).
74 class Unwinder {
75  public:
76   friend class UnwinderHandle;
77 
78   enum class UnwindMode { kUnwindStack, kFramePointer };
79 
80   // Callbacks from the unwinder to the primary producer thread.
81   class Delegate {
82    public:
83     virtual void PostEmitSample(DataSourceInstanceID ds_id,
84                                 CompletedSample sample) = 0;
85     virtual void PostEmitUnwinderSkippedSample(DataSourceInstanceID ds_id,
86                                                ParsedSample sample) = 0;
87     virtual void PostFinishDataSourceStop(DataSourceInstanceID ds_id) = 0;
88 
89     virtual ~Delegate();
90   };
91 
~Unwinder()92   ~Unwinder() { PERFETTO_DCHECK_THREAD(thread_checker_); }
93 
94   void PostStartDataSource(DataSourceInstanceID ds_id,
95                            bool kernel_frames,
96                            UnwindMode unwind_mode);
97   void PostAdoptProcDescriptors(DataSourceInstanceID ds_id,
98                                 pid_t pid,
99                                 base::ScopedFile maps_fd,
100                                 base::ScopedFile mem_fd);
101   void PostRecordTimedOutProcDescriptors(DataSourceInstanceID ds_id, pid_t pid);
102   void PostRecordNoUserspaceProcess(DataSourceInstanceID ds_id, pid_t pid);
103   void PostProcessQueue();
104   void PostInitiateDataSourceStop(DataSourceInstanceID ds_id);
105   void PostPurgeDataSource(DataSourceInstanceID ds_id);
106 
107   void PostClearCachedStatePeriodic(DataSourceInstanceID ds_id,
108                                     uint32_t period_ms);
109 
unwind_queue()110   UnwindQueue<UnwindEntry, kUnwindQueueCapacity>& unwind_queue() {
111     return unwind_queue_;
112   }
113 
GetEnqueuedFootprint()114   uint64_t GetEnqueuedFootprint() {
115     uint64_t freed =
116         footprint_tracker_.stack_bytes_freed.load(std::memory_order_acquire);
117     uint64_t allocated = footprint_tracker_.stack_bytes_allocated.load(
118         std::memory_order_relaxed);
119 
120     // overflow not a concern in practice
121     PERFETTO_DCHECK(allocated >= freed);
122     return allocated - freed;
123   }
124 
IncrementEnqueuedFootprint(uint64_t increment)125   void IncrementEnqueuedFootprint(uint64_t increment) {
126     footprint_tracker_.stack_bytes_allocated.fetch_add(
127         increment, std::memory_order_relaxed);
128   }
129 
130  private:
131   struct ProcessState {
132     // kInitial: unwinder waiting for more info on the process (proc-fds, their
133     //           lookup expiration, or that there is no need for them).
134     // kFdsResolved: proc-fds available, can unwind samples.
135     // kFdsTimedOut: proc-fd lookup timed out, will discard samples. Can still
136     //               transition to kFdsResolved if the fds are received later.
137     // kNoUserspace: only handling kernel callchains (the sample might
138     //               still be for a userspace process), can process samples.
139     enum class Status { kInitial, kFdsResolved, kFdsTimedOut, kNoUserspace };
140 
141     Status status = Status::kInitial;
142     // Present iff status == kFdsResolved.
143     std::optional<UnwindingMetadata> unwind_state;
144     // Used to distinguish first-time unwinding attempts for a process, for
145     // logging purposes.
146     bool attempted_unwinding = false;
147   };
148 
149   struct DataSourceState {
150     enum class Status { kActive, kShuttingDown };
DataSourceStateDataSourceState151     explicit DataSourceState(UnwindMode _unwind_mode)
152         : unwind_mode(_unwind_mode) {}
153 
154     Status status = Status::kActive;
155     const UnwindMode unwind_mode;
156     std::map<pid_t, ProcessState> process_states;
157   };
158 
159   // Accounting for how much heap memory is attached to the enqueued samples at
160   // a given time. Read by the main thread, mutated by both threads.
161   // We track just the heap allocated for the sampled stacks, as it dominates
162   // the per-sample heap use.
163   struct QueueFootprintTracker {
164     std::atomic<uint64_t> stack_bytes_allocated;
165     std::atomic<uint64_t> stack_bytes_freed;
166   };
167 
168   // Must be instantiated via the |UnwinderHandle|.
169   Unwinder(Delegate* delegate, base::UnixTaskRunner* task_runner);
170 
171   // Marks the data source as valid and active at the unwinding stage.
172   // Initializes kernel address symbolization if needed.
173   void StartDataSource(DataSourceInstanceID ds_id,
174                        bool kernel_frames,
175                        UnwindMode unwind_mode);
176 
177   void AdoptProcDescriptors(DataSourceInstanceID ds_id,
178                             pid_t pid,
179                             base::ScopedFile maps_fd,
180                             base::ScopedFile mem_fd);
181   void UpdateProcessStateStatus(DataSourceInstanceID ds_id,
182                                 pid_t pid,
183                                 ProcessState::Status new_status);
184 
185   // Primary task. Processes the enqueued samples using
186   // |ConsumeAndUnwindReadySamples|, and re-evaluates data source state.
187   void ProcessQueue();
188 
189   // Processes the enqueued samples for which all unwinding inputs are ready.
190   // Returns the set of data source instances which still have samples pending
191   // (i.e. waiting on the proc-fds).
192   base::FlatSet<DataSourceInstanceID> ConsumeAndUnwindReadySamples();
193 
194   CompletedSample UnwindSample(const ParsedSample& sample,
195                                UnwindingMetadata* opt_user_state,
196                                bool pid_unwound_before,
197                                UnwindMode unwind_mode);
198 
199   // Returns a list of symbolized kernel frames in the sample (if any).
200   std::vector<unwindstack::FrameData> SymbolizeKernelCallchain(
201       const ParsedSample& sample);
202 
203   // Marks the data source as shutting down at the unwinding stage. It is known
204   // that no new samples for this source will be pushed into the queue, but we
205   // need to delay the unwinder state teardown until all previously-enqueued
206   // samples for this source are processed.
207   void InitiateDataSourceStop(DataSourceInstanceID ds_id);
208 
209   // Tears down unwinding state for the data source without any outstanding
210   // samples, and informs the service that it can continue the shutdown
211   // sequence.
212   void FinishDataSourceStop(DataSourceInstanceID ds_id);
213 
214   // Immediately destroys the data source state, used for abrupt stops.
215   void PurgeDataSource(DataSourceInstanceID ds_id);
216 
DecrementEnqueuedFootprint(uint64_t decrement)217   void DecrementEnqueuedFootprint(uint64_t decrement) {
218     footprint_tracker_.stack_bytes_freed.fetch_add(decrement,
219                                                    std::memory_order_relaxed);
220   }
221 
222   // Clears the parsed maps for all previously-sampled processes, and resets the
223   // libunwindstack cache. This has the effect of deallocating the cached Elf
224   // objects within libunwindstack, which take up non-trivial amounts of memory.
225   //
226   // There are two reasons for having this operation:
227   // * over a longer trace, it's desireable to drop heavy state for processes
228   //   that haven't been sampled recently.
229   // * since libunwindstack's cache is not bounded, it'll tend towards having
230   //   state for all processes that are targeted by the profiling config.
231   //   Clearing the cache periodically helps keep its footprint closer to the
232   //   actual working set (NB: which might still be arbitrarily big, depending
233   //   on the profiling config).
234   //
235   // After this function completes, the next unwind for each process will
236   // therefore incur a guaranteed maps reparse.
237   //
238   // Unwinding for concurrent data sources will *not* be directly affected at
239   // the time of writing, as the non-cleared parsed maps will keep the cached
240   // Elf objects alive through shared_ptrs.
241   //
242   // Note that this operation is heavy in terms of cpu%, and should therefore
243   // be called only for profiling configs that require it.
244   //
245   // TODO(rsavitski): dropping the full parsed maps is somewhat excessive, could
246   // instead clear just the |MapInfo.elf| shared_ptr, but that's considered too
247   // brittle as it's an implementation detail of libunwindstack.
248   // TODO(rsavitski): improve libunwindstack cache's architecture (it is still
249   // worth having at the moment to speed up unwinds across map reparses).
250   void ClearCachedStatePeriodic(DataSourceInstanceID ds_id, uint32_t period_ms);
251 
252   void ResetAndEnableUnwindstackCache();
253 
254   base::UnixTaskRunner* const task_runner_;
255   Delegate* const delegate_;
256   UnwindQueue<UnwindEntry, kUnwindQueueCapacity> unwind_queue_;
257   QueueFootprintTracker footprint_tracker_;
258   std::map<DataSourceInstanceID, DataSourceState> data_sources_;
259   LazyKernelSymbolizer kernel_symbolizer_;
260 
261   PERFETTO_THREAD_CHECKER(thread_checker_)
262 };
263 
264 // Owning resource handle for an |Unwinder| with a dedicated task thread.
265 // Ensures that the |Unwinder| is constructed and destructed on the task thread.
266 // TODO(rsavitski): update base::ThreadTaskRunner to allow for this pattern of
267 // owned state, and consolidate.
268 class UnwinderHandle {
269  public:
UnwinderHandle(Unwinder::Delegate * delegate)270   explicit UnwinderHandle(Unwinder::Delegate* delegate) {
271     std::mutex init_lock;
272     std::condition_variable init_cv;
273 
274     std::function<void(base::UnixTaskRunner*, Unwinder*)> initializer =
275         [this, &init_lock, &init_cv](base::UnixTaskRunner* task_runner,
276                                      Unwinder* unwinder) {
277           std::lock_guard<std::mutex> lock(init_lock);
278           task_runner_ = task_runner;
279           unwinder_ = unwinder;
280           // Notify while still holding the lock, as init_cv ceases to exist as
281           // soon as the main thread observes a non-null task_runner_, and it
282           // can wake up spuriously (i.e. before the notify if we had unlocked
283           // before notifying).
284           init_cv.notify_one();
285         };
286 
287     thread_ = std::thread(&UnwinderHandle::RunTaskThread, this,
288                           std::move(initializer), delegate);
289 
290     std::unique_lock<std::mutex> lock(init_lock);
291     init_cv.wait(lock, [this] { return !!task_runner_ && !!unwinder_; });
292   }
293 
~UnwinderHandle()294   ~UnwinderHandle() {
295     if (task_runner_) {
296       PERFETTO_CHECK(!task_runner_->QuitCalled());
297       task_runner_->Quit();
298 
299       PERFETTO_DCHECK(thread_.joinable());
300     }
301     if (thread_.joinable())
302       thread_.join();
303   }
304 
305   Unwinder* operator->() { return unwinder_; }
306 
307  private:
RunTaskThread(std::function<void (base::UnixTaskRunner *,Unwinder *)> initializer,Unwinder::Delegate * delegate)308   void RunTaskThread(
309       std::function<void(base::UnixTaskRunner*, Unwinder*)> initializer,
310       Unwinder::Delegate* delegate) {
311     base::UnixTaskRunner task_runner;
312     Unwinder unwinder(delegate, &task_runner);
313     task_runner.PostTask(
314         std::bind(std::move(initializer), &task_runner, &unwinder));
315     task_runner.Run();
316   }
317 
318   std::thread thread_;
319   base::UnixTaskRunner* task_runner_ = nullptr;
320   Unwinder* unwinder_ = nullptr;
321 };
322 
323 }  // namespace profiling
324 }  // namespace perfetto
325 
326 #endif  // SRC_PROFILING_PERF_UNWINDING_H_
327