1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184

base / profiler / stack_sampler.h [blame]

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_PROFILER_STACK_SAMPLER_H_
#define BASE_PROFILER_STACK_SAMPLER_H_

#include <memory>
#include <vector>

#include "base/base_export.h"
#include "base/containers/circular_deque.h"
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/profiler/frame.h"
#include "base/profiler/register_context.h"
#include "base/profiler/sampling_profiler_thread_token.h"
#include "base/profiler/stack_copier.h"
#include "base/profiler/stack_unwind_data.h"
#include "base/threading/platform_thread.h"
#include "build/build_config.h"

namespace base {

class ModuleCache;
class StackBuffer;
class StackSamplerTestDelegate;
class Unwinder;

// StackSampler is an implementation detail of StackSamplingProfiler. It
// abstracts the native implementation required to record a set of stack frames
// for a given thread. It delegates to StackCopier for the
// platform-specific stack copying implementation.
// This class is used on both the SamplingThread and a worker thread of the
// thread pool. Recording stack frames always occurs on the
// SamplingThread but unwinding the stack can occur on either the SamplingThread
// or a worker thread. Sampling can start before the thread pool is running so
// unwinding will occur on the SamplingThread until the thread pool is ready.
class BASE_EXPORT StackSampler {
 public:
  // Factory for generating a set of Unwinders for use by the profiler.
  using UnwindersFactory =
      OnceCallback<std::vector<std::unique_ptr<Unwinder>>()>;

  // Creates a stack sampler that records samples for thread with
  // |thread_token|. Unwinders in |unwinders| must be stored in increasing
  // priority to guide unwind attempts. Only the unwinder with the lowest
  // priority is allowed to return with UnwindResult::kCompleted. Returns null
  // if this platform does not support stack sampling.
  static std::unique_ptr<StackSampler> Create(
      SamplingProfilerThreadToken thread_token,
      std::unique_ptr<StackUnwindData> stack_unwind_data,
      UnwindersFactory core_unwinders_factory,
      RepeatingClosure record_sample_callback,
      StackSamplerTestDelegate* test_delegate);

  ~StackSampler();

  StackSampler(const StackSampler&) = delete;
  StackSampler& operator=(const StackSampler&) = delete;

  // Gets the required size of the stack buffer.
  static size_t GetStackBufferSize();

  // Creates an instance of the a stack buffer that can be used for calls to
  // any StackSampler object.
  static std::unique_ptr<StackBuffer> CreateStackBuffer();

  // The following functions are all called on the SamplingThread (not the
  // thread being sampled).

  // Performs post-construction initialization on the SamplingThread.
  void Initialize();

  // Stops the sampler.
  void Stop(OnceClosure done_callback);

  // Adds an auxiliary unwinder to handle additional, non-native-code unwind
  // scenarios. Unwinders must be inserted in increasing priority, following
  // |unwinders| provided in Create(), to guide unwind attempts.
  void AddAuxUnwinder(std::unique_ptr<Unwinder> unwinder);

  // Records a set of frames and returns them.
  void RecordStackFrames(StackBuffer* stack_buffer,
                         PlatformThreadId thread_id,
                         OnceClosure done_callback);

  StackUnwindData* GetStackUnwindData();

  // Exposes the internal function for unit testing.
  static std::vector<Frame> WalkStackForTesting(
      ModuleCache* module_cache,
      RegisterContext* thread_context,
      uintptr_t stack_top,
      std::vector<UnwinderCapture> unwinders);

  // Create a StackSampler, overriding the platform-specific components.
  static std::unique_ptr<StackSampler> CreateForTesting(
      std::unique_ptr<StackCopier> stack_copier,
      std::unique_ptr<StackUnwindData> stack_unwind_data,
      UnwindersFactory core_unwinders_factory,
      RepeatingClosure record_sample_callback = RepeatingClosure(),
      StackSamplerTestDelegate* test_delegate = nullptr);

#if BUILDFLAG(IS_CHROMEOS)
  // How often to record the "Memory.StackSamplingProfiler.StackSampleSize2" UMA
  // histogram. Specifically, only 1 in kUMAHistogramDownsampleAmount calls to
  // RecordStackFrames will add a sample to the histogram. RecordStackFrames is
  // called many times a second. We don't need multiple samples per second to
  // get a good understanding of average stack sizes, and it's a lot of data to
  // record. kUMAHistogramDownsampleAmount should give us about 1 sample per 10
  // seconds per process, which is plenty. 199 is prime which should avoid any
  // aliasing issues (e.g. if stacks are larger on second boundaries or some
  // such weirdness).
  static constexpr uint32_t kUMAHistogramDownsampleAmount = 199;
#endif

 private:
  FRIEND_TEST_ALL_PREFIXES(StackSamplerTest,
                           AuxUnwinderInvokedWhileRecordingStackFrames);

  StackSampler(std::unique_ptr<StackCopier> stack_copier,
               std::unique_ptr<StackUnwindData> stack_unwind_data,
               UnwindersFactory core_unwinders_factory,
               RepeatingClosure record_sample_callback,
               StackSamplerTestDelegate* test_delegate);

  static std::vector<Frame> WalkStack(ModuleCache* module_cache,
                                      RegisterContext* thread_context,
                                      uintptr_t stack_top,
                                      std::vector<UnwinderCapture> unwinders);

  void UnwindComplete(TimeTicks timestamp,
                      OnceClosure done_callback,
                      std::vector<Frame> frames);
  void AddAuxUnwinderWithoutInit(std::unique_ptr<Unwinder> unwinder);
  void ThreadPoolRunning();

  const std::unique_ptr<StackCopier> stack_copier_;
  UnwindersFactory unwinders_factory_;

  const RepeatingClosure record_sample_callback_;
  const raw_ptr<StackSamplerTestDelegate> test_delegate_;

#if BUILDFLAG(IS_CHROMEOS)
  // Counter for "Memory.StackSamplingProfiler.StackSampleSize2" UMA histogram.
  // See comments above kUMAHistogramDownsampleAmount. Unsigned so that overflow
  // isn't undefined behavior.
  uint32_t stack_size_histogram_sampling_counter_ = 0;
#endif

  scoped_refptr<SequencedTaskRunner> thread_pool_runner_;

  // The StackUnwindData will be in released on the `thread_pool_runner_` if it
  // is non-null.
  std::unique_ptr<StackUnwindData> unwind_data_;

  bool was_initialized_ = false;
  bool thread_pool_ready_ = false;

  base::WeakPtrFactory<StackSampler> weak_ptr_factory_{this};
};

// StackSamplerTestDelegate provides seams for test code to execute during stack
// collection.
class BASE_EXPORT StackSamplerTestDelegate {
 public:
  StackSamplerTestDelegate(const StackSamplerTestDelegate&) = delete;
  StackSamplerTestDelegate& operator=(const StackSamplerTestDelegate&) = delete;

  virtual ~StackSamplerTestDelegate();

  // Called after copying the stack and resuming the target thread, but prior to
  // walking the stack. Invoked on the SamplingThread.
  virtual void OnPreStackWalk() = 0;

 protected:
  StackSamplerTestDelegate();
};

}  // namespace base

#endif  // BASE_PROFILER_STACK_SAMPLER_H_