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