OLD | NEW |
---|---|
1 // Copyright 2012 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/v8.h" | 5 #include "src/v8.h" |
6 | 6 |
7 #include "src/profile-generator-inl.h" | 7 #include "src/profile-generator-inl.h" |
8 | 8 |
9 #include "src/compiler.h" | 9 #include "src/compiler.h" |
10 #include "src/debug.h" | 10 #include "src/debug.h" |
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
200 if (map_entry->value == NULL) { | 200 if (map_entry->value == NULL) { |
201 // New node added. | 201 // New node added. |
202 ProfileNode* new_node = new ProfileNode(tree_, entry); | 202 ProfileNode* new_node = new ProfileNode(tree_, entry); |
203 map_entry->value = new_node; | 203 map_entry->value = new_node; |
204 children_list_.Add(new_node); | 204 children_list_.Add(new_node); |
205 } | 205 } |
206 return reinterpret_cast<ProfileNode*>(map_entry->value); | 206 return reinterpret_cast<ProfileNode*>(map_entry->value); |
207 } | 207 } |
208 | 208 |
209 | 209 |
210 void ProfileNode::IncrementLineTicks(int src_line) { | |
211 if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) return; | |
212 // Increment a hit counter of a certain source line. | |
213 // Add a new source line if not found. | |
214 HashMap::Entry* e = | |
215 line_ticks_.Lookup(reinterpret_cast<void*>(src_line), src_line, true); | |
216 if (NULL != e) { | |
alph
2014/07/29 12:55:56
It couldn't be NULL if insert arg is true.
style:
| |
217 e->value = static_cast<char*>(e->value) + 1; | |
alph
2014/07/29 12:55:56
why char*? uintptr_t?
yurys
2014/07/29 13:15:10
intptr_t
| |
218 } | |
219 } | |
220 | |
221 | |
222 bool ProfileNode::GetLineTicks(LineTick* entries, | |
223 unsigned int number) const { | |
224 if (NULL == entries || number == 0) { | |
alph
2014/07/29 12:55:56
ditto
| |
225 return false; | |
226 } | |
227 | |
228 unsigned lineNumber = line_ticks_.occupancy(); | |
229 if (lineNumber == 0) return false; | |
alph
2014/07/29 12:55:56
should return true.
| |
230 if (number < lineNumber) return false; | |
231 | |
232 LineTick* entry = entries; | |
233 | |
234 for (HashMap::Entry* p = line_ticks_.Start(); | |
235 p != NULL; | |
236 p = line_ticks_.Next(p), entry++) { | |
237 entry->line = reinterpret_cast<intptr_t>(p->key); | |
alph
2014/07/29 12:55:56
entry->line is unsigned int, i.e. 32 bits. So it s
| |
238 entry->ticks = reinterpret_cast<intptr_t>(p->value); | |
239 } | |
240 | |
241 return true; | |
242 } | |
243 | |
244 | |
210 void ProfileNode::Print(int indent) { | 245 void ProfileNode::Print(int indent) { |
211 base::OS::Print("%5u %*s %s%s %d #%d %s", self_ticks_, indent, "", | 246 base::OS::Print("%5u %*s %s%s %d #%d %s", self_ticks_, indent, "", |
212 entry_->name_prefix(), entry_->name(), entry_->script_id(), | 247 entry_->name_prefix(), entry_->name(), entry_->script_id(), |
213 id(), entry_->bailout_reason()); | 248 id(), entry_->bailout_reason()); |
214 if (entry_->resource_name()[0] != '\0') | 249 if (entry_->resource_name()[0] != '\0') |
215 base::OS::Print(" %s:%d", entry_->resource_name(), entry_->line_number()); | 250 base::OS::Print(" %s:%d", entry_->resource_name(), entry_->line_number()); |
216 base::OS::Print("\n"); | 251 base::OS::Print("\n"); |
217 for (HashMap::Entry* p = children_.Start(); | 252 for (HashMap::Entry* p = children_.Start(); |
218 p != NULL; | 253 p != NULL; |
219 p = children_.Next(p)) { | 254 p = children_.Next(p)) { |
(...skipping 20 matching lines...) Expand all Loading... | |
240 root_(new ProfileNode(this, &root_entry_)) { | 275 root_(new ProfileNode(this, &root_entry_)) { |
241 } | 276 } |
242 | 277 |
243 | 278 |
244 ProfileTree::~ProfileTree() { | 279 ProfileTree::~ProfileTree() { |
245 DeleteNodesCallback cb; | 280 DeleteNodesCallback cb; |
246 TraverseDepthFirst(&cb); | 281 TraverseDepthFirst(&cb); |
247 } | 282 } |
248 | 283 |
249 | 284 |
250 ProfileNode* ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path) { | 285 ProfileNode* ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path, |
286 int src_line) { | |
251 ProfileNode* node = root_; | 287 ProfileNode* node = root_; |
252 for (CodeEntry** entry = path.start() + path.length() - 1; | 288 for (CodeEntry** entry = path.start() + path.length() - 1; |
253 entry != path.start() - 1; | 289 entry != path.start() - 1; |
254 --entry) { | 290 --entry) { |
255 if (*entry != NULL) { | 291 if (*entry != NULL) { |
256 node = node->FindOrAddChild(*entry); | 292 node = node->FindOrAddChild(*entry); |
257 } | 293 } |
258 } | 294 } |
259 node->IncrementSelfTicks(); | 295 node->IncrementSelfTicks(); |
296 if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) { | |
297 node->IncrementLineTicks(src_line); | |
298 } | |
260 return node; | 299 return node; |
261 } | 300 } |
262 | 301 |
263 | 302 |
264 void ProfileTree::AddPathFromStart(const Vector<CodeEntry*>& path) { | 303 void ProfileTree::AddPathFromStart(const Vector<CodeEntry*>& path, |
304 int src_line) { | |
265 ProfileNode* node = root_; | 305 ProfileNode* node = root_; |
266 for (CodeEntry** entry = path.start(); | 306 for (CodeEntry** entry = path.start(); |
267 entry != path.start() + path.length(); | 307 entry != path.start() + path.length(); |
268 ++entry) { | 308 ++entry) { |
269 if (*entry != NULL) { | 309 if (*entry != NULL) { |
270 node = node->FindOrAddChild(*entry); | 310 node = node->FindOrAddChild(*entry); |
271 } | 311 } |
272 } | 312 } |
273 node->IncrementSelfTicks(); | 313 node->IncrementSelfTicks(); |
314 if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) { | |
315 node->IncrementLineTicks(src_line); | |
316 } | |
274 } | 317 } |
275 | 318 |
276 | 319 |
277 struct NodesPair { | 320 struct NodesPair { |
278 NodesPair(ProfileNode* src, ProfileNode* dst) | 321 NodesPair(ProfileNode* src, ProfileNode* dst) |
279 : src(src), dst(dst) { } | 322 : src(src), dst(dst) { } |
280 ProfileNode* src; | 323 ProfileNode* src; |
281 ProfileNode* dst; | 324 ProfileNode* dst; |
282 }; | 325 }; |
283 | 326 |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
325 | 368 |
326 | 369 |
327 CpuProfile::CpuProfile(const char* title, bool record_samples) | 370 CpuProfile::CpuProfile(const char* title, bool record_samples) |
328 : title_(title), | 371 : title_(title), |
329 record_samples_(record_samples), | 372 record_samples_(record_samples), |
330 start_time_(base::TimeTicks::HighResolutionNow()) { | 373 start_time_(base::TimeTicks::HighResolutionNow()) { |
331 } | 374 } |
332 | 375 |
333 | 376 |
334 void CpuProfile::AddPath(base::TimeTicks timestamp, | 377 void CpuProfile::AddPath(base::TimeTicks timestamp, |
335 const Vector<CodeEntry*>& path) { | 378 const Vector<CodeEntry*>& path, |
336 ProfileNode* top_frame_node = top_down_.AddPathFromEnd(path); | 379 int src_line) { |
yurys
2014/07/29 13:15:10
It seems that with per line self tick count it wou
| |
380 ProfileNode* top_frame_node = top_down_.AddPathFromEnd(path, src_line); | |
337 if (record_samples_) { | 381 if (record_samples_) { |
338 timestamps_.Add(timestamp); | 382 timestamps_.Add(timestamp); |
339 samples_.Add(top_frame_node); | 383 samples_.Add(top_frame_node); |
340 } | 384 } |
341 } | 385 } |
342 | 386 |
343 | 387 |
344 void CpuProfile::CalculateTotalTicksAndSamplingRate() { | 388 void CpuProfile::CalculateTotalTicksAndSamplingRate() { |
345 end_time_ = base::TimeTicks::HighResolutionNow(); | 389 end_time_ = base::TimeTicks::HighResolutionNow(); |
346 } | 390 } |
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
515 if (profile == finished_profiles_[i]) { | 559 if (profile == finished_profiles_[i]) { |
516 finished_profiles_.Remove(i); | 560 finished_profiles_.Remove(i); |
517 return; | 561 return; |
518 } | 562 } |
519 } | 563 } |
520 UNREACHABLE(); | 564 UNREACHABLE(); |
521 } | 565 } |
522 | 566 |
523 | 567 |
524 void CpuProfilesCollection::AddPathToCurrentProfiles( | 568 void CpuProfilesCollection::AddPathToCurrentProfiles( |
525 base::TimeTicks timestamp, const Vector<CodeEntry*>& path) { | 569 base::TimeTicks timestamp, const Vector<CodeEntry*>& path, |
570 int src_line) { | |
526 // As starting / stopping profiles is rare relatively to this | 571 // As starting / stopping profiles is rare relatively to this |
527 // method, we don't bother minimizing the duration of lock holding, | 572 // method, we don't bother minimizing the duration of lock holding, |
528 // e.g. copying contents of the list to a local vector. | 573 // e.g. copying contents of the list to a local vector. |
529 current_profiles_semaphore_.Wait(); | 574 current_profiles_semaphore_.Wait(); |
530 for (int i = 0; i < current_profiles_.length(); ++i) { | 575 for (int i = 0; i < current_profiles_.length(); ++i) { |
531 current_profiles_[i]->AddPath(timestamp, path); | 576 current_profiles_[i]->AddPath(timestamp, path, src_line); |
532 } | 577 } |
533 current_profiles_semaphore_.Signal(); | 578 current_profiles_semaphore_.Signal(); |
534 } | 579 } |
535 | 580 |
536 | 581 |
537 CodeEntry* CpuProfilesCollection::NewCodeEntry( | 582 CodeEntry* CpuProfilesCollection::NewCodeEntry( |
538 Logger::LogEventsAndTags tag, | 583 Logger::LogEventsAndTags tag, |
539 const char* name, | 584 const char* name, |
540 const char* name_prefix, | 585 const char* name_prefix, |
541 const char* resource_name, | 586 const char* resource_name, |
542 int line_number, | 587 int line_number, |
543 int column_number) { | 588 int column_number, |
589 JITLineInfoTable* line_info) { | |
544 CodeEntry* code_entry = new CodeEntry(tag, | 590 CodeEntry* code_entry = new CodeEntry(tag, |
545 name, | 591 name, |
546 name_prefix, | 592 name_prefix, |
547 resource_name, | 593 resource_name, |
548 line_number, | 594 line_number, |
549 column_number); | 595 column_number, |
596 line_info); | |
550 code_entries_.Add(code_entry); | 597 code_entries_.Add(code_entry); |
551 return code_entry; | 598 return code_entry; |
552 } | 599 } |
553 | 600 |
554 | 601 |
555 const char* const ProfileGenerator::kAnonymousFunctionName = | 602 const char* const ProfileGenerator::kAnonymousFunctionName = |
556 "(anonymous function)"; | 603 "(anonymous function)"; |
557 const char* const ProfileGenerator::kProgramEntryName = | 604 const char* const ProfileGenerator::kProgramEntryName = |
558 "(program)"; | 605 "(program)"; |
559 const char* const ProfileGenerator::kIdleEntryName = | 606 const char* const ProfileGenerator::kIdleEntryName = |
(...skipping 12 matching lines...) Expand all Loading... | |
572 profiles->NewCodeEntry(Logger::FUNCTION_TAG, kIdleEntryName)), | 619 profiles->NewCodeEntry(Logger::FUNCTION_TAG, kIdleEntryName)), |
573 gc_entry_( | 620 gc_entry_( |
574 profiles->NewCodeEntry(Logger::BUILTIN_TAG, | 621 profiles->NewCodeEntry(Logger::BUILTIN_TAG, |
575 kGarbageCollectorEntryName)), | 622 kGarbageCollectorEntryName)), |
576 unresolved_entry_( | 623 unresolved_entry_( |
577 profiles->NewCodeEntry(Logger::FUNCTION_TAG, | 624 profiles->NewCodeEntry(Logger::FUNCTION_TAG, |
578 kUnresolvedFunctionName)) { | 625 kUnresolvedFunctionName)) { |
579 } | 626 } |
580 | 627 |
581 | 628 |
629 static int GetSourceLine(unsigned int pc_offset, CodeEntry* entry) { | |
630 int src_line = v8::CpuProfileNode::kNoLineNumberInfo; | |
631 const JITLineInfoTable& table = entry->line_info(); | |
632 if (!table.entries()->length()) return src_line; | |
633 src_line = table.GetSourceLineNumber(pc_offset); | |
634 return src_line; | |
635 } | |
alph
2014/07/29 12:55:56
could you please make it more readable, e.g.:
int
| |
636 | |
637 | |
582 void ProfileGenerator::RecordTickSample(const TickSample& sample) { | 638 void ProfileGenerator::RecordTickSample(const TickSample& sample) { |
583 // Allocate space for stack frames + pc + function + vm-state. | 639 // Allocate space for stack frames + pc + function + vm-state. |
584 ScopedVector<CodeEntry*> entries(sample.frames_count + 3); | 640 ScopedVector<CodeEntry*> entries(sample.frames_count + 3); |
585 // As actual number of decoded code entries may vary, initialize | 641 // As actual number of decoded code entries may vary, initialize |
586 // entries vector with NULL values. | 642 // entries vector with NULL values. |
587 CodeEntry** entry = entries.start(); | 643 CodeEntry** entry = entries.start(); |
588 memset(entry, 0, entries.length() * sizeof(*entry)); | 644 memset(entry, 0, entries.length() * sizeof(*entry)); |
645 int src_line = v8::CpuProfileNode::kNoLineNumberInfo; | |
589 if (sample.pc != NULL) { | 646 if (sample.pc != NULL) { |
590 if (sample.has_external_callback && sample.state == EXTERNAL && | 647 if (sample.has_external_callback && sample.state == EXTERNAL && |
591 sample.top_frame_type == StackFrame::EXIT) { | 648 sample.top_frame_type == StackFrame::EXIT) { |
592 // Don't use PC when in external callback code, as it can point | 649 // Don't use PC when in external callback code, as it can point |
593 // inside callback's code, and we will erroneously report | 650 // inside callback's code, and we will erroneously report |
594 // that a callback calls itself. | 651 // that a callback calls itself. |
595 *entry++ = code_map_.FindEntry(sample.external_callback); | 652 *entry++ = code_map_.FindEntry(sample.external_callback); |
596 } else { | 653 } else { |
597 Address start; | 654 Address start; |
598 CodeEntry* pc_entry = code_map_.FindEntry(sample.pc, &start); | 655 CodeEntry* pc_entry = code_map_.FindEntry(sample.pc, &start); |
599 // If pc is in the function code before it set up stack frame or after the | 656 // If pc is in the function code before it set up stack frame or after the |
600 // frame was destroyed SafeStackFrameIterator incorrectly thinks that | 657 // frame was destroyed SafeStackFrameIterator incorrectly thinks that |
601 // ebp contains return address of the current function and skips caller's | 658 // ebp contains return address of the current function and skips caller's |
602 // frame. Check for this case and just skip such samples. | 659 // frame. Check for this case and just skip such samples. |
603 if (pc_entry) { | 660 if (pc_entry) { |
661 Code* code = Code::cast(HeapObject::FromAddress(start)); | |
604 List<OffsetRange>* ranges = pc_entry->no_frame_ranges(); | 662 List<OffsetRange>* ranges = pc_entry->no_frame_ranges(); |
663 src_line = GetSourceLine(sample.pc - code->instruction_start(), | |
664 pc_entry); | |
605 if (ranges) { | 665 if (ranges) { |
606 Code* code = Code::cast(HeapObject::FromAddress(start)); | |
607 int pc_offset = static_cast<int>( | 666 int pc_offset = static_cast<int>( |
608 sample.pc - code->instruction_start()); | 667 sample.pc - code->instruction_start()); |
609 for (int i = 0; i < ranges->length(); i++) { | 668 for (int i = 0; i < ranges->length(); i++) { |
610 OffsetRange& range = ranges->at(i); | 669 OffsetRange& range = ranges->at(i); |
611 if (range.from <= pc_offset && pc_offset < range.to) { | 670 if (range.from <= pc_offset && pc_offset < range.to) { |
612 return; | 671 return; |
613 } | 672 } |
614 } | 673 } |
615 } | 674 } |
616 *entry++ = pc_entry; | 675 *entry++ = pc_entry; |
617 | 676 |
618 if (pc_entry->builtin_id() == Builtins::kFunctionCall || | 677 if (pc_entry->builtin_id() == Builtins::kFunctionCall || |
619 pc_entry->builtin_id() == Builtins::kFunctionApply) { | 678 pc_entry->builtin_id() == Builtins::kFunctionApply) { |
620 // When current function is FunctionCall or FunctionApply builtin the | 679 // When current function is FunctionCall or FunctionApply builtin the |
621 // top frame is either frame of the calling JS function or internal | 680 // top frame is either frame of the calling JS function or internal |
622 // frame. In the latter case we know the caller for sure but in the | 681 // frame. In the latter case we know the caller for sure but in the |
623 // former case we don't so we simply replace the frame with | 682 // former case we don't so we simply replace the frame with |
624 // 'unresolved' entry. | 683 // 'unresolved' entry. |
625 if (sample.top_frame_type == StackFrame::JAVA_SCRIPT) { | 684 if (sample.top_frame_type == StackFrame::JAVA_SCRIPT) { |
626 *entry++ = unresolved_entry_; | 685 *entry++ = unresolved_entry_; |
627 } | 686 } |
628 } | 687 } |
629 } | 688 } |
630 } | 689 } |
631 | 690 |
691 bool unresolved_src = (src_line == v8::CpuProfileNode::kNoLineNumberInfo) ? | |
yurys
2014/07/29 13:15:10
bool unresolved_src = src_line == v8::CpuProfileNo
| |
692 true : false; | |
693 | |
632 for (const Address* stack_pos = sample.stack, | 694 for (const Address* stack_pos = sample.stack, |
633 *stack_end = stack_pos + sample.frames_count; | 695 *stack_end = stack_pos + sample.frames_count; |
634 stack_pos != stack_end; | 696 stack_pos != stack_end; |
635 ++stack_pos) { | 697 ++stack_pos) { |
636 *entry++ = code_map_.FindEntry(*stack_pos); | 698 Address start = NULL; |
699 *entry = code_map_.FindEntry(*stack_pos, &start); | |
700 if (unresolved_src && NULL != *entry) { | |
701 Code* code = Code::cast(HeapObject::FromAddress(start)); | |
702 src_line = GetSourceLine(*stack_pos - code->instruction_start(), | |
703 *entry); | |
704 if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) { | |
705 src_line = (*entry)->line_number(); | |
706 } | |
707 unresolved_src = false; | |
708 } | |
709 entry++; | |
637 } | 710 } |
638 } | 711 } |
639 | 712 |
640 if (FLAG_prof_browser_mode) { | 713 if (FLAG_prof_browser_mode) { |
641 bool no_symbolized_entries = true; | 714 bool no_symbolized_entries = true; |
642 for (CodeEntry** e = entries.start(); e != entry; ++e) { | 715 for (CodeEntry** e = entries.start(); e != entry; ++e) { |
643 if (*e != NULL) { | 716 if (*e != NULL) { |
644 no_symbolized_entries = false; | 717 no_symbolized_entries = false; |
645 break; | 718 break; |
646 } | 719 } |
647 } | 720 } |
648 // If no frames were symbolized, put the VM state entry in. | 721 // If no frames were symbolized, put the VM state entry in. |
649 if (no_symbolized_entries) { | 722 if (no_symbolized_entries) { |
650 *entry++ = EntryForVMState(sample.state); | 723 *entry++ = EntryForVMState(sample.state); |
651 } | 724 } |
652 } | 725 } |
653 | 726 |
654 profiles_->AddPathToCurrentProfiles(sample.timestamp, entries); | 727 profiles_->AddPathToCurrentProfiles(sample.timestamp, entries, src_line); |
655 } | 728 } |
656 | 729 |
657 | 730 |
658 CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { | 731 CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { |
659 switch (tag) { | 732 switch (tag) { |
660 case GC: | 733 case GC: |
661 return gc_entry_; | 734 return gc_entry_; |
662 case JS: | 735 case JS: |
663 case COMPILER: | 736 case COMPILER: |
664 // DOM events handlers are reported as OTHER / EXTERNAL entries. | 737 // DOM events handlers are reported as OTHER / EXTERNAL entries. |
665 // To avoid confusing people, let's put all these entries into | 738 // To avoid confusing people, let's put all these entries into |
666 // one bucket. | 739 // one bucket. |
667 case OTHER: | 740 case OTHER: |
668 case EXTERNAL: | 741 case EXTERNAL: |
669 return program_entry_; | 742 return program_entry_; |
670 case IDLE: | 743 case IDLE: |
671 return idle_entry_; | 744 return idle_entry_; |
672 default: return NULL; | 745 default: return NULL; |
673 } | 746 } |
674 } | 747 } |
675 | 748 |
676 } } // namespace v8::internal | 749 } } // namespace v8::internal |
OLD | NEW |