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/ast/scopeinfo.h" | 7 #include "src/ast/scopeinfo.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 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
43 | 43 |
44 | 44 |
45 const char* const CodeEntry::kEmptyNamePrefix = ""; | 45 const char* const CodeEntry::kEmptyNamePrefix = ""; |
46 const char* const CodeEntry::kEmptyResourceName = ""; | 46 const char* const CodeEntry::kEmptyResourceName = ""; |
47 const char* const CodeEntry::kEmptyBailoutReason = ""; | 47 const char* const CodeEntry::kEmptyBailoutReason = ""; |
48 const char* const CodeEntry::kNoDeoptReason = ""; | 48 const char* const CodeEntry::kNoDeoptReason = ""; |
49 | 49 |
50 | 50 |
51 CodeEntry::~CodeEntry() { | 51 CodeEntry::~CodeEntry() { |
52 delete line_info_; | 52 delete line_info_; |
| 53 for (auto location : inline_locations_) { |
| 54 for (auto entry : location.second) { |
| 55 delete entry; |
| 56 } |
| 57 } |
53 } | 58 } |
54 | 59 |
55 | 60 |
56 uint32_t CodeEntry::GetHash() const { | 61 uint32_t CodeEntry::GetHash() const { |
57 uint32_t hash = ComputeIntegerHash(tag(), v8::internal::kZeroHashSeed); | 62 uint32_t hash = ComputeIntegerHash(tag(), v8::internal::kZeroHashSeed); |
58 if (script_id_ != v8::UnboundScript::kNoScriptId) { | 63 if (script_id_ != v8::UnboundScript::kNoScriptId) { |
59 hash ^= ComputeIntegerHash(static_cast<uint32_t>(script_id_), | 64 hash ^= ComputeIntegerHash(static_cast<uint32_t>(script_id_), |
60 v8::internal::kZeroHashSeed); | 65 v8::internal::kZeroHashSeed); |
61 hash ^= ComputeIntegerHash(static_cast<uint32_t>(position_), | 66 hash ^= ComputeIntegerHash(static_cast<uint32_t>(position_), |
62 v8::internal::kZeroHashSeed); | 67 v8::internal::kZeroHashSeed); |
(...skipping 30 matching lines...) Expand all Loading... |
93 } | 98 } |
94 | 99 |
95 | 100 |
96 int CodeEntry::GetSourceLine(int pc_offset) const { | 101 int CodeEntry::GetSourceLine(int pc_offset) const { |
97 if (line_info_ && !line_info_->empty()) { | 102 if (line_info_ && !line_info_->empty()) { |
98 return line_info_->GetSourceLineNumber(pc_offset); | 103 return line_info_->GetSourceLineNumber(pc_offset); |
99 } | 104 } |
100 return v8::CpuProfileNode::kNoLineNumberInfo; | 105 return v8::CpuProfileNode::kNoLineNumberInfo; |
101 } | 106 } |
102 | 107 |
| 108 void CodeEntry::AddInlineStack(int pc_offset, |
| 109 std::vector<CodeEntry*>& inline_stack) { |
| 110 // It's better to use std::move to place the vector into the map, |
| 111 // but it's not supported by the current stdlibc++ on MacOS. |
| 112 inline_locations_.insert(std::make_pair(pc_offset, std::vector<CodeEntry*>())) |
| 113 .first->second.swap(inline_stack); |
| 114 } |
| 115 |
| 116 const std::vector<CodeEntry*>* CodeEntry::GetInlineStack(int pc_offset) const { |
| 117 auto it = inline_locations_.find(pc_offset); |
| 118 return it != inline_locations_.end() ? &it->second : NULL; |
| 119 } |
103 | 120 |
104 void CodeEntry::FillFunctionInfo(SharedFunctionInfo* shared) { | 121 void CodeEntry::FillFunctionInfo(SharedFunctionInfo* shared) { |
105 if (!shared->script()->IsScript()) return; | 122 if (!shared->script()->IsScript()) return; |
106 Script* script = Script::cast(shared->script()); | 123 Script* script = Script::cast(shared->script()); |
107 set_script_id(script->id()); | 124 set_script_id(script->id()); |
108 set_position(shared->start_position()); | 125 set_position(shared->start_position()); |
109 set_bailout_reason(GetBailoutReason(shared->disable_optimization_reason())); | 126 set_bailout_reason(GetBailoutReason(shared->disable_optimization_reason())); |
110 } | 127 } |
111 | 128 |
112 | |
113 CpuProfileDeoptInfo CodeEntry::GetDeoptInfo() { | 129 CpuProfileDeoptInfo CodeEntry::GetDeoptInfo() { |
114 DCHECK(has_deopt_info()); | 130 DCHECK(has_deopt_info()); |
115 | 131 |
116 CpuProfileDeoptInfo info; | 132 CpuProfileDeoptInfo info; |
117 info.deopt_reason = deopt_reason_; | 133 info.deopt_reason = deopt_reason_; |
118 if (inlined_function_infos_.empty()) { | 134 if (inlined_function_infos_.empty()) { |
119 info.stack.push_back(CpuProfileDeoptFrame( | 135 info.stack.push_back(CpuProfileDeoptFrame( |
120 {script_id_, position_ + deopt_position_.position()})); | 136 {script_id_, position_ + deopt_position_.position()})); |
121 return info; | 137 return info; |
122 } | 138 } |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
267 unsigned ProfileTree::GetFunctionId(const ProfileNode* node) { | 283 unsigned ProfileTree::GetFunctionId(const ProfileNode* node) { |
268 CodeEntry* code_entry = node->entry(); | 284 CodeEntry* code_entry = node->entry(); |
269 HashMap::Entry* entry = | 285 HashMap::Entry* entry = |
270 function_ids_.LookupOrInsert(code_entry, code_entry->GetHash()); | 286 function_ids_.LookupOrInsert(code_entry, code_entry->GetHash()); |
271 if (!entry->value) { | 287 if (!entry->value) { |
272 entry->value = reinterpret_cast<void*>(next_function_id_++); | 288 entry->value = reinterpret_cast<void*>(next_function_id_++); |
273 } | 289 } |
274 return static_cast<unsigned>(reinterpret_cast<uintptr_t>(entry->value)); | 290 return static_cast<unsigned>(reinterpret_cast<uintptr_t>(entry->value)); |
275 } | 291 } |
276 | 292 |
277 ProfileNode* ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path, | 293 ProfileNode* ProfileTree::AddPathFromEnd(const std::vector<CodeEntry*>& path, |
278 int src_line, bool update_stats) { | 294 int src_line, bool update_stats) { |
279 ProfileNode* node = root_; | 295 ProfileNode* node = root_; |
280 CodeEntry* last_entry = NULL; | 296 CodeEntry* last_entry = NULL; |
281 for (CodeEntry** entry = path.start() + path.length() - 1; | 297 for (auto it = path.rbegin(); it != path.rend(); ++it) { |
282 entry != path.start() - 1; | 298 if (*it == NULL) continue; |
283 --entry) { | 299 last_entry = *it; |
284 if (*entry != NULL) { | 300 node = node->FindOrAddChild(*it); |
285 node = node->FindOrAddChild(*entry); | |
286 last_entry = *entry; | |
287 } | |
288 } | 301 } |
289 if (last_entry && last_entry->has_deopt_info()) { | 302 if (last_entry && last_entry->has_deopt_info()) { |
290 node->CollectDeoptInfo(last_entry); | 303 node->CollectDeoptInfo(last_entry); |
291 } | 304 } |
292 if (update_stats) { | 305 if (update_stats) { |
293 node->IncrementSelfTicks(); | 306 node->IncrementSelfTicks(); |
294 if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) { | 307 if (src_line != v8::CpuProfileNode::kNoLineNumberInfo) { |
295 node->IncrementLineTicks(src_line); | 308 node->IncrementLineTicks(src_line); |
296 } | 309 } |
297 } | 310 } |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
349 } | 362 } |
350 | 363 |
351 | 364 |
352 CpuProfile::CpuProfile(Isolate* isolate, const char* title, bool record_samples) | 365 CpuProfile::CpuProfile(Isolate* isolate, const char* title, bool record_samples) |
353 : title_(title), | 366 : title_(title), |
354 record_samples_(record_samples), | 367 record_samples_(record_samples), |
355 start_time_(base::TimeTicks::HighResolutionNow()), | 368 start_time_(base::TimeTicks::HighResolutionNow()), |
356 top_down_(isolate) {} | 369 top_down_(isolate) {} |
357 | 370 |
358 void CpuProfile::AddPath(base::TimeTicks timestamp, | 371 void CpuProfile::AddPath(base::TimeTicks timestamp, |
359 const Vector<CodeEntry*>& path, int src_line, | 372 const std::vector<CodeEntry*>& path, int src_line, |
360 bool update_stats) { | 373 bool update_stats) { |
361 ProfileNode* top_frame_node = | 374 ProfileNode* top_frame_node = |
362 top_down_.AddPathFromEnd(path, src_line, update_stats); | 375 top_down_.AddPathFromEnd(path, src_line, update_stats); |
363 if (record_samples_ && !timestamp.IsNull()) { | 376 if (record_samples_ && !timestamp.IsNull()) { |
364 timestamps_.Add(timestamp); | 377 timestamps_.Add(timestamp); |
365 samples_.Add(top_frame_node); | 378 samples_.Add(top_frame_node); |
366 } | 379 } |
367 } | 380 } |
368 | 381 |
369 | 382 |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
518 for (int i = 0; i < finished_profiles_.length(); i++) { | 531 for (int i = 0; i < finished_profiles_.length(); i++) { |
519 if (profile == finished_profiles_[i]) { | 532 if (profile == finished_profiles_[i]) { |
520 finished_profiles_.Remove(i); | 533 finished_profiles_.Remove(i); |
521 return; | 534 return; |
522 } | 535 } |
523 } | 536 } |
524 UNREACHABLE(); | 537 UNREACHABLE(); |
525 } | 538 } |
526 | 539 |
527 void CpuProfilesCollection::AddPathToCurrentProfiles( | 540 void CpuProfilesCollection::AddPathToCurrentProfiles( |
528 base::TimeTicks timestamp, const Vector<CodeEntry*>& path, int src_line, | 541 base::TimeTicks timestamp, const std::vector<CodeEntry*>& path, |
529 bool update_stats) { | 542 int src_line, bool update_stats) { |
530 // As starting / stopping profiles is rare relatively to this | 543 // As starting / stopping profiles is rare relatively to this |
531 // method, we don't bother minimizing the duration of lock holding, | 544 // method, we don't bother minimizing the duration of lock holding, |
532 // e.g. copying contents of the list to a local vector. | 545 // e.g. copying contents of the list to a local vector. |
533 current_profiles_semaphore_.Wait(); | 546 current_profiles_semaphore_.Wait(); |
534 for (int i = 0; i < current_profiles_.length(); ++i) { | 547 for (int i = 0; i < current_profiles_.length(); ++i) { |
535 current_profiles_[i]->AddPath(timestamp, path, src_line, update_stats); | 548 current_profiles_[i]->AddPath(timestamp, path, src_line, update_stats); |
536 } | 549 } |
537 current_profiles_semaphore_.Signal(); | 550 current_profiles_semaphore_.Signal(); |
538 } | 551 } |
539 | 552 |
(...skipping 29 matching lines...) Expand all Loading... |
569 gc_entry_( | 582 gc_entry_( |
570 profiles->NewCodeEntry(Logger::BUILTIN_TAG, | 583 profiles->NewCodeEntry(Logger::BUILTIN_TAG, |
571 kGarbageCollectorEntryName)), | 584 kGarbageCollectorEntryName)), |
572 unresolved_entry_( | 585 unresolved_entry_( |
573 profiles->NewCodeEntry(Logger::FUNCTION_TAG, | 586 profiles->NewCodeEntry(Logger::FUNCTION_TAG, |
574 kUnresolvedFunctionName)) { | 587 kUnresolvedFunctionName)) { |
575 } | 588 } |
576 | 589 |
577 | 590 |
578 void ProfileGenerator::RecordTickSample(const TickSample& sample) { | 591 void ProfileGenerator::RecordTickSample(const TickSample& sample) { |
579 // Allocate space for stack frames + pc + function + vm-state. | 592 std::vector<CodeEntry*> entries; |
580 ScopedVector<CodeEntry*> entries(sample.frames_count + 3); | 593 // Conservatively reserve space for stack frames + pc + function + vm-state. |
581 // As actual number of decoded code entries may vary, initialize | 594 // There could in fact be more of them because of inlined entries. |
582 // entries vector with NULL values. | 595 entries.reserve(sample.frames_count + 3); |
583 CodeEntry** entry = entries.start(); | |
584 memset(entry, 0, entries.length() * sizeof(*entry)); | |
585 | 596 |
586 // The ProfileNode knows nothing about all versions of generated code for | 597 // The ProfileNode knows nothing about all versions of generated code for |
587 // the same JS function. The line number information associated with | 598 // the same JS function. The line number information associated with |
588 // the latest version of generated code is used to find a source line number | 599 // the latest version of generated code is used to find a source line number |
589 // for a JS function. Then, the detected source line is passed to | 600 // for a JS function. Then, the detected source line is passed to |
590 // ProfileNode to increase the tick count for this source line. | 601 // ProfileNode to increase the tick count for this source line. |
591 int src_line = v8::CpuProfileNode::kNoLineNumberInfo; | 602 int src_line = v8::CpuProfileNode::kNoLineNumberInfo; |
592 bool src_line_not_found = true; | 603 bool src_line_not_found = true; |
593 | 604 |
594 if (sample.pc != NULL) { | 605 if (sample.pc != NULL) { |
595 if (sample.has_external_callback && sample.state == EXTERNAL && | 606 if (sample.has_external_callback && sample.state == EXTERNAL && |
596 sample.top_frame_type == StackFrame::EXIT) { | 607 sample.top_frame_type == StackFrame::EXIT) { |
597 // Don't use PC when in external callback code, as it can point | 608 // Don't use PC when in external callback code, as it can point |
598 // inside callback's code, and we will erroneously report | 609 // inside callback's code, and we will erroneously report |
599 // that a callback calls itself. | 610 // that a callback calls itself. |
600 *entry++ = code_map_.FindEntry(sample.external_callback); | 611 entries.push_back(code_map_.FindEntry(sample.external_callback)); |
601 } else { | 612 } else { |
602 CodeEntry* pc_entry = code_map_.FindEntry(sample.pc); | 613 CodeEntry* pc_entry = code_map_.FindEntry(sample.pc); |
603 // If there is no pc_entry we're likely in native code. | 614 // If there is no pc_entry we're likely in native code. |
604 // Find out, if top of stack was pointing inside a JS function | 615 // Find out, if top of stack was pointing inside a JS function |
605 // meaning that we have encountered a frameless invocation. | 616 // meaning that we have encountered a frameless invocation. |
606 if (!pc_entry && (sample.top_frame_type == StackFrame::JAVA_SCRIPT || | 617 if (!pc_entry && (sample.top_frame_type == StackFrame::JAVA_SCRIPT || |
607 sample.top_frame_type == StackFrame::INTERPRETED || | 618 sample.top_frame_type == StackFrame::INTERPRETED || |
608 sample.top_frame_type == StackFrame::OPTIMIZED)) { | 619 sample.top_frame_type == StackFrame::OPTIMIZED)) { |
609 pc_entry = code_map_.FindEntry(sample.tos); | 620 pc_entry = code_map_.FindEntry(sample.tos); |
610 } | 621 } |
611 // If pc is in the function code before it set up stack frame or after the | 622 // If pc is in the function code before it set up stack frame or after the |
612 // frame was destroyed SafeStackFrameIterator incorrectly thinks that | 623 // frame was destroyed SafeStackFrameIterator incorrectly thinks that |
613 // ebp contains return address of the current function and skips caller's | 624 // ebp contains return address of the current function and skips caller's |
614 // frame. Check for this case and just skip such samples. | 625 // frame. Check for this case and just skip such samples. |
615 if (pc_entry) { | 626 if (pc_entry) { |
616 int pc_offset = | 627 int pc_offset = |
617 static_cast<int>(sample.pc - pc_entry->instruction_start()); | 628 static_cast<int>(sample.pc - pc_entry->instruction_start()); |
618 src_line = pc_entry->GetSourceLine(pc_offset); | 629 src_line = pc_entry->GetSourceLine(pc_offset); |
619 if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) { | 630 if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) { |
620 src_line = pc_entry->line_number(); | 631 src_line = pc_entry->line_number(); |
621 } | 632 } |
622 src_line_not_found = false; | 633 src_line_not_found = false; |
623 *entry++ = pc_entry; | 634 entries.push_back(pc_entry); |
624 | 635 |
625 if (pc_entry->builtin_id() == Builtins::kFunctionPrototypeApply || | 636 if (pc_entry->builtin_id() == Builtins::kFunctionPrototypeApply || |
626 pc_entry->builtin_id() == Builtins::kFunctionPrototypeCall) { | 637 pc_entry->builtin_id() == Builtins::kFunctionPrototypeCall) { |
627 // When current function is either the Function.prototype.apply or the | 638 // When current function is either the Function.prototype.apply or the |
628 // Function.prototype.call builtin the top frame is either frame of | 639 // Function.prototype.call builtin the top frame is either frame of |
629 // the calling JS function or internal frame. | 640 // the calling JS function or internal frame. |
630 // In the latter case we know the caller for sure but in the | 641 // In the latter case we know the caller for sure but in the |
631 // former case we don't so we simply replace the frame with | 642 // former case we don't so we simply replace the frame with |
632 // 'unresolved' entry. | 643 // 'unresolved' entry. |
633 if (sample.top_frame_type == StackFrame::JAVA_SCRIPT) { | 644 if (sample.top_frame_type == StackFrame::JAVA_SCRIPT) { |
634 *entry++ = unresolved_entry_; | 645 entries.push_back(unresolved_entry_); |
635 } | 646 } |
636 } | 647 } |
637 } | 648 } |
638 } | 649 } |
639 | 650 |
640 for (const Address *stack_pos = sample.stack, | 651 for (const Address *stack_pos = sample.stack, |
641 *stack_end = stack_pos + sample.frames_count; | 652 *stack_end = stack_pos + sample.frames_count; |
642 stack_pos != stack_end; ++stack_pos) { | 653 stack_pos != stack_end; ++stack_pos) { |
643 *entry = code_map_.FindEntry(*stack_pos); | 654 CodeEntry* entry = code_map_.FindEntry(*stack_pos); |
644 | 655 |
645 // Skip unresolved frames (e.g. internal frame) and get source line of | 656 if (entry) { |
646 // the first JS caller. | 657 // Find out if the entry has an inlining stack associated. |
647 if (src_line_not_found && *entry) { | |
648 int pc_offset = | 658 int pc_offset = |
649 static_cast<int>(*stack_pos - (*entry)->instruction_start()); | 659 static_cast<int>(*stack_pos - entry->instruction_start()); |
650 src_line = (*entry)->GetSourceLine(pc_offset); | 660 const std::vector<CodeEntry*>* inline_stack = |
651 if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) { | 661 entry->GetInlineStack(pc_offset); |
652 src_line = (*entry)->line_number(); | 662 if (inline_stack) { |
| 663 entries.insert(entries.end(), inline_stack->rbegin(), |
| 664 inline_stack->rend()); |
653 } | 665 } |
654 src_line_not_found = false; | 666 // Skip unresolved frames (e.g. internal frame) and get source line of |
| 667 // the first JS caller. |
| 668 if (src_line_not_found) { |
| 669 src_line = entry->GetSourceLine(pc_offset); |
| 670 if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) { |
| 671 src_line = entry->line_number(); |
| 672 } |
| 673 src_line_not_found = false; |
| 674 } |
655 } | 675 } |
656 | 676 entries.push_back(entry); |
657 entry++; | |
658 } | 677 } |
659 } | 678 } |
660 | 679 |
661 if (FLAG_prof_browser_mode) { | 680 if (FLAG_prof_browser_mode) { |
662 bool no_symbolized_entries = true; | 681 bool no_symbolized_entries = true; |
663 for (CodeEntry** e = entries.start(); e != entry; ++e) { | 682 for (auto e : entries) { |
664 if (*e != NULL) { | 683 if (e != NULL) { |
665 no_symbolized_entries = false; | 684 no_symbolized_entries = false; |
666 break; | 685 break; |
667 } | 686 } |
668 } | 687 } |
669 // If no frames were symbolized, put the VM state entry in. | 688 // If no frames were symbolized, put the VM state entry in. |
670 if (no_symbolized_entries) { | 689 if (no_symbolized_entries) { |
671 *entry++ = EntryForVMState(sample.state); | 690 entries.push_back(EntryForVMState(sample.state)); |
672 } | 691 } |
673 } | 692 } |
674 | 693 |
675 profiles_->AddPathToCurrentProfiles(sample.timestamp, entries, src_line, | 694 profiles_->AddPathToCurrentProfiles(sample.timestamp, entries, src_line, |
676 sample.update_stats); | 695 sample.update_stats); |
677 } | 696 } |
678 | 697 |
679 | 698 |
680 CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { | 699 CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { |
681 switch (tag) { | 700 switch (tag) { |
682 case GC: | 701 case GC: |
683 return gc_entry_; | 702 return gc_entry_; |
684 case JS: | 703 case JS: |
685 case COMPILER: | 704 case COMPILER: |
686 // DOM events handlers are reported as OTHER / EXTERNAL entries. | 705 // DOM events handlers are reported as OTHER / EXTERNAL entries. |
687 // To avoid confusing people, let's put all these entries into | 706 // To avoid confusing people, let's put all these entries into |
688 // one bucket. | 707 // one bucket. |
689 case OTHER: | 708 case OTHER: |
690 case EXTERNAL: | 709 case EXTERNAL: |
691 return program_entry_; | 710 return program_entry_; |
692 case IDLE: | 711 case IDLE: |
693 return idle_entry_; | 712 return idle_entry_; |
694 default: return NULL; | 713 default: return NULL; |
695 } | 714 } |
696 } | 715 } |
697 | 716 |
698 } // namespace internal | 717 } // namespace internal |
699 } // namespace v8 | 718 } // namespace v8 |
OLD | NEW |