Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(20)

Side by Side Diff: src/profiler/profile-generator.cc

Issue 1740073002: Make CPU profiler unwind the inlined functions stack. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: rebaseline Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/profiler/profile-generator.h ('k') | test/cctest/test-cpu-profiler.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « src/profiler/profile-generator.h ('k') | test/cctest/test-cpu-profiler.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698