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 19 namespace v8 { 20 namespace sampler { 21 class Sampler; 22 } // namespace sampler 23 namespace internal { 24 25 // Forward declarations. 26 class CodeEntry; 27 class CodeMap; 28 class CpuProfilesCollection; 29 class Isolate; 30 class 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 44 class 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 55 class 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 65 class 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 74 class 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 83 class 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 97 class 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. 107 class 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. 115 class TickSampleEventRecord { 116 public: 117 // The parameterless constructor is used when we dequeue data from 118 // the ticks buffer. 119 TickSampleEventRecord() = default; TickSampleEventRecord(unsigned order)120 explicit TickSampleEventRecord(unsigned order) : order(order) { } 121 122 unsigned order; 123 TickSample sample; 124 }; 125 126 class 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. 135 class 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. 151 class 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 161 class ProfilerCodeObserver; 162 163 // This class implements both the profile events processor thread and 164 // methods called by event producers: VM and stack sampler threads. 165 class 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(); running()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 SetSamplingInterval(base::TimeDelta)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 214 class 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 sampler()241 sampler::Sampler* sampler() { return sampler_.get(); } period()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. 263 class V8_EXPORT_PRIVATE ProfilerCodeObserver : public CodeEventObserver { 264 public: 265 explicit ProfilerCodeObserver(Isolate*, CodeEntryStorage&); 266 267 void CodeEventHandler(const CodeEventsContainer& evt_rec) override; code_entries()268 CodeEntryStorage* code_entries() { return &code_entries_; } code_map()269 CodeMap* code_map() { return &code_map_; } weak_code_registry()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 processor()283 ProfilerEventsProcessor* processor() { return processor_; } 284 285 // Redirects code events to be enqueued on the given events processor. set_processor(ProfilerEventsProcessor * processor)286 void set_processor(ProfilerEventsProcessor* processor) { 287 processor_ = processor; 288 } 289 290 // Stops redirection of code events onto an events processor. clear_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. 320 class 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 sampling_interval()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 is_profiling()368 bool is_profiling() const { return is_profiling_; } 369 symbolizer()370 Symbolizer* symbolizer() const { return symbolizer_.get(); } processor()371 ProfilerEventsProcessor* processor() const { return processor_.get(); } isolate()372 Isolate* isolate() const { return isolate_; } code_entries()373 CodeEntryStorage* code_entries() { return &code_entries_; } 374 profiler_listener_for_test()375 ProfilerListener* profiler_listener_for_test() const { 376 return profiler_listener_.get(); 377 } code_map_for_test()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