summaryrefslogtreecommitdiffstats
path: root/chromium/base/profiler/stack_sampling_profiler.h
blob: 9d52f27a7efc84645ec681756b3a92b194ec26d6 (plain)
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
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
// Copyright 2015 The Chromium Authors. All rights reserved.
// 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 <string>
#include <vector>

#include "base/base_export.h"
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string16.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"

namespace base {

class NativeStackSampler;

// 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::StackStackSamplingProfiler::SamplingParams params;
//   // Any thread's ID may be passed as the target.
//   base::StackSamplingProfiler profiler(base::PlatformThread::CurrentId()),
//       params);
//
//   // Or, to process the profiles within Chrome rather than via UMA, use a
//   // custom completed callback:
//   base::StackStackSamplingProfiler::CompletedCallback
//       thread_safe_callback = ...;
//   base::StackSamplingProfiler profiler(base::PlatformThread::CurrentId()),
//       params, thread_safe_callback);
//
//   profiler.Start();
//   // ... work being done on the target thread here ...
//   profiler.Stop();  // optional, stops collection before complete per params
//
// The default SamplingParams causes stacks to be recorded in a single burst at
// a 10Hz interval for a total of 30 seconds. All of these parameters may be
// altered as desired.
//
// When all call stack profiles are complete or the profiler is stopped, if the
// custom completed callback was set it is called from a thread created by the
// profiler with the completed profiles. A profile is considered complete if all
// requested samples were recorded for the profile (i.e. it was not stopped
// prematurely).  If no callback was set, the default completed callback will be
// called with the profiles. It is expected that the the default completed
// callback is set by the metrics system to allow profiles to be provided via
// UMA.
//
// The results of the profiling are passed to the completed callback and consist
// of a vector of CallStackProfiles. Each CallStackProfile corresponds to a
// burst as specified in SamplingParams and contains a set of Samples and
// Modules. One Sample corresponds to a single recorded stack, and the Modules
// record those modules associated with the recorded stack frames.
class BASE_EXPORT StackSamplingProfiler {
 public:
  // Module represents the module (DLL or exe) corresponding to a stack frame.
  struct BASE_EXPORT Module {
    Module();
    Module(const void* base_address, const std::string& id,
           const FilePath& filename);
    ~Module();

    // Points to the base address of the module.
    const void* base_address;

    // An opaque binary string that uniquely identifies a particular program
    // version with high probability. This is parsed from headers of the loaded
    // module.
    // For binaries generated by GNU tools:
    //   Contents of the .note.gnu.build-id field.
    // On Windows:
    //   GUID + AGE in the debug image headers of a module.
    std::string id;

    // The filename of the module.
    FilePath filename;
  };

  // Frame represents an individual sampled stack frame with module information.
  struct BASE_EXPORT Frame {
    // Identifies an unknown module.
    static const size_t kUnknownModuleIndex = static_cast<size_t>(-1);

    Frame(const void* instruction_pointer, size_t module_index);
    ~Frame();

    // The sampled instruction pointer within the function.
    const void* instruction_pointer;

    // Index of the module in CallStackProfile::modules. We don't represent
    // module state directly here to save space.
    size_t module_index;
  };

  // Sample represents a set of stack frames.
  using Sample = std::vector<Frame>;

  // CallStackProfile represents a set of samples.
  struct BASE_EXPORT CallStackProfile {
    CallStackProfile();
    ~CallStackProfile();

    std::vector<Module> modules;
    std::vector<Sample> samples;

    // Duration of this profile.
    TimeDelta profile_duration;

    // Time between samples.
    TimeDelta sampling_period;

    // True if sample ordering is important and should be preserved if and when
    // this profile is compressed and processed.
    bool preserve_sample_ordering;

    // User data associated with this profile.
    uintptr_t user_data;
  };

  using CallStackProfiles = std::vector<CallStackProfile>;

  // Represents parameters that configure the sampling.
  struct BASE_EXPORT SamplingParams {
    SamplingParams();

    // Time to delay before first samples are taken. Defaults to 0.
    TimeDelta initial_delay;

    // Number of sampling bursts to perform. Defaults to 1.
    int bursts;

    // Interval between sampling bursts. This is the desired duration from the
    // start of one burst to the start of the next burst. Defaults to 10s.
    TimeDelta burst_interval;

    // Number of samples to record per burst. Defaults to 300.
    int samples_per_burst;

    // Interval between samples during a sampling burst. This is the desired
    // duration from the start of one sample to the start of the next
    // sample. Defaults to 100ms.
    TimeDelta sampling_interval;

    // True if sample ordering is important and should be preserved if and when
    // this profile is compressed and processed. Defaults to false.
    bool preserve_sample_ordering;

    // User data associated with this profile.
    uintptr_t user_data;
  };

  // The callback type used to collect completed profiles.
  //
  // IMPORTANT NOTE: the callback is invoked on a thread the profiler
  // constructs, rather than on the thread used to construct the profiler and
  // set the callback, and thus the callback must be callable on any thread. For
  // threads with message loops that create StackSamplingProfilers, posting a
  // task to the message loop with a copy of the profiles is the recommended
  // thread-safe callback implementation.
  using CompletedCallback = Callback<void(const CallStackProfiles&)>;

  // Creates a profiler that sends completed profiles to the default completed
  // callback.
  StackSamplingProfiler(PlatformThreadId thread_id,
                        const SamplingParams& params);
  // Creates a profiler that sends completed profiles to |completed_callback|.
  StackSamplingProfiler(PlatformThreadId thread_id,
                        const SamplingParams& params,
                        CompletedCallback callback);
  ~StackSamplingProfiler();

  // Initializes the profiler and starts sampling.
  void Start();

  // Stops the profiler and any ongoing sampling. Calling this function is
  // optional; if not invoked profiling terminates when all the profiling bursts
  // specified in the SamplingParams are completed.
  void Stop();

  // Sets a callback to process profiles collected by profiler instances without
  // a completed callback. Profiles are queued internally until a non-null
  // callback is provided to this function,
  //
  // The callback is typically called on a thread created by the profiler.  If
  // completed profiles are queued when set, however, it will also be called
  // immediately on the calling thread.
  static void SetDefaultCompletedCallback(CompletedCallback callback);

 private:
  // SamplingThread is a separate thread used to suspend and sample stacks from
  // the target thread.
  class SamplingThread : public PlatformThread::Delegate {
   public:
    // Samples stacks using |native_sampler|. When complete, invokes
    // |completed_callback| with the collected call stack profiles.
    // |completed_callback| must be callable on any thread.
    SamplingThread(scoped_ptr<NativeStackSampler> native_sampler,
                   const SamplingParams& params,
                   CompletedCallback completed_callback);
    ~SamplingThread() override;

    // PlatformThread::Delegate:
    void ThreadMain() override;

    void Stop();

   private:
    // Collects a call stack profile from a single burst. Returns true if the
    // profile was collected, or false if collection was stopped before it
    // completed.
    bool CollectProfile(CallStackProfile* profile, TimeDelta* elapsed_time);

    // Collects call stack profiles from all bursts, or until the sampling is
    // stopped. If stopped before complete, |call_stack_profiles| will contain
    // only full bursts.
    void CollectProfiles(CallStackProfiles* profiles);

    scoped_ptr<NativeStackSampler> native_sampler_;
    const SamplingParams params_;

    // If Stop() is called, it signals this event to force the sampling to
    // terminate before all the samples specified in |params_| are collected.
    WaitableEvent stop_event_;

    const CompletedCallback completed_callback_;

    DISALLOW_COPY_AND_ASSIGN(SamplingThread);
  };

  // The thread whose stack will be sampled.
  PlatformThreadId thread_id_;

  const SamplingParams params_;

  scoped_ptr<SamplingThread> sampling_thread_;
  PlatformThreadHandle sampling_thread_handle_;

  const CompletedCallback completed_callback_;

  DISALLOW_COPY_AND_ASSIGN(StackSamplingProfiler);
};

// The metrics provider code wants to put Samples in a map and compare them,
// which requires us to define a few operators.
BASE_EXPORT bool operator==(const StackSamplingProfiler::Frame& a,
                            const StackSamplingProfiler::Frame& b);
BASE_EXPORT bool operator<(const StackSamplingProfiler::Frame& a,
                           const StackSamplingProfiler::Frame& b);

}  // namespace base

#endif  // BASE_PROFILER_STACK_SAMPLING_PROFILER_H_