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.
7 #include "cpu-profiler-inl.h"
10 #include "frames-inl.h"
13 #include "vm-state-inl.h"
15 #include "../include/v8-profiler.h"
20 static const int kProfilerStackSize = 64 * KB;
23 ProfilerEventsProcessor::ProfilerEventsProcessor(
24 ProfileGenerator* generator,
27 : Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)),
28 generator_(generator),
32 last_code_event_id_(0), last_processed_code_event_id_(0) {
36 void ProfilerEventsProcessor::Enqueue(const CodeEventsContainer& event) {
37 event.generic.order = ++last_code_event_id_;
38 events_buffer_.Enqueue(event);
42 void ProfilerEventsProcessor::AddCurrentStack(Isolate* isolate) {
43 TickSampleEventRecord record(last_code_event_id_);
45 StackFrameIterator it(isolate);
47 StackFrame* frame = it.frame();
48 regs.sp = frame->sp();
49 regs.fp = frame->fp();
50 regs.pc = frame->pc();
52 record.sample.Init(isolate, regs);
53 ticks_from_vm_buffer_.Enqueue(record);
57 void ProfilerEventsProcessor::StopSynchronously() {
58 if (!running_) return;
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()); \
73 CODE_EVENTS_TYPE_LIST(PROFILER_TYPE_CASE)
75 #undef PROFILER_TYPE_CASE
76 default: return true; // Skip record.
78 last_processed_code_event_id_ = record.generic.order;
84 ProfilerEventsProcessor::SampleProcessingResult
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;
95 const TickSampleEventRecord* record = ticks_buffer_.Peek();
97 if (ticks_from_vm_buffer_.IsEmpty()) return NoSamplesInQueue;
98 return FoundSampleForNextCodeEvent;
100 if (record->order != last_processed_code_event_id_) {
101 return FoundSampleForNextCodeEvent;
103 generator_->RecordTickSample(record->sample);
104 ticks_buffer_.Remove();
105 return OneSampleProcessed;
109 void ProfilerEventsProcessor::Run() {
113 // Keep processing existing events until we need to do next sample.
115 if (FoundSampleForNextCodeEvent == ProcessOneSample()) {
116 // All ticks of the current last_processed_code_event_id_ are
117 // processed, proceed to the next code event.
120 } while (!timer.HasExpired(period_));
122 // Schedule next sample. sampler_ is NULL in tests.
123 if (sampler_) sampler_->DoSample();
126 // Process remaining tick events.
128 SampleProcessingResult result;
130 result = ProcessOneSample();
131 } while (result == OneSampleProcessed);
132 } while (ProcessCodeEvent());
136 void* ProfilerEventsProcessor::operator new(size_t size) {
137 return AlignedAlloc(size, V8_ALIGNOF(ProfilerEventsProcessor));
141 void ProfilerEventsProcessor::operator delete(void* ptr) {
146 int CpuProfiler::GetProfilesCount() {
147 // The count of profiles doesn't depend on a security token.
148 return profiles_->profiles()->length();
152 CpuProfile* CpuProfiler::GetProfile(int index) {
153 return profiles_->profiles()->at(index);
157 void CpuProfiler::DeleteAllProfiles() {
158 if (is_profiling_) StopProcessor();
163 void CpuProfiler::DeleteProfile(CpuProfile* profile) {
164 profiles_->RemoveProfile(profile);
166 if (profiles_->profiles()->is_empty() && !is_profiling_) {
167 // If this was the last profile, clean up all accessory data as well.
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);
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));
193 processor_->Enqueue(evt_rec);
197 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
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();
207 processor_->Enqueue(evt_rec);
211 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
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();
221 processor_->Enqueue(evt_rec);
225 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
227 SharedFunctionInfo* shared,
228 CompilationInfo* info,
230 if (FilterOutCodeCreateEvent(tag)) return;
231 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
232 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
233 rec->start = code->address();
234 rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name));
236 rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges());
238 if (shared->script()->IsScript()) {
239 ASSERT(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()));
245 rec->size = code->ExecutableSize();
246 rec->shared = shared->address();
247 processor_->Enqueue(evt_rec);
251 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
253 SharedFunctionInfo* shared,
254 CompilationInfo* info,
255 Name* source, int line, int column) {
256 if (FilterOutCodeCreateEvent(tag)) return;
257 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
258 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
259 rec->start = code->address();
260 rec->entry = profiles_->NewCodeEntry(
262 profiles_->GetFunctionName(shared->DebugName()),
263 CodeEntry::kEmptyNamePrefix,
264 profiles_->GetName(source),
268 rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges());
270 ASSERT(Script::cast(shared->script()));
271 Script* script = Script::cast(shared->script());
272 rec->entry->set_script_id(script->id()->value());
273 rec->size = code->ExecutableSize();
274 rec->shared = shared->address();
275 rec->entry->set_bailout_reason(
276 GetBailoutReason(shared->DisableOptimizationReason()));
277 processor_->Enqueue(evt_rec);
281 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
284 if (FilterOutCodeCreateEvent(tag)) return;
285 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
286 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
287 rec->start = code->address();
288 rec->entry = profiles_->NewCodeEntry(
290 profiles_->GetName(args_count),
292 rec->size = code->ExecutableSize();
294 processor_->Enqueue(evt_rec);
298 void CpuProfiler::CodeMoveEvent(Address from, Address to) {
299 CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE);
300 CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
303 processor_->Enqueue(evt_rec);
307 void CpuProfiler::CodeDeleteEvent(Address from) {
311 void CpuProfiler::SharedFunctionInfoMoveEvent(Address from, Address to) {
312 CodeEventsContainer evt_rec(CodeEventRecord::SHARED_FUNC_MOVE);
313 SharedFunctionInfoMoveEventRecord* rec =
314 &evt_rec.SharedFunctionInfoMoveEventRecord_;
317 processor_->Enqueue(evt_rec);
321 void CpuProfiler::GetterCallbackEvent(Name* name, Address entry_point) {
322 if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return;
323 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
324 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
325 rec->start = entry_point;
326 rec->entry = profiles_->NewCodeEntry(
327 Logger::CALLBACK_TAG,
328 profiles_->GetName(name),
332 processor_->Enqueue(evt_rec);
336 void CpuProfiler::RegExpCodeCreateEvent(Code* code, String* source) {
337 if (FilterOutCodeCreateEvent(Logger::REG_EXP_TAG)) return;
338 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
339 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
340 rec->start = code->address();
341 rec->entry = profiles_->NewCodeEntry(
343 profiles_->GetName(source),
345 rec->size = code->ExecutableSize();
346 processor_->Enqueue(evt_rec);
350 void CpuProfiler::SetterCallbackEvent(Name* name, Address entry_point) {
351 if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return;
352 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
353 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
354 rec->start = entry_point;
355 rec->entry = profiles_->NewCodeEntry(
356 Logger::CALLBACK_TAG,
357 profiles_->GetName(name),
361 processor_->Enqueue(evt_rec);
365 CpuProfiler::CpuProfiler(Isolate* isolate)
367 sampling_interval_(TimeDelta::FromMicroseconds(
368 FLAG_cpu_profiler_sampling_interval)),
369 profiles_(new CpuProfilesCollection(isolate->heap())),
372 is_profiling_(false) {
376 CpuProfiler::CpuProfiler(Isolate* isolate,
377 CpuProfilesCollection* test_profiles,
378 ProfileGenerator* test_generator,
379 ProfilerEventsProcessor* test_processor)
381 sampling_interval_(TimeDelta::FromMicroseconds(
382 FLAG_cpu_profiler_sampling_interval)),
383 profiles_(test_profiles),
384 generator_(test_generator),
385 processor_(test_processor),
386 is_profiling_(false) {
390 CpuProfiler::~CpuProfiler() {
391 ASSERT(!is_profiling_);
396 void CpuProfiler::set_sampling_interval(TimeDelta value) {
397 ASSERT(!is_profiling_);
398 sampling_interval_ = value;
402 void CpuProfiler::ResetProfiles() {
404 profiles_ = new CpuProfilesCollection(isolate()->heap());
408 void CpuProfiler::StartProfiling(const char* title, bool record_samples) {
409 if (profiles_->StartProfiling(title, record_samples)) {
410 StartProcessorIfNotStarted();
415 void CpuProfiler::StartProfiling(String* title, bool record_samples) {
416 StartProfiling(profiles_->GetName(title), record_samples);
420 void CpuProfiler::StartProcessorIfNotStarted() {
421 if (processor_ == NULL) {
422 Logger* logger = isolate_->logger();
423 // Disable logging when using the new implementation.
424 saved_is_logging_ = logger->is_logging_;
425 logger->is_logging_ = false;
426 generator_ = new ProfileGenerator(profiles_);
427 Sampler* sampler = logger->sampler();
428 processor_ = new ProfilerEventsProcessor(
429 generator_, sampler, sampling_interval_);
430 is_profiling_ = true;
431 // Enumerate stuff we already have in the heap.
432 ASSERT(isolate_->heap()->HasBeenSetUp());
433 if (!FLAG_prof_browser_mode) {
434 logger->LogCodeObjects();
436 logger->LogCompiledFunctions();
437 logger->LogAccessorCallbacks();
439 // Enable stack sampling.
440 sampler->SetHasProcessingThread(true);
441 sampler->IncreaseProfilingDepth();
442 processor_->AddCurrentStack(isolate_);
443 processor_->StartSynchronously();
448 CpuProfile* CpuProfiler::StopProfiling(const char* title) {
449 if (!is_profiling_) return NULL;
450 StopProcessorIfLastProfile(title);
451 CpuProfile* result = profiles_->StopProfiling(title);
452 if (result != NULL) {
459 CpuProfile* CpuProfiler::StopProfiling(String* title) {
460 if (!is_profiling_) return NULL;
461 const char* profile_title = profiles_->GetName(title);
462 StopProcessorIfLastProfile(profile_title);
463 return profiles_->StopProfiling(profile_title);
467 void CpuProfiler::StopProcessorIfLastProfile(const char* title) {
468 if (profiles_->IsLastProfile(title)) StopProcessor();
472 void CpuProfiler::StopProcessor() {
473 Logger* logger = isolate_->logger();
474 Sampler* sampler = reinterpret_cast<Sampler*>(logger->ticker_);
475 is_profiling_ = false;
476 processor_->StopSynchronously();
481 sampler->SetHasProcessingThread(false);
482 sampler->DecreaseProfilingDepth();
483 logger->is_logging_ = saved_is_logging_;
487 void CpuProfiler::LogBuiltins() {
488 Builtins* builtins = isolate_->builtins();
489 ASSERT(builtins->is_initialized());
490 for (int i = 0; i < Builtins::builtin_count; i++) {
491 CodeEventsContainer evt_rec(CodeEventRecord::REPORT_BUILTIN);
492 ReportBuiltinEventRecord* rec = &evt_rec.ReportBuiltinEventRecord_;
493 Builtins::Name id = static_cast<Builtins::Name>(i);
494 rec->start = builtins->builtin(id)->address();
495 rec->builtin_id = id;
496 processor_->Enqueue(evt_rec);
501 } } // namespace v8::internal