• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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