1// Copyright 2012 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#ifndef V8_PROFILER_CPU_PROFILER_H_ 6#define V8_PROFILER_CPU_PROFILER_H_ 7 8#include <atomic> 9#include <memory> 10 11#include "src/base/platform/condition-variable.h" 12#include "src/base/platform/mutex.h" 13#include "src/base/platform/time.h" 14#include "src/profiler/circular-queue.h" 15#include "src/profiler/profiler-listener.h" 16#include "src/profiler/tick-sample.h" 17#include "src/utils/locked-queue.h" 18 19namespace v8 { 20namespace sampler { 21class Sampler; 22} // namespace sampler 23namespace internal { 24 25// Forward declarations. 26class CodeEntry; 27class CodeMap; 28class CpuProfilesCollection; 29class Isolate; 30class Symbolizer; 31 32#define CODE_EVENTS_TYPE_LIST(V) \ 33 V(kCodeCreation, CodeCreateEventRecord) \ 34 V(kCodeMove, CodeMoveEventRecord) \ 35 V(kCodeDisableOpt, CodeDisableOptEventRecord) \ 36 V(kCodeDeopt, CodeDeoptEventRecord) \ 37 V(kReportBuiltin, ReportBuiltinEventRecord) \ 38 V(kCodeDelete, CodeDeleteEventRecord) 39 40#define VM_EVENTS_TYPE_LIST(V) \ 41 CODE_EVENTS_TYPE_LIST(V) \ 42 V(kNativeContextMove, NativeContextMoveEventRecord) 43 44class CodeEventRecord { 45 public: 46#define DECLARE_TYPE(type, ignore) type, 47 enum class Type { kNoEvent = 0, VM_EVENTS_TYPE_LIST(DECLARE_TYPE) }; 48#undef DECLARE_TYPE 49 50 Type type; 51 mutable unsigned order; 52}; 53 54 55class CodeCreateEventRecord : public CodeEventRecord { 56 public: 57 Address instruction_start; 58 CodeEntry* entry; 59 unsigned instruction_size; 60 61 V8_INLINE void UpdateCodeMap(CodeMap* code_map); 62}; 63 64 65class CodeMoveEventRecord : public CodeEventRecord { 66 public: 67 Address from_instruction_start; 68 Address to_instruction_start; 69 70 V8_INLINE void UpdateCodeMap(CodeMap* code_map); 71}; 72 73 74class CodeDisableOptEventRecord : public CodeEventRecord { 75 public: 76 Address instruction_start; 77 const char* bailout_reason; 78 79 V8_INLINE void UpdateCodeMap(CodeMap* code_map); 80}; 81 82 83class CodeDeoptEventRecord : public CodeEventRecord { 84 public: 85 Address instruction_start; 86 const char* deopt_reason; 87 int deopt_id; 88 Address pc; 89 int fp_to_sp_delta; 90 CpuProfileDeoptFrame* deopt_frames; 91 int deopt_frame_count; 92 93 V8_INLINE void UpdateCodeMap(CodeMap* code_map); 94}; 95 96 97class ReportBuiltinEventRecord : public CodeEventRecord { 98 public: 99 Address instruction_start; 100 unsigned instruction_size; 101 Builtin builtin; 102 103 V8_INLINE void UpdateCodeMap(CodeMap* code_map); 104}; 105 106// Signals that a native context's address has changed. 107class NativeContextMoveEventRecord : public CodeEventRecord { 108 public: 109 Address from_address; 110 Address to_address; 111}; 112 113// A record type for sending samples from the main thread/signal handler to the 114// profiling thread. 115class TickSampleEventRecord { 116 public: 117 // The parameterless constructor is used when we dequeue data from 118 // the ticks buffer. 119 TickSampleEventRecord() = default; 120 explicit TickSampleEventRecord(unsigned order) : order(order) { } 121 122 unsigned order; 123 TickSample sample; 124}; 125 126class CodeDeleteEventRecord : public CodeEventRecord { 127 public: 128 CodeEntry* entry; 129 130 V8_INLINE void UpdateCodeMap(CodeMap* code_map); 131}; 132 133// A record type for sending code events (e.g. create, move, delete) to the 134// profiling thread. 135class CodeEventsContainer { 136 public: 137 explicit CodeEventsContainer( 138 CodeEventRecord::Type type = CodeEventRecord::Type::kNoEvent) { 139 generic.type = type; 140 } 141 union { 142 CodeEventRecord generic; 143#define DECLARE_CLASS(ignore, type) type type##_; 144 VM_EVENTS_TYPE_LIST(DECLARE_CLASS) 145#undef DECLARE_CLASS 146 }; 147}; 148 149// Maintains the number of active CPU profilers in an isolate, and routes 150// logging to a given ProfilerListener. 151class V8_NODISCARD ProfilingScope { 152 public: 153 ProfilingScope(Isolate* isolate, ProfilerListener* listener); 154 ~ProfilingScope(); 155 156 private: 157 Isolate* const isolate_; 158 ProfilerListener* const listener_; 159}; 160 161class ProfilerCodeObserver; 162 163// This class implements both the profile events processor thread and 164// methods called by event producers: VM and stack sampler threads. 165class V8_EXPORT_PRIVATE ProfilerEventsProcessor : public base::Thread, 166 public CodeEventObserver { 167 public: 168 ~ProfilerEventsProcessor() override; 169 170 void CodeEventHandler(const CodeEventsContainer& evt_rec) override; 171 172 // Thread control. 173 void Run() override = 0; 174 void StopSynchronously(); 175 bool running() { return running_.load(std::memory_order_relaxed); } 176 void Enqueue(const CodeEventsContainer& event); 177 178 // Puts current stack into the tick sample events buffer. 179 void AddCurrentStack(bool update_stats = false); 180 void AddDeoptStack(Address from, int fp_to_sp_delta); 181 // Add a sample into the tick sample events buffer. Used for testing. 182 void AddSample(TickSample sample); 183 184 virtual void SetSamplingInterval(base::TimeDelta) {} 185 186 protected: 187 ProfilerEventsProcessor(Isolate* isolate, Symbolizer* symbolizer, 188 ProfilerCodeObserver* code_observer, 189 CpuProfilesCollection* profiles); 190 191 // Called from events processing thread (Run() method.) 192 bool ProcessCodeEvent(); 193 194 enum SampleProcessingResult { 195 OneSampleProcessed, 196 FoundSampleForNextCodeEvent, 197 NoSamplesInQueue 198 }; 199 virtual SampleProcessingResult ProcessOneSample() = 0; 200 201 Symbolizer* symbolizer_; 202 ProfilerCodeObserver* code_observer_; 203 CpuProfilesCollection* profiles_; 204 std::atomic_bool running_{true}; 205 base::ConditionVariable running_cond_; 206 base::Mutex running_mutex_; 207 LockedQueue<CodeEventsContainer> events_buffer_; 208 LockedQueue<TickSampleEventRecord> ticks_from_vm_buffer_; 209 std::atomic<unsigned> last_code_event_id_; 210 unsigned last_processed_code_event_id_; 211 Isolate* isolate_; 212}; 213 214class V8_EXPORT_PRIVATE SamplingEventsProcessor 215 : public ProfilerEventsProcessor { 216 public: 217 SamplingEventsProcessor(Isolate* isolate, Symbolizer* symbolizer, 218 ProfilerCodeObserver* code_observer, 219 CpuProfilesCollection* profiles, 220 base::TimeDelta period, bool use_precise_sampling); 221 ~SamplingEventsProcessor() override; 222 223 // SamplingCircularQueue has stricter alignment requirements than a normal new 224 // can fulfil, so we need to provide our own new/delete here. 225 void* operator new(size_t size); 226 void operator delete(void* ptr); 227 228 void Run() override; 229 230 void SetSamplingInterval(base::TimeDelta period) override; 231 232 // Tick sample events are filled directly in the buffer of the circular 233 // queue (because the structure is of fixed width, but usually not all 234 // stack frame entries are filled.) This method returns a pointer to the 235 // next record of the buffer. 236 // These methods are not thread-safe and should only ever be called by one 237 // producer (from CpuSampler::SampleStack()). For testing, use AddSample. 238 inline TickSample* StartTickSample(); 239 inline void FinishTickSample(); 240 241 sampler::Sampler* sampler() { return sampler_.get(); } 242 base::TimeDelta period() const { return period_; } 243 244 private: 245 SampleProcessingResult ProcessOneSample() override; 246 void SymbolizeAndAddToProfiles(const TickSampleEventRecord* record); 247 248 static const size_t kTickSampleBufferSize = 512 * KB; 249 static const size_t kTickSampleQueueLength = 250 kTickSampleBufferSize / sizeof(TickSampleEventRecord); 251 SamplingCircularQueue<TickSampleEventRecord, 252 kTickSampleQueueLength> ticks_buffer_; 253 std::unique_ptr<sampler::Sampler> sampler_; 254 base::TimeDelta period_; // Samples & code events processing period. 255 const bool use_precise_sampling_; // Whether or not busy-waiting is used for 256 // low sampling intervals on Windows. 257}; 258 259// Builds and maintains a CodeMap tracking code objects on the VM heap. While 260// alive, logs generated code, callbacks, and builtins from the isolate. 261// Redirects events to the profiler events processor when present. CodeEntry 262// lifetime is associated with the given CodeEntryStorage. 263class V8_EXPORT_PRIVATE ProfilerCodeObserver : public CodeEventObserver { 264 public: 265 explicit ProfilerCodeObserver(Isolate*, CodeEntryStorage&); 266 267 void CodeEventHandler(const CodeEventsContainer& evt_rec) override; 268 CodeEntryStorage* code_entries() { return &code_entries_; } 269 CodeMap* code_map() { return &code_map_; } 270 WeakCodeRegistry* weak_code_registry() { return &weak_code_registry_; } 271 size_t GetEstimatedMemoryUsage() const; 272 273 void ClearCodeMap(); 274 275 private: 276 friend class ProfilerEventsProcessor; 277 278 void CodeEventHandlerInternal(const CodeEventsContainer& evt_rec); 279 280 void CreateEntriesForRuntimeCallStats(); 281 void LogBuiltins(); 282 283 ProfilerEventsProcessor* processor() { return processor_; } 284 285 // Redirects code events to be enqueued on the given events processor. 286 void set_processor(ProfilerEventsProcessor* processor) { 287 processor_ = processor; 288 } 289 290 // Stops redirection of code events onto an events processor. 291 void clear_processor() { processor_ = nullptr; } 292 293 Isolate* const isolate_; 294 CodeEntryStorage& code_entries_; 295 CodeMap code_map_; 296 WeakCodeRegistry weak_code_registry_; 297 ProfilerEventsProcessor* processor_; 298}; 299 300// The CpuProfiler is a sampling CPU profiler for JS frames. It corresponds to 301// v8::CpuProfiler at the API level. It spawns an additional thread which is 302// responsible for triggering samples and then symbolizing the samples with 303// function names. To symbolize on a background thread, the profiler copies 304// metadata about generated code off-heap. 305// 306// Sampling is done using posix signals (except on Windows). The profiling 307// thread sends a signal to the main thread, based on a timer. The signal 308// handler can interrupt the main thread between any abitrary instructions. 309// This means we are very careful about reading stack values during the signal 310// handler as we could be in the middle of an operation that is modifying the 311// stack. 312// 313// The story on Windows is similar except we use thread suspend and resume. 314// 315// Samples are passed to the profiling thread via a circular buffer. The 316// profiling thread symbolizes the samples by looking up the code pointers 317// against its own list of code objects. The profiling thread also listens for 318// code creation/move/deletion events (from the GC), to maintain its list of 319// code objects accurately. 320class V8_EXPORT_PRIVATE CpuProfiler { 321 public: 322 explicit CpuProfiler(Isolate* isolate, CpuProfilingNamingMode = kDebugNaming, 323 CpuProfilingLoggingMode = kLazyLogging); 324 325 CpuProfiler(Isolate* isolate, CpuProfilingNamingMode naming_mode, 326 CpuProfilingLoggingMode logging_mode, 327 CpuProfilesCollection* profiles, Symbolizer* test_symbolizer, 328 ProfilerEventsProcessor* test_processor, 329 ProfilerCodeObserver* test_code_observer); 330 331 ~CpuProfiler(); 332 CpuProfiler(const CpuProfiler&) = delete; 333 CpuProfiler& operator=(const CpuProfiler&) = delete; 334 335 static void CollectSample(Isolate* isolate); 336 static size_t GetAllProfilersMemorySize(Isolate* isolate); 337 338 using ProfilingMode = v8::CpuProfilingMode; 339 using CpuProfilingResult = v8::CpuProfilingResult; 340 using NamingMode = v8::CpuProfilingNamingMode; 341 using LoggingMode = v8::CpuProfilingLoggingMode; 342 using StartProfilingStatus = CpuProfilingStatus; 343 344 base::TimeDelta sampling_interval() const { return base_sampling_interval_; } 345 void set_sampling_interval(base::TimeDelta value); 346 void set_use_precise_sampling(bool); 347 void CollectSample(); 348 size_t GetEstimatedMemoryUsage() const; 349 CpuProfilingResult StartProfiling( 350 CpuProfilingOptions options = {}, 351 std::unique_ptr<DiscardedSamplesDelegate> delegate = nullptr); 352 CpuProfilingResult StartProfiling( 353 const char* title, CpuProfilingOptions options = {}, 354 std::unique_ptr<DiscardedSamplesDelegate> delegate = nullptr); 355 CpuProfilingResult StartProfiling( 356 String title, CpuProfilingOptions options = {}, 357 std::unique_ptr<DiscardedSamplesDelegate> delegate = nullptr); 358 359 CpuProfile* StopProfiling(const char* title); 360 CpuProfile* StopProfiling(String title); 361 CpuProfile* StopProfiling(ProfilerId id); 362 363 int GetProfilesCount(); 364 CpuProfile* GetProfile(int index); 365 void DeleteAllProfiles(); 366 void DeleteProfile(CpuProfile* profile); 367 368 bool is_profiling() const { return is_profiling_; } 369 370 Symbolizer* symbolizer() const { return symbolizer_.get(); } 371 ProfilerEventsProcessor* processor() const { return processor_.get(); } 372 Isolate* isolate() const { return isolate_; } 373 CodeEntryStorage* code_entries() { return &code_entries_; } 374 375 ProfilerListener* profiler_listener_for_test() const { 376 return profiler_listener_.get(); 377 } 378 CodeMap* code_map_for_test() { return code_observer_->code_map(); } 379 380 private: 381 void StartProcessorIfNotStarted(); 382 void StopProcessor(); 383 void ResetProfiles(); 384 385 void EnableLogging(); 386 void DisableLogging(); 387 388 // Computes a sampling interval sufficient to accomodate attached profiles. 389 base::TimeDelta ComputeSamplingInterval() const; 390 // Dynamically updates the sampler to use a sampling interval sufficient for 391 // child profiles. 392 void AdjustSamplingInterval(); 393 394 Isolate* const isolate_; 395 const NamingMode naming_mode_; 396 const LoggingMode logging_mode_; 397 bool use_precise_sampling_ = true; 398 // Sampling interval to which per-profile sampling intervals will be clamped 399 // to a multiple of, or used as the default if unspecified. 400 base::TimeDelta base_sampling_interval_; 401 402 // Storage for CodeEntry objects allocated by the profiler. May live for 403 // multiple profiling sessions, independent of heap listener state. 404 CodeEntryStorage code_entries_; 405 406 std::unique_ptr<ProfilerCodeObserver> code_observer_; 407 std::unique_ptr<CpuProfilesCollection> profiles_; 408 std::unique_ptr<Symbolizer> symbolizer_; 409 std::unique_ptr<ProfilerEventsProcessor> processor_; 410 std::unique_ptr<ProfilerListener> profiler_listener_; 411 std::unique_ptr<ProfilingScope> profiling_scope_; 412 bool is_profiling_; 413}; 414 415} // namespace internal 416} // namespace v8 417 418#endif // V8_PROFILER_CPU_PROFILER_H_ 419