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