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