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