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
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227

base / profiler / stack_sampling_profiler.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_SAMPLING_PROFILER_H_
#define BASE_PROFILER_STACK_SAMPLING_PROFILER_H_

#include <memory>
#include <optional>
#include <vector>

#include "base/base_export.h"
#include "base/functional/callback.h"
#include "base/profiler/profile_builder.h"
#include "base/profiler/sampling_profiler_thread_token.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"

namespace base {

class Unwinder;
class StackSampler;
class StackSamplerTestDelegate;

// StackSamplingProfiler periodically stops a thread to sample its stack, for
// the purpose of collecting information about which code paths are
// executing. This information is used in aggregate by UMA to identify hot
// and/or janky code paths.
//
// Sample StackSamplingProfiler usage:
//
//   // Create and customize params as desired.
//   base::StackSamplingProfiler::SamplingParams params;
//
//   // Create a ProfileBuilder subclass to process the profiles.
//   class ProfileBuilder : public base::ProfileBuilder {...}
//
//   // Then create the profiler:
//   base::StackSamplingProfiler profiler(
//       GetSamplingProfilerCurrentThreadToken(),
//       params,
//       std::make_unique<ProfileBuilder>(...));
//
//   // On Android the caller also must provide a factory function for creating
//   // its core stack unwinders. See the ThreadProfiler implementation for an
//   // example of how to do this.
//   base::StackSamplingProfiler profiler(
//       GetSamplingProfilerCurrentThreadToken(),
//       params,
//       std::make_unique<ProfileBuilder>(...),
//       core_unwinders_factory);
//
//   // Then start the profiling.
//   profiler.Start();
//
//   // ... work being done on the target thread here ...
//
//   // Optionally stop collection before complete per params.
//   profiler.Stop();
//
// The default SamplingParams causes stacks to be recorded in a single profile
// at a 10Hz interval for a total of 30 seconds. All of these parameters may be
// altered as desired.
//
// When a call stack profile is complete, or the profiler is stopped,
// ProfileBuilder's OnProfileCompleted function is called from a thread created
// by the profiler.
class BASE_EXPORT StackSamplingProfiler {
 public:
  // Factory for generating a set of Unwinders for use by the profiler. More
  // general unwinders should appear before more specific unwinders in the
  // generated vector, e.g. a system unwinder should appear before a Chrome
  // unwinder. The callback will be invoked on the profiler thread.
  using UnwindersFactory =
      OnceCallback<std::vector<std::unique_ptr<Unwinder>>()>;

  // Represents parameters that configure the sampling.
  struct BASE_EXPORT SamplingParams {
    // Time to delay before first samples are taken.
    TimeDelta initial_delay = Milliseconds(0);

    // Number of samples to record per profile.
    int samples_per_profile = 300;

    // Interval between samples during a sampling profile. This is the desired
    // duration from the start of one sample to the start of the next sample.
    TimeDelta sampling_interval = Milliseconds(100);
  };

  // Returns true if the profiler is supported on the current platform
  // configuration.
  static bool IsSupportedForCurrentPlatform();

  // Creates a profiler for the the thread associated with |thread_token|,
  // generated by GetSamplingProfilerCurrentThreadToken().
  // |record_sample_callback| is called for each sample right before recording
  // the stack sample. An optional |test_delegate| can be supplied by tests.
  //
  // The caller must ensure that this object gets destroyed before the thread
  // exits.
  StackSamplingProfiler(
      SamplingProfilerThreadToken thread_token,
      const SamplingParams& params,
      std::unique_ptr<ProfileBuilder> profile_builder,
      UnwindersFactory core_unwinders_factory,
      RepeatingClosure record_sample_callback = RepeatingClosure(),
      StackSamplerTestDelegate* test_delegate = nullptr);

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

  // Stops any profiling currently taking place before destroying the profiler.
  // This will block until profile_builder_'s OnProfileCompleted function has
  // executed if profiling has started but not already finished.
  ~StackSamplingProfiler();

  // Initializes the profiler and starts sampling. Might block on a
  // WaitableEvent if this StackSamplingProfiler was previously started and
  // recently stopped, while the previous profiling phase winds down.
  void Start();

  // Stops the profiler and any ongoing sampling. This method will return
  // immediately with the profile_builder_'s OnProfileCompleted function being
  // run asynchronously. At most one more stack sample will be taken after this
  // method returns. Calling this function is optional; if not invoked profiling
  // terminates when all the profiling samples specified in the SamplingParams
  // are completed or the profiler object is destroyed, whichever occurs first.
  void Stop();

  // Adds an auxiliary unwinder to handle additional, non-native-code unwind
  // scenarios.
  void AddAuxUnwinder(std::unique_ptr<Unwinder> unwinder);

  // Test peer class. These functions are purely for internal testing of
  // StackSamplingProfiler; DO NOT USE within tests outside of this directory.
  // The functions are static because they interact with the sampling thread, a
  // singleton used by all StackSamplingProfiler objects.  The functions can
  // only be called by the same thread that started the sampling.
  class BASE_EXPORT TestPeer {
   public:
    // Resets the internal state to that of a fresh start. This is necessary
    // so that tests don't inherit state from previous tests.
    static void Reset();

    // Returns whether the sampling thread is currently running or not.
    static bool IsSamplingThreadRunning();

    // Disables inherent idle-shutdown behavior.
    static void DisableIdleShutdown();

    // Initiates an idle shutdown task, as though the idle timer had expired,
    // causing the thread to exit. There is no "idle" check so this must be
    // called only when all sampling tasks have completed. This blocks until
    // the task has been executed, though the actual stopping of the thread
    // still happens asynchronously. Watch IsSamplingThreadRunning() to know
    // when the thread has exited. If |simulate_intervening_start| is true then
    // this method will make it appear to the shutdown task that a new profiler
    // was started between when the idle-shutdown was initiated and when it
    // runs.
    static void PerformSamplingThreadIdleShutdown(
        bool simulate_intervening_start);

    // Provides access to the method computing the next sample time.
    static TimeTicks GetNextSampleTime(TimeTicks scheduled_current_sample_time,
                                       TimeDelta sampling_interval,
                                       TimeTicks now);
  };

 private:
  // SamplingThread is a separate thread used to suspend and sample stacks from
  // the target thread.
  class SamplingThread;

  // Friend the global functions from sample_metadata.cc so that it can call
  // into the function below.
  friend void ApplyMetadataToPastSamplesImpl(
      TimeTicks period_start,
      TimeTicks period_end,
      uint64_t name_hash,
      std::optional<int64_t> key,
      int64_t value,
      std::optional<PlatformThreadId> thread_id);
  friend void AddProfileMetadataImpl(uint64_t name_hash,
                                     int64_t key,
                                     int64_t value,
                                     std::optional<PlatformThreadId> thread_id);

  // Apply metadata to already recorded samples. See the
  // ApplyMetadataToPastSamples() docs in sample_metadata.h.
  static void ApplyMetadataToPastSamples(
      TimeTicks period_start,
      TimeTicks period_end,
      uint64_t name_hash,
      std::optional<int64_t> key,
      int64_t value,
      std::optional<PlatformThreadId> thread_id);

  // Adds metadata as metadata global to the sampling profile.
  static void AddProfileMetadata(uint64_t name_hash,
                                 int64_t key,
                                 int64_t value,
                                 std::optional<PlatformThreadId> thread_id);

  // The thread whose stack will be sampled.
  SamplingProfilerThreadToken thread_token_;

  const SamplingParams params_;

  // Stack sampler which stops the thread and collects stack frames. The
  // ownership of this object will be transferred to the sampling thread when
  // thread sampling starts.
  std::unique_ptr<StackSampler> sampler_;

  // This starts "signaled", is reset when sampling begins, and is signaled
  // when that sampling is complete and the profile builder's
  // OnProfileCompleted function has executed.
  WaitableEvent profiling_inactive_;

  // An ID uniquely identifying this profiler to the sampling thread. This
  // will be an internal "null" value when no collection has been started.
  int profiler_id_;
};

}  // namespace base

#endif  // BASE_PROFILER_STACK_SAMPLING_PROFILER_H_