 Chromium Code Reviews
 Chromium Code Reviews Issue 2461003002:
  [profiler] Emit runtime call stats into sampling profile  (Closed)
    
  
    Issue 2461003002:
  [profiler] Emit runtime call stats into sampling profile  (Closed) 
  | 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/profiler/profile-generator.h" | 5 #include "src/profiler/profile-generator.h" | 
| 6 | 6 | 
| 7 #include "src/base/adapters.h" | 7 #include "src/base/adapters.h" | 
| 8 #include "src/debug/debug.h" | 8 #include "src/debug/debug.h" | 
| 9 #include "src/deoptimizer.h" | 9 #include "src/deoptimizer.h" | 
| 10 #include "src/global-handles.h" | 10 #include "src/global-handles.h" | 
| (...skipping 630 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 641 // As starting / stopping profiles is rare relatively to this | 641 // As starting / stopping profiles is rare relatively to this | 
| 642 // method, we don't bother minimizing the duration of lock holding, | 642 // method, we don't bother minimizing the duration of lock holding, | 
| 643 // e.g. copying contents of the list to a local vector. | 643 // e.g. copying contents of the list to a local vector. | 
| 644 current_profiles_semaphore_.Wait(); | 644 current_profiles_semaphore_.Wait(); | 
| 645 for (int i = 0; i < current_profiles_.length(); ++i) { | 645 for (int i = 0; i < current_profiles_.length(); ++i) { | 
| 646 current_profiles_[i]->AddPath(timestamp, path, src_line, update_stats); | 646 current_profiles_[i]->AddPath(timestamp, path, src_line, update_stats); | 
| 647 } | 647 } | 
| 648 current_profiles_semaphore_.Signal(); | 648 current_profiles_semaphore_.Signal(); | 
| 649 } | 649 } | 
| 650 | 650 | 
| 651 ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles) | 651 ProfileGenerator::ProfileGenerator(Isolate* isolate, | 
| 652 : profiles_(profiles) {} | 652 CpuProfilesCollection* profiles) | 
| 653 : isolate_(isolate), profiles_(profiles) {} | |
| 653 | 654 | 
| 654 void ProfileGenerator::RecordTickSample(const TickSample& sample) { | 655 void ProfileGenerator::RecordTickSample(const TickSample& sample) { | 
| 655 std::vector<CodeEntry*> entries; | 656 std::vector<CodeEntry*> entries; | 
| 656 // Conservatively reserve space for stack frames + pc + function + vm-state. | 657 // Conservatively reserve space for stack frames + pc + function + vm-state. | 
| 657 // There could in fact be more of them because of inlined entries. | 658 // There could in fact be more of them because of inlined entries. | 
| 658 entries.reserve(sample.frames_count + 3); | 659 entries.reserve(sample.frames_count + 3); | 
| 659 | 660 | 
| 660 // The ProfileNode knows nothing about all versions of generated code for | 661 // The ProfileNode knows nothing about all versions of generated code for | 
| 661 // the same JS function. The line number information associated with | 662 // the same JS function. The line number information associated with | 
| 662 // the latest version of generated code is used to find a source line number | 663 // the latest version of generated code is used to find a source line number | 
| 663 // for a JS function. Then, the detected source line is passed to | 664 // for a JS function. Then, the detected source line is passed to | 
| 664 // ProfileNode to increase the tick count for this source line. | 665 // ProfileNode to increase the tick count for this source line. | 
| 665 int src_line = v8::CpuProfileNode::kNoLineNumberInfo; | 666 int src_line = v8::CpuProfileNode::kNoLineNumberInfo; | 
| 666 bool src_line_not_found = true; | 667 bool src_line_not_found = true; | 
| 667 | 668 | 
| 668 if (sample.pc != nullptr) { | 669 if (sample.pc != nullptr) { | 
| 669 if (sample.has_external_callback && sample.state == EXTERNAL) { | 670 if (sample.has_external_callback && sample.state == EXTERNAL) { | 
| 670 // Don't use PC when in external callback code, as it can point | 671 // Don't use PC when in external callback code, as it can point | 
| 671 // inside callback's code, and we will erroneously report | 672 // inside callback's code, and we will erroneously report | 
| 672 // that a callback calls itself. | 673 // that a callback calls itself. | 
| 673 entries.push_back(code_map_.FindEntry( | 674 entries.push_back(FindEntry(sample.external_callback_entry)); | 
| 674 reinterpret_cast<Address>(sample.external_callback_entry))); | |
| 675 } else { | 675 } else { | 
| 676 CodeEntry* pc_entry = | 676 CodeEntry* pc_entry = FindEntry(sample.pc); | 
| 677 code_map_.FindEntry(reinterpret_cast<Address>(sample.pc)); | |
| 678 // If there is no pc_entry we're likely in native code. | 677 // If there is no pc_entry we're likely in native code. | 
| 679 // Find out, if top of stack was pointing inside a JS function | 678 // Find out, if top of stack was pointing inside a JS function | 
| 680 // meaning that we have encountered a frameless invocation. | 679 // meaning that we have encountered a frameless invocation. | 
| 681 if (!pc_entry && !sample.has_external_callback) { | 680 if (!pc_entry && !sample.has_external_callback) { | 
| 682 pc_entry = code_map_.FindEntry(reinterpret_cast<Address>(sample.tos)); | 681 pc_entry = FindEntry(sample.tos); | 
| 683 } | 682 } | 
| 684 // If pc is in the function code before it set up stack frame or after the | 683 // If pc is in the function code before it set up stack frame or after the | 
| 685 // frame was destroyed SafeStackFrameIterator incorrectly thinks that | 684 // frame was destroyed SafeStackFrameIterator incorrectly thinks that | 
| 686 // ebp contains return address of the current function and skips caller's | 685 // ebp contains return address of the current function and skips caller's | 
| 687 // frame. Check for this case and just skip such samples. | 686 // frame. Check for this case and just skip such samples. | 
| 688 if (pc_entry) { | 687 if (pc_entry) { | 
| 689 int pc_offset = static_cast<int>(reinterpret_cast<Address>(sample.pc) - | 688 int pc_offset = static_cast<int>(reinterpret_cast<Address>(sample.pc) - | 
| 690 pc_entry->instruction_start()); | 689 pc_entry->instruction_start()); | 
| 691 src_line = pc_entry->GetSourceLine(pc_offset); | 690 src_line = pc_entry->GetSourceLine(pc_offset); | 
| 692 if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) { | 691 if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) { | 
| (...skipping 12 matching lines...) Expand all Loading... | |
| 705 // 'unresolved' entry. | 704 // 'unresolved' entry. | 
| 706 if (!sample.has_external_callback) { | 705 if (!sample.has_external_callback) { | 
| 707 entries.push_back(CodeEntry::unresolved_entry()); | 706 entries.push_back(CodeEntry::unresolved_entry()); | 
| 708 } | 707 } | 
| 709 } | 708 } | 
| 710 } | 709 } | 
| 711 } | 710 } | 
| 712 | 711 | 
| 713 for (unsigned i = 0; i < sample.frames_count; ++i) { | 712 for (unsigned i = 0; i < sample.frames_count; ++i) { | 
| 714 Address stack_pos = reinterpret_cast<Address>(sample.stack[i]); | 713 Address stack_pos = reinterpret_cast<Address>(sample.stack[i]); | 
| 715 CodeEntry* entry = code_map_.FindEntry(stack_pos); | 714 CodeEntry* entry = FindEntry(stack_pos); | 
| 716 | |
| 717 if (entry) { | 715 if (entry) { | 
| 718 // Find out if the entry has an inlining stack associated. | 716 // Find out if the entry has an inlining stack associated. | 
| 719 int pc_offset = | 717 int pc_offset = | 
| 720 static_cast<int>(stack_pos - entry->instruction_start()); | 718 static_cast<int>(stack_pos - entry->instruction_start()); | 
| 721 const std::vector<CodeEntry*>* inline_stack = | 719 const std::vector<CodeEntry*>* inline_stack = | 
| 722 entry->GetInlineStack(pc_offset); | 720 entry->GetInlineStack(pc_offset); | 
| 723 if (inline_stack) { | 721 if (inline_stack) { | 
| 724 entries.insert(entries.end(), inline_stack->rbegin(), | 722 entries.insert(entries.end(), inline_stack->rbegin(), | 
| 725 inline_stack->rend()); | 723 inline_stack->rend()); | 
| 726 } | 724 } | 
| (...skipping 22 matching lines...) Expand all Loading... | |
| 749 // If no frames were symbolized, put the VM state entry in. | 747 // If no frames were symbolized, put the VM state entry in. | 
| 750 if (no_symbolized_entries) { | 748 if (no_symbolized_entries) { | 
| 751 entries.push_back(EntryForVMState(sample.state)); | 749 entries.push_back(EntryForVMState(sample.state)); | 
| 752 } | 750 } | 
| 753 } | 751 } | 
| 754 | 752 | 
| 755 profiles_->AddPathToCurrentProfiles(sample.timestamp, entries, src_line, | 753 profiles_->AddPathToCurrentProfiles(sample.timestamp, entries, src_line, | 
| 756 sample.update_stats); | 754 sample.update_stats); | 
| 757 } | 755 } | 
| 758 | 756 | 
| 757 CodeEntry* ProfileGenerator::FindEntry(void* address) { | |
| 758 CodeEntry* entry = code_map_.FindEntry(reinterpret_cast<Address>(address)); | |
| 759 if (!entry) { | |
| 760 RuntimeCallStats* rcs = isolate_->counters()->runtime_call_stats(); | |
| 761 void* start = reinterpret_cast<void*>(rcs); | |
| 762 void* end = reinterpret_cast<void*>(rcs + 1); | |
| 763 if (start <= address && address < end) { | |
| 764 RuntimeCallCounter* counter = | |
| 765 reinterpret_cast<RuntimeCallCounter*>(address); | |
| 766 entry = new CodeEntry(CodeEventListener::FUNCTION_TAG, counter->name, | |
| 
fmeawad
2016/10/28 19:51:32
I don't think they should be considered as CodeEnt
 
alph
2016/10/28 20:15:34
I don't think we need an extra class here. CodeEnt
 | |
| 767 CodeEntry::kEmptyNamePrefix, "native V8Runtime"); | |
| 768 code_map_.AddCode(reinterpret_cast<Address>(address), entry, 1); | |
| 769 } | |
| 770 } | |
| 771 return entry; | |
| 772 } | |
| 759 | 773 | 
| 760 CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { | 774 CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { | 
| 761 switch (tag) { | 775 switch (tag) { | 
| 762 case GC: | 776 case GC: | 
| 763 return CodeEntry::gc_entry(); | 777 return CodeEntry::gc_entry(); | 
| 764 case JS: | 778 case JS: | 
| 765 case COMPILER: | 779 case COMPILER: | 
| 766 // DOM events handlers are reported as OTHER / EXTERNAL entries. | 780 // DOM events handlers are reported as OTHER / EXTERNAL entries. | 
| 767 // To avoid confusing people, let's put all these entries into | 781 // To avoid confusing people, let's put all these entries into | 
| 768 // one bucket. | 782 // one bucket. | 
| 769 case OTHER: | 783 case OTHER: | 
| 770 case EXTERNAL: | 784 case EXTERNAL: | 
| 771 return CodeEntry::program_entry(); | 785 return CodeEntry::program_entry(); | 
| 772 case IDLE: | 786 case IDLE: | 
| 773 return CodeEntry::idle_entry(); | 787 return CodeEntry::idle_entry(); | 
| 774 default: return NULL; | 788 default: return NULL; | 
| 775 } | 789 } | 
| 776 } | 790 } | 
| 777 | 791 | 
| 778 } // namespace internal | 792 } // namespace internal | 
| 779 } // namespace v8 | 793 } // namespace v8 | 
| OLD | NEW |