• 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 #include "src/v8.h"
6 
7 #include "src/cpu-profiler-inl.h"
8 
9 #include "src/compiler.h"
10 #include "src/frames-inl.h"
11 #include "src/hashmap.h"
12 #include "src/log-inl.h"
13 #include "src/vm-state-inl.h"
14 
15 #include "include/v8-profiler.h"
16 
17 namespace v8 {
18 namespace internal {
19 
20 static const int kProfilerStackSize = 64 * KB;
21 
22 
ProfilerEventsProcessor(ProfileGenerator * generator,Sampler * sampler,base::TimeDelta period)23 ProfilerEventsProcessor::ProfilerEventsProcessor(
24     ProfileGenerator* generator,
25     Sampler* sampler,
26     base::TimeDelta period)
27     : Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)),
28       generator_(generator),
29       sampler_(sampler),
30       running_(true),
31       period_(period),
32       last_code_event_id_(0), last_processed_code_event_id_(0) {
33 }
34 
35 
Enqueue(const CodeEventsContainer & event)36 void ProfilerEventsProcessor::Enqueue(const CodeEventsContainer& event) {
37   event.generic.order = ++last_code_event_id_;
38   events_buffer_.Enqueue(event);
39 }
40 
41 
AddCurrentStack(Isolate * isolate)42 void ProfilerEventsProcessor::AddCurrentStack(Isolate* isolate) {
43   TickSampleEventRecord record(last_code_event_id_);
44   RegisterState regs;
45   StackFrameIterator it(isolate);
46   if (!it.done()) {
47     StackFrame* frame = it.frame();
48     regs.sp = frame->sp();
49     regs.fp = frame->fp();
50     regs.pc = frame->pc();
51   }
52   record.sample.Init(isolate, regs);
53   ticks_from_vm_buffer_.Enqueue(record);
54 }
55 
56 
StopSynchronously()57 void ProfilerEventsProcessor::StopSynchronously() {
58   if (!running_) return;
59   running_ = false;
60   Join();
61 }
62 
63 
ProcessCodeEvent()64 bool ProfilerEventsProcessor::ProcessCodeEvent() {
65   CodeEventsContainer record;
66   if (events_buffer_.Dequeue(&record)) {
67     switch (record.generic.type) {
68 #define PROFILER_TYPE_CASE(type, clss)                          \
69       case CodeEventRecord::type:                               \
70         record.clss##_.UpdateCodeMap(generator_->code_map());   \
71         break;
72 
73       CODE_EVENTS_TYPE_LIST(PROFILER_TYPE_CASE)
74 
75 #undef PROFILER_TYPE_CASE
76       default: return true;  // Skip record.
77     }
78     last_processed_code_event_id_ = record.generic.order;
79     return true;
80   }
81   return false;
82 }
83 
84 ProfilerEventsProcessor::SampleProcessingResult
ProcessOneSample()85     ProfilerEventsProcessor::ProcessOneSample() {
86   if (!ticks_from_vm_buffer_.IsEmpty()
87       && ticks_from_vm_buffer_.Peek()->order ==
88          last_processed_code_event_id_) {
89     TickSampleEventRecord record;
90     ticks_from_vm_buffer_.Dequeue(&record);
91     generator_->RecordTickSample(record.sample);
92     return OneSampleProcessed;
93   }
94 
95   const TickSampleEventRecord* record = ticks_buffer_.Peek();
96   if (record == NULL) {
97     if (ticks_from_vm_buffer_.IsEmpty()) return NoSamplesInQueue;
98     return FoundSampleForNextCodeEvent;
99   }
100   if (record->order != last_processed_code_event_id_) {
101     return FoundSampleForNextCodeEvent;
102   }
103   generator_->RecordTickSample(record->sample);
104   ticks_buffer_.Remove();
105   return OneSampleProcessed;
106 }
107 
108 
Run()109 void ProfilerEventsProcessor::Run() {
110   while (running_) {
111     base::ElapsedTimer timer;
112     timer.Start();
113     // Keep processing existing events until we need to do next sample.
114     do {
115       if (FoundSampleForNextCodeEvent == ProcessOneSample()) {
116         // All ticks of the current last_processed_code_event_id_ are
117         // processed, proceed to the next code event.
118         ProcessCodeEvent();
119       }
120     } while (!timer.HasExpired(period_));
121 
122     // Schedule next sample. sampler_ is NULL in tests.
123     if (sampler_) sampler_->DoSample();
124   }
125 
126   // Process remaining tick events.
127   do {
128     SampleProcessingResult result;
129     do {
130       result = ProcessOneSample();
131     } while (result == OneSampleProcessed);
132   } while (ProcessCodeEvent());
133 }
134 
135 
operator new(size_t size)136 void* ProfilerEventsProcessor::operator new(size_t size) {
137   return AlignedAlloc(size, V8_ALIGNOF(ProfilerEventsProcessor));
138 }
139 
140 
operator delete(void * ptr)141 void ProfilerEventsProcessor::operator delete(void* ptr) {
142   AlignedFree(ptr);
143 }
144 
145 
GetProfilesCount()146 int CpuProfiler::GetProfilesCount() {
147   // The count of profiles doesn't depend on a security token.
148   return profiles_->profiles()->length();
149 }
150 
151 
GetProfile(int index)152 CpuProfile* CpuProfiler::GetProfile(int index) {
153   return profiles_->profiles()->at(index);
154 }
155 
156 
DeleteAllProfiles()157 void CpuProfiler::DeleteAllProfiles() {
158   if (is_profiling_) StopProcessor();
159   ResetProfiles();
160 }
161 
162 
DeleteProfile(CpuProfile * profile)163 void CpuProfiler::DeleteProfile(CpuProfile* profile) {
164   profiles_->RemoveProfile(profile);
165   delete profile;
166   if (profiles_->profiles()->is_empty() && !is_profiling_) {
167     // If this was the last profile, clean up all accessory data as well.
168     ResetProfiles();
169   }
170 }
171 
172 
FilterOutCodeCreateEvent(Logger::LogEventsAndTags tag)173 static bool FilterOutCodeCreateEvent(Logger::LogEventsAndTags tag) {
174   return FLAG_prof_browser_mode
175       && (tag != Logger::CALLBACK_TAG
176           && tag != Logger::FUNCTION_TAG
177           && tag != Logger::LAZY_COMPILE_TAG
178           && tag != Logger::REG_EXP_TAG
179           && tag != Logger::SCRIPT_TAG);
180 }
181 
182 
CallbackEvent(Name * name,Address entry_point)183 void CpuProfiler::CallbackEvent(Name* name, Address entry_point) {
184   if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return;
185   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
186   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
187   rec->start = entry_point;
188   rec->entry = profiles_->NewCodeEntry(
189       Logger::CALLBACK_TAG,
190       profiles_->GetName(name));
191   rec->size = 1;
192   rec->shared = NULL;
193   processor_->Enqueue(evt_rec);
194 }
195 
196 
CodeCreateEvent(Logger::LogEventsAndTags tag,Code * code,const char * name)197 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
198                                   Code* code,
199                                   const char* name) {
200   if (FilterOutCodeCreateEvent(tag)) return;
201   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
202   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
203   rec->start = code->address();
204   rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name));
205   rec->size = code->ExecutableSize();
206   rec->shared = NULL;
207   processor_->Enqueue(evt_rec);
208 }
209 
210 
CodeCreateEvent(Logger::LogEventsAndTags tag,Code * code,Name * name)211 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
212                                   Code* code,
213                                   Name* name) {
214   if (FilterOutCodeCreateEvent(tag)) return;
215   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
216   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
217   rec->start = code->address();
218   rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name));
219   rec->size = code->ExecutableSize();
220   rec->shared = NULL;
221   processor_->Enqueue(evt_rec);
222 }
223 
224 
CodeCreateEvent(Logger::LogEventsAndTags tag,Code * code,SharedFunctionInfo * shared,CompilationInfo * info,Name * script_name)225 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, Code* code,
226                                   SharedFunctionInfo* shared,
227                                   CompilationInfo* info, Name* script_name) {
228   if (FilterOutCodeCreateEvent(tag)) return;
229   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
230   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
231   rec->start = code->address();
232   rec->entry = profiles_->NewCodeEntry(
233       tag, profiles_->GetFunctionName(shared->DebugName()),
234       CodeEntry::kEmptyNamePrefix, profiles_->GetName(script_name));
235   if (info) {
236     rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges());
237   }
238   if (shared->script()->IsScript()) {
239     DCHECK(Script::cast(shared->script()));
240     Script* script = Script::cast(shared->script());
241     rec->entry->set_script_id(script->id()->value());
242     rec->entry->set_bailout_reason(
243         GetBailoutReason(shared->DisableOptimizationReason()));
244   }
245   rec->size = code->ExecutableSize();
246   rec->shared = shared->address();
247   processor_->Enqueue(evt_rec);
248 }
249 
250 
CodeCreateEvent(Logger::LogEventsAndTags tag,Code * code,SharedFunctionInfo * shared,CompilationInfo * info,Name * script_name,int line,int column)251 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, Code* code,
252                                   SharedFunctionInfo* shared,
253                                   CompilationInfo* info, Name* script_name,
254                                   int line, int column) {
255   if (FilterOutCodeCreateEvent(tag)) return;
256   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
257   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
258   rec->start = code->address();
259   rec->entry = profiles_->NewCodeEntry(
260       tag, profiles_->GetFunctionName(shared->DebugName()),
261       CodeEntry::kEmptyNamePrefix, profiles_->GetName(script_name), line,
262       column);
263   if (info) {
264     rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges());
265   }
266   DCHECK(Script::cast(shared->script()));
267   Script* script = Script::cast(shared->script());
268   rec->entry->set_script_id(script->id()->value());
269   rec->size = code->ExecutableSize();
270   rec->shared = shared->address();
271   rec->entry->set_bailout_reason(
272       GetBailoutReason(shared->DisableOptimizationReason()));
273   processor_->Enqueue(evt_rec);
274 }
275 
276 
CodeCreateEvent(Logger::LogEventsAndTags tag,Code * code,int args_count)277 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
278                                   Code* code,
279                                   int args_count) {
280   if (FilterOutCodeCreateEvent(tag)) return;
281   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
282   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
283   rec->start = code->address();
284   rec->entry = profiles_->NewCodeEntry(
285       tag,
286       profiles_->GetName(args_count),
287       "args_count: ");
288   rec->size = code->ExecutableSize();
289   rec->shared = NULL;
290   processor_->Enqueue(evt_rec);
291 }
292 
293 
CodeMoveEvent(Address from,Address to)294 void CpuProfiler::CodeMoveEvent(Address from, Address to) {
295   CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE);
296   CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
297   rec->from = from;
298   rec->to = to;
299   processor_->Enqueue(evt_rec);
300 }
301 
302 
CodeDisableOptEvent(Code * code,SharedFunctionInfo * shared)303 void CpuProfiler::CodeDisableOptEvent(Code* code, SharedFunctionInfo* shared) {
304   CodeEventsContainer evt_rec(CodeEventRecord::CODE_DISABLE_OPT);
305   CodeDisableOptEventRecord* rec = &evt_rec.CodeDisableOptEventRecord_;
306   rec->start = code->address();
307   rec->bailout_reason = GetBailoutReason(shared->DisableOptimizationReason());
308   processor_->Enqueue(evt_rec);
309 }
310 
311 
CodeDeleteEvent(Address from)312 void CpuProfiler::CodeDeleteEvent(Address from) {
313 }
314 
315 
SharedFunctionInfoMoveEvent(Address from,Address to)316 void CpuProfiler::SharedFunctionInfoMoveEvent(Address from, Address to) {
317   CodeEventsContainer evt_rec(CodeEventRecord::SHARED_FUNC_MOVE);
318   SharedFunctionInfoMoveEventRecord* rec =
319       &evt_rec.SharedFunctionInfoMoveEventRecord_;
320   rec->from = from;
321   rec->to = to;
322   processor_->Enqueue(evt_rec);
323 }
324 
325 
GetterCallbackEvent(Name * name,Address entry_point)326 void CpuProfiler::GetterCallbackEvent(Name* name, Address entry_point) {
327   if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return;
328   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
329   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
330   rec->start = entry_point;
331   rec->entry = profiles_->NewCodeEntry(
332       Logger::CALLBACK_TAG,
333       profiles_->GetName(name),
334       "get ");
335   rec->size = 1;
336   rec->shared = NULL;
337   processor_->Enqueue(evt_rec);
338 }
339 
340 
RegExpCodeCreateEvent(Code * code,String * source)341 void CpuProfiler::RegExpCodeCreateEvent(Code* code, String* source) {
342   if (FilterOutCodeCreateEvent(Logger::REG_EXP_TAG)) return;
343   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
344   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
345   rec->start = code->address();
346   rec->entry = profiles_->NewCodeEntry(
347       Logger::REG_EXP_TAG,
348       profiles_->GetName(source),
349       "RegExp: ");
350   rec->size = code->ExecutableSize();
351   processor_->Enqueue(evt_rec);
352 }
353 
354 
SetterCallbackEvent(Name * name,Address entry_point)355 void CpuProfiler::SetterCallbackEvent(Name* name, Address entry_point) {
356   if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return;
357   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
358   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
359   rec->start = entry_point;
360   rec->entry = profiles_->NewCodeEntry(
361       Logger::CALLBACK_TAG,
362       profiles_->GetName(name),
363       "set ");
364   rec->size = 1;
365   rec->shared = NULL;
366   processor_->Enqueue(evt_rec);
367 }
368 
369 
CpuProfiler(Isolate * isolate)370 CpuProfiler::CpuProfiler(Isolate* isolate)
371     : isolate_(isolate),
372       sampling_interval_(base::TimeDelta::FromMicroseconds(
373           FLAG_cpu_profiler_sampling_interval)),
374       profiles_(new CpuProfilesCollection(isolate->heap())),
375       generator_(NULL),
376       processor_(NULL),
377       is_profiling_(false) {
378 }
379 
380 
CpuProfiler(Isolate * isolate,CpuProfilesCollection * test_profiles,ProfileGenerator * test_generator,ProfilerEventsProcessor * test_processor)381 CpuProfiler::CpuProfiler(Isolate* isolate,
382                          CpuProfilesCollection* test_profiles,
383                          ProfileGenerator* test_generator,
384                          ProfilerEventsProcessor* test_processor)
385     : isolate_(isolate),
386       sampling_interval_(base::TimeDelta::FromMicroseconds(
387           FLAG_cpu_profiler_sampling_interval)),
388       profiles_(test_profiles),
389       generator_(test_generator),
390       processor_(test_processor),
391       is_profiling_(false) {
392 }
393 
394 
~CpuProfiler()395 CpuProfiler::~CpuProfiler() {
396   DCHECK(!is_profiling_);
397   delete profiles_;
398 }
399 
400 
set_sampling_interval(base::TimeDelta value)401 void CpuProfiler::set_sampling_interval(base::TimeDelta value) {
402   DCHECK(!is_profiling_);
403   sampling_interval_ = value;
404 }
405 
406 
ResetProfiles()407 void CpuProfiler::ResetProfiles() {
408   delete profiles_;
409   profiles_ = new CpuProfilesCollection(isolate()->heap());
410 }
411 
412 
StartProfiling(const char * title,bool record_samples)413 void CpuProfiler::StartProfiling(const char* title, bool record_samples) {
414   if (profiles_->StartProfiling(title, record_samples)) {
415     StartProcessorIfNotStarted();
416   }
417 }
418 
419 
StartProfiling(String * title,bool record_samples)420 void CpuProfiler::StartProfiling(String* title, bool record_samples) {
421   StartProfiling(profiles_->GetName(title), record_samples);
422 }
423 
424 
StartProcessorIfNotStarted()425 void CpuProfiler::StartProcessorIfNotStarted() {
426   if (processor_ != NULL) {
427     processor_->AddCurrentStack(isolate_);
428     return;
429   }
430   Logger* logger = isolate_->logger();
431   // Disable logging when using the new implementation.
432   saved_is_logging_ = logger->is_logging_;
433   logger->is_logging_ = false;
434   generator_ = new ProfileGenerator(profiles_);
435   Sampler* sampler = logger->sampler();
436   processor_ = new ProfilerEventsProcessor(
437       generator_, sampler, sampling_interval_);
438   is_profiling_ = true;
439   // Enumerate stuff we already have in the heap.
440   DCHECK(isolate_->heap()->HasBeenSetUp());
441   if (!FLAG_prof_browser_mode) {
442     logger->LogCodeObjects();
443   }
444   logger->LogCompiledFunctions();
445   logger->LogAccessorCallbacks();
446   LogBuiltins();
447   // Enable stack sampling.
448   sampler->SetHasProcessingThread(true);
449   sampler->IncreaseProfilingDepth();
450   processor_->AddCurrentStack(isolate_);
451   processor_->StartSynchronously();
452 }
453 
454 
StopProfiling(const char * title)455 CpuProfile* CpuProfiler::StopProfiling(const char* title) {
456   if (!is_profiling_) return NULL;
457   StopProcessorIfLastProfile(title);
458   CpuProfile* result = profiles_->StopProfiling(title);
459   if (result != NULL) {
460     result->Print();
461   }
462   return result;
463 }
464 
465 
StopProfiling(String * title)466 CpuProfile* CpuProfiler::StopProfiling(String* title) {
467   if (!is_profiling_) return NULL;
468   const char* profile_title = profiles_->GetName(title);
469   StopProcessorIfLastProfile(profile_title);
470   return profiles_->StopProfiling(profile_title);
471 }
472 
473 
StopProcessorIfLastProfile(const char * title)474 void CpuProfiler::StopProcessorIfLastProfile(const char* title) {
475   if (profiles_->IsLastProfile(title)) StopProcessor();
476 }
477 
478 
StopProcessor()479 void CpuProfiler::StopProcessor() {
480   Logger* logger = isolate_->logger();
481   Sampler* sampler = reinterpret_cast<Sampler*>(logger->ticker_);
482   is_profiling_ = false;
483   processor_->StopSynchronously();
484   delete processor_;
485   delete generator_;
486   processor_ = NULL;
487   generator_ = NULL;
488   sampler->SetHasProcessingThread(false);
489   sampler->DecreaseProfilingDepth();
490   logger->is_logging_ = saved_is_logging_;
491 }
492 
493 
LogBuiltins()494 void CpuProfiler::LogBuiltins() {
495   Builtins* builtins = isolate_->builtins();
496   DCHECK(builtins->is_initialized());
497   for (int i = 0; i < Builtins::builtin_count; i++) {
498     CodeEventsContainer evt_rec(CodeEventRecord::REPORT_BUILTIN);
499     ReportBuiltinEventRecord* rec = &evt_rec.ReportBuiltinEventRecord_;
500     Builtins::Name id = static_cast<Builtins::Name>(i);
501     rec->start = builtins->builtin(id)->address();
502     rec->builtin_id = id;
503     processor_->Enqueue(evt_rec);
504   }
505 }
506 
507 
508 } }  // namespace v8::internal
509