cpu-profiler.h 12.1 KB
Newer Older
1
// Copyright 2012 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5 6
#ifndef V8_PROFILER_CPU_PROFILER_H_
#define V8_PROFILER_CPU_PROFILER_H_
7

8
#include <atomic>
9 10
#include <memory>

11 12
#include "src/base/platform/condition-variable.h"
#include "src/base/platform/mutex.h"
13
#include "src/base/platform/time.h"
14
#include "src/profiler/circular-queue.h"
lpy's avatar
lpy committed
15
#include "src/profiler/profiler-listener.h"
lpy's avatar
lpy committed
16
#include "src/profiler/tick-sample.h"
17
#include "src/utils/locked-queue.h"
18 19

namespace v8 {
20 21 22
namespace sampler {
class Sampler;
}
23 24
namespace internal {

25 26 27 28
// Forward declarations.
class CodeEntry;
class CodeMap;
class CpuProfilesCollection;
29
class Isolate;
30 31
class ProfileGenerator;

32 33 34 35 36
#define CODE_EVENTS_TYPE_LIST(V)                 \
  V(CODE_CREATION, CodeCreateEventRecord)        \
  V(CODE_MOVE, CodeMoveEventRecord)              \
  V(CODE_DISABLE_OPT, CodeDisableOptEventRecord) \
  V(CODE_DEOPT, CodeDeoptEventRecord)            \
37
  V(REPORT_BUILTIN, ReportBuiltinEventRecord)
38

39 40 41
#define VM_EVENTS_TYPE_LIST(V) \
  CODE_EVENTS_TYPE_LIST(V)     \
  V(NATIVE_CONTEXT_MOVE, NativeContextMoveEventRecord)
42 43 44 45

class CodeEventRecord {
 public:
#define DECLARE_TYPE(type, ignore) type,
46
  enum Type { NONE = 0, VM_EVENTS_TYPE_LIST(DECLARE_TYPE) };
47 48 49
#undef DECLARE_TYPE

  Type type;
50
  mutable unsigned order;
51 52 53 54 55
};


class CodeCreateEventRecord : public CodeEventRecord {
 public:
56
  Address instruction_start;
57
  CodeEntry* entry;
58
  unsigned instruction_size;
59

60
  V8_INLINE void UpdateCodeMap(CodeMap* code_map);
61 62 63 64 65
};


class CodeMoveEventRecord : public CodeEventRecord {
 public:
66 67
  Address from_instruction_start;
  Address to_instruction_start;
68

69
  V8_INLINE void UpdateCodeMap(CodeMap* code_map);
70 71 72
};


73 74
class CodeDisableOptEventRecord : public CodeEventRecord {
 public:
75
  Address instruction_start;
76 77
  const char* bailout_reason;

78
  V8_INLINE void UpdateCodeMap(CodeMap* code_map);
79 80 81
};


82 83
class CodeDeoptEventRecord : public CodeEventRecord {
 public:
84
  Address instruction_start;
85
  const char* deopt_reason;
86
  int deopt_id;
87
  Address pc;
lpy's avatar
lpy committed
88
  int fp_to_sp_delta;
89 90
  CpuProfileDeoptFrame* deopt_frames;
  int deopt_frame_count;
91

92
  V8_INLINE void UpdateCodeMap(CodeMap* code_map);
93 94 95
};


96 97
class ReportBuiltinEventRecord : public CodeEventRecord {
 public:
98
  Address instruction_start;
99 100
  Builtins::Name builtin_id;

101
  V8_INLINE void UpdateCodeMap(CodeMap* code_map);
102 103
};

104 105 106 107 108 109
// Signals that a native context's address has changed.
class NativeContextMoveEventRecord : public CodeEventRecord {
 public:
  Address from_address;
  Address to_address;
};
110

111 112
// A record type for sending samples from the main thread/signal handler to the
// profiling thread.
113
class TickSampleEventRecord {
114
 public:
115 116
  // The parameterless constructor is used when we dequeue data from
  // the ticks buffer.
117
  TickSampleEventRecord() = default;
118 119
  explicit TickSampleEventRecord(unsigned order) : order(order) { }

120
  unsigned order;
121
  TickSample sample;
122 123
};

124 125
// A record type for sending code events (e.g. create, move, delete) to the
// profiling thread.
126 127 128 129 130 131 132 133 134
class CodeEventsContainer {
 public:
  explicit CodeEventsContainer(
      CodeEventRecord::Type type = CodeEventRecord::NONE) {
    generic.type = type;
  }
  union  {
    CodeEventRecord generic;
#define DECLARE_CLASS(ignore, type) type type##_;
135
    VM_EVENTS_TYPE_LIST(DECLARE_CLASS)
136
#undef DECLARE_CLASS
137 138 139
  };
};

140 141
// Maintains the number of active CPU profilers in an isolate, and routes
// logging to a given ProfilerListener.
142 143
class ProfilingScope {
 public:
144 145
  ProfilingScope(Isolate* isolate, ProfilerListener* listener);
  ~ProfilingScope();
146 147 148

 private:
  Isolate* const isolate_;
149
  ProfilerListener* const listener_;
150
};
151

152 153
class ProfilerCodeObserver;

154 155
// This class implements both the profile events processor thread and
// methods called by event producers: VM and stack sampler threads.
156 157
class V8_EXPORT_PRIVATE ProfilerEventsProcessor : public base::Thread,
                                                  public CodeEventObserver {
158
 public:
159
  virtual ~ProfilerEventsProcessor();
160

161 162
  void CodeEventHandler(const CodeEventsContainer& evt_rec) override;

163
  // Thread control.
164
  void Run() override = 0;
165
  void StopSynchronously();
166
  bool running() { return running_.load(std::memory_order_relaxed); }
167
  void Enqueue(const CodeEventsContainer& event);
168

169
  // Puts current stack into the tick sample events buffer.
170 171
  void AddCurrentStack(bool update_stats = false);
  void AddDeoptStack(Address from, int fp_to_sp_delta);
172
  // Add a sample into the tick sample events buffer. Used for testing.
173
  void AddSample(TickSample sample);
174

175 176
  virtual void SetSamplingInterval(base::TimeDelta) {}

177
 protected:
178 179
  ProfilerEventsProcessor(Isolate* isolate, ProfileGenerator* generator,
                          ProfilerCodeObserver* code_observer);
180

181
  // Called from events processing thread (Run() method.)
182
  bool ProcessCodeEvent();
183

184 185 186 187 188
  enum SampleProcessingResult {
    OneSampleProcessed,
    FoundSampleForNextCodeEvent,
    NoSamplesInQueue
  };
189
  virtual SampleProcessingResult ProcessOneSample() = 0;
190

191
  ProfileGenerator* generator_;
192
  ProfilerCodeObserver* code_observer_;
193
  std::atomic_bool running_{true};
194 195
  base::ConditionVariable running_cond_;
  base::Mutex running_mutex_;
196
  LockedQueue<CodeEventsContainer> events_buffer_;
197 198 199
  LockedQueue<TickSampleEventRecord> ticks_from_vm_buffer_;
  std::atomic<unsigned> last_code_event_id_;
  unsigned last_processed_code_event_id_;
200
  Isolate* isolate_;
201 202
};

203 204
class V8_EXPORT_PRIVATE SamplingEventsProcessor
    : public ProfilerEventsProcessor {
205 206
 public:
  SamplingEventsProcessor(Isolate* isolate, ProfileGenerator* generator,
207
                          ProfilerCodeObserver* code_observer,
208
                          base::TimeDelta period, bool use_precise_sampling);
209 210 211 212 213 214 215 216 217
  ~SamplingEventsProcessor() override;

  // SamplingCircularQueue has stricter alignment requirements than a normal new
  // can fulfil, so we need to provide our own new/delete here.
  void* operator new(size_t size);
  void operator delete(void* ptr);

  void Run() override;

218 219
  void SetSamplingInterval(base::TimeDelta period) override;

220 221 222 223
  // Tick sample events are filled directly in the buffer of the circular
  // queue (because the structure is of fixed width, but usually not all
  // stack frame entries are filled.) This method returns a pointer to the
  // next record of the buffer.
224 225
  // These methods are not thread-safe and should only ever be called by one
  // producer (from CpuSampler::SampleStack()). For testing, use AddSample.
226 227 228 229
  inline TickSample* StartTickSample();
  inline void FinishTickSample();

  sampler::Sampler* sampler() { return sampler_.get(); }
230
  base::TimeDelta period() const { return period_; }
231 232 233 234

 private:
  SampleProcessingResult ProcessOneSample() override;

235
  static const size_t kTickSampleBufferSize = 512 * KB;
236 237 238 239
  static const size_t kTickSampleQueueLength =
      kTickSampleBufferSize / sizeof(TickSampleEventRecord);
  SamplingCircularQueue<TickSampleEventRecord,
                        kTickSampleQueueLength> ticks_buffer_;
240
  std::unique_ptr<sampler::Sampler> sampler_;
241
  base::TimeDelta period_;           // Samples & code events processing period.
242 243
  const bool use_precise_sampling_;  // Whether or not busy-waiting is used for
                                     // low sampling intervals on Windows.
244 245
};

246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
// Builds and maintains a CodeMap tracking code objects on the VM heap. While
// alive, logs generated code, callbacks, and builtins from the isolate.
// Redirects events to the profiler events processor when present.
class V8_EXPORT_PRIVATE ProfilerCodeObserver : public CodeEventObserver {
 public:
  explicit ProfilerCodeObserver(Isolate*);

  void CodeEventHandler(const CodeEventsContainer& evt_rec) override;

  CodeMap* code_map() { return &code_map_; }

 private:
  friend class ProfilerEventsProcessor;

  void CodeEventHandlerInternal(const CodeEventsContainer& evt_rec);

  void CreateEntriesForRuntimeCallStats();
  void LogBuiltins();

  ProfilerEventsProcessor* processor() { return processor_; }

  // Redirects code events to be enqueued on the given events processor.
  void set_processor(ProfilerEventsProcessor* processor) {
    processor_ = processor;
  }

  // Stops redirection of code events onto an events processor.
  void clear_processor() { processor_ = nullptr; }

  Isolate* const isolate_;
  CodeMap code_map_;
  ProfilerEventsProcessor* processor_;
};

280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
// The CpuProfiler is a sampling CPU profiler for JS frames. It corresponds to
// v8::CpuProfiler at the API level. It spawns an additional thread which is
// responsible for triggering samples and then symbolizing the samples with
// function names. To symbolize on a background thread, the profiler copies
// metadata about generated code off-heap.
//
// Sampling is done using posix signals (except on Windows). The profiling
// thread sends a signal to the main thread, based on a timer. The signal
// handler can interrupt the main thread between any abitrary instructions.
// This means we are very careful about reading stack values during the signal
// handler as we could be in the middle of an operation that is modifying the
// stack.
//
// The story on Windows is similar except we use thread suspend and resume.
//
// Samples are passed to the profiling thread via a circular buffer. The
// profiling thread symbolizes the samples by looking up the code pointers
// against its own list of code objects. The profiling thread also listens for
// code creation/move/deletion events (from the GC), to maintain its list of
// code objects accurately.
300
class V8_EXPORT_PRIVATE CpuProfiler {
301
 public:
302 303
  explicit CpuProfiler(Isolate* isolate, CpuProfilingNamingMode = kDebugNaming,
                       CpuProfilingLoggingMode = kLazyLogging);
304

305
  CpuProfiler(Isolate* isolate, CpuProfilingNamingMode naming_mode,
306
              CpuProfilingLoggingMode logging_mode,
307
              CpuProfilesCollection* profiles, ProfileGenerator* test_generator,
308 309
              ProfilerEventsProcessor* test_processor);

310
  ~CpuProfiler();
311

312 313
  static void CollectSample(Isolate* isolate);

314 315
  using ProfilingMode = v8::CpuProfilingMode;
  using NamingMode = v8::CpuProfilingNamingMode;
316
  using LoggingMode = v8::CpuProfilingLoggingMode;
317

318
  base::TimeDelta sampling_interval() const { return base_sampling_interval_; }
319
  void set_sampling_interval(base::TimeDelta value);
320
  void set_use_precise_sampling(bool);
321
  void CollectSample();
322 323
  void StartProfiling(const char* title, CpuProfilingOptions options = {});
  void StartProfiling(String title, CpuProfilingOptions options = {});
324

325
  CpuProfile* StopProfiling(const char* title);
326
  CpuProfile* StopProfiling(String title);
327
  int GetProfilesCount();
328
  CpuProfile* GetProfile(int index);
329 330
  void DeleteAllProfiles();
  void DeleteProfile(CpuProfile* profile);
331

332
  bool is_profiling() const { return is_profiling_; }
333

334 335
  ProfileGenerator* generator() const { return generator_.get(); }
  ProfilerEventsProcessor* processor() const { return processor_.get(); }
336
  Isolate* isolate() const { return isolate_; }
337

338
  ProfilerListener* profiler_listener_for_test() const {
339 340 341
    return profiler_listener_.get();
  }

342 343
 private:
  void StartProcessorIfNotStarted();
344
  void StopProcessorIfLastProfile(const char* title);
345 346
  void StopProcessor();
  void ResetProfiles();
347 348 349

  void EnableLogging();
  void DisableLogging();
350

351 352 353 354 355 356
  // Computes a sampling interval sufficient to accomodate attached profiles.
  base::TimeDelta ComputeSamplingInterval() const;
  // Dynamically updates the sampler to use a sampling interval sufficient for
  // child profiles.
  void AdjustSamplingInterval();

357
  Isolate* const isolate_;
358
  const NamingMode naming_mode_;
359
  const LoggingMode logging_mode_;
360
  bool use_precise_sampling_ = true;
361 362 363
  // Sampling interval to which per-profile sampling intervals will be clamped
  // to a multiple of, or used as the default if unspecified.
  base::TimeDelta base_sampling_interval_;
364 365 366
  std::unique_ptr<CpuProfilesCollection> profiles_;
  std::unique_ptr<ProfileGenerator> generator_;
  std::unique_ptr<ProfilerEventsProcessor> processor_;
367
  std::unique_ptr<ProfilerListener> profiler_listener_;
368 369
  std::unique_ptr<ProfilingScope> profiling_scope_;
  ProfilerCodeObserver code_observer_;
370
  bool is_profiling_;
371 372 373 374

  DISALLOW_COPY_AND_ASSIGN(CpuProfiler);
};

375 376
}  // namespace internal
}  // namespace v8
377

378
#endif  // V8_PROFILER_CPU_PROFILER_H_