• 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(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