| 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/base/adapters.h" | 8 #include "src/base/adapters.h" |
| 9 #include "src/debug/debug.h" | 9 #include "src/debug/debug.h" |
| 10 #include "src/deoptimizer.h" | 10 #include "src/deoptimizer.h" |
| (...skipping 30 matching lines...) Expand all Loading... |
| 41 } | 41 } |
| 42 return it->second; | 42 return it->second; |
| 43 } | 43 } |
| 44 | 44 |
| 45 | 45 |
| 46 const char* const CodeEntry::kEmptyNamePrefix = ""; | 46 const char* const CodeEntry::kEmptyNamePrefix = ""; |
| 47 const char* const CodeEntry::kEmptyResourceName = ""; | 47 const char* const CodeEntry::kEmptyResourceName = ""; |
| 48 const char* const CodeEntry::kEmptyBailoutReason = ""; | 48 const char* const CodeEntry::kEmptyBailoutReason = ""; |
| 49 const char* const CodeEntry::kNoDeoptReason = ""; | 49 const char* const CodeEntry::kNoDeoptReason = ""; |
| 50 | 50 |
| 51 const char* const CodeEntry::kProgramEntryName = "(program)"; |
| 52 const char* const CodeEntry::kIdleEntryName = "(idle)"; |
| 53 const char* const CodeEntry::kGarbageCollectorEntryName = "(garbage collector)"; |
| 54 const char* const CodeEntry::kUnresolvedFunctionName = "(unresolved function)"; |
| 55 |
| 56 base::LazyDynamicInstance<CodeEntry, CodeEntry::ProgramEntryCreateTrait>::type |
| 57 CodeEntry::kProgramEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER; |
| 58 |
| 59 base::LazyDynamicInstance<CodeEntry, CodeEntry::IdleEntryCreateTrait>::type |
| 60 CodeEntry::kIdleEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER; |
| 61 |
| 62 base::LazyDynamicInstance<CodeEntry, CodeEntry::GCEntryCreateTrait>::type |
| 63 CodeEntry::kGCEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER; |
| 64 |
| 65 base::LazyDynamicInstance<CodeEntry, |
| 66 CodeEntry::UnresolvedEntryCreateTrait>::type |
| 67 CodeEntry::kUnresolvedEntry = LAZY_DYNAMIC_INSTANCE_INITIALIZER; |
| 68 |
| 69 CodeEntry* CodeEntry::ProgramEntryCreateTrait::Create() { |
| 70 return new CodeEntry(Logger::FUNCTION_TAG, CodeEntry::kProgramEntryName); |
| 71 } |
| 72 |
| 73 CodeEntry* CodeEntry::IdleEntryCreateTrait::Create() { |
| 74 return new CodeEntry(Logger::FUNCTION_TAG, CodeEntry::kIdleEntryName); |
| 75 } |
| 76 |
| 77 CodeEntry* CodeEntry::GCEntryCreateTrait::Create() { |
| 78 return new CodeEntry(Logger::BUILTIN_TAG, |
| 79 CodeEntry::kGarbageCollectorEntryName); |
| 80 } |
| 81 |
| 82 CodeEntry* CodeEntry::UnresolvedEntryCreateTrait::Create() { |
| 83 return new CodeEntry(Logger::FUNCTION_TAG, |
| 84 CodeEntry::kUnresolvedFunctionName); |
| 85 } |
| 51 | 86 |
| 52 CodeEntry::~CodeEntry() { | 87 CodeEntry::~CodeEntry() { |
| 53 delete line_info_; | 88 delete line_info_; |
| 54 for (auto location : inline_locations_) { | 89 for (auto location : inline_locations_) { |
| 55 for (auto entry : location.second) { | 90 for (auto entry : location.second) { |
| 56 delete entry; | 91 delete entry; |
| 57 } | 92 } |
| 58 } | 93 } |
| 59 } | 94 } |
| 60 | 95 |
| (...skipping 391 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 452 base::OS::Print("%p %5d %s\n", static_cast<void*>(key), value.size, | 487 base::OS::Print("%p %5d %s\n", static_cast<void*>(key), value.size, |
| 453 value.entry->name()); | 488 value.entry->name()); |
| 454 } | 489 } |
| 455 | 490 |
| 456 | 491 |
| 457 void CodeMap::Print() { | 492 void CodeMap::Print() { |
| 458 CodeTreePrinter printer; | 493 CodeTreePrinter printer; |
| 459 tree_.ForEach(&printer); | 494 tree_.ForEach(&printer); |
| 460 } | 495 } |
| 461 | 496 |
| 462 | |
| 463 CpuProfilesCollection::CpuProfilesCollection(Heap* heap) | 497 CpuProfilesCollection::CpuProfilesCollection(Heap* heap) |
| 464 : function_and_resource_names_(heap), | 498 : resource_names_(heap), |
| 465 isolate_(heap->isolate()), | 499 isolate_(heap->isolate()), |
| 466 current_profiles_semaphore_(1) {} | 500 current_profiles_semaphore_(1) {} |
| 467 | 501 |
| 468 | |
| 469 static void DeleteCodeEntry(CodeEntry** entry_ptr) { | |
| 470 delete *entry_ptr; | |
| 471 } | |
| 472 | |
| 473 | |
| 474 static void DeleteCpuProfile(CpuProfile** profile_ptr) { | 502 static void DeleteCpuProfile(CpuProfile** profile_ptr) { |
| 475 delete *profile_ptr; | 503 delete *profile_ptr; |
| 476 } | 504 } |
| 477 | 505 |
| 478 | 506 |
| 479 CpuProfilesCollection::~CpuProfilesCollection() { | 507 CpuProfilesCollection::~CpuProfilesCollection() { |
| 480 finished_profiles_.Iterate(DeleteCpuProfile); | 508 finished_profiles_.Iterate(DeleteCpuProfile); |
| 481 current_profiles_.Iterate(DeleteCpuProfile); | 509 current_profiles_.Iterate(DeleteCpuProfile); |
| 482 code_entries_.Iterate(DeleteCodeEntry); | |
| 483 } | 510 } |
| 484 | 511 |
| 485 | 512 |
| 486 bool CpuProfilesCollection::StartProfiling(const char* title, | 513 bool CpuProfilesCollection::StartProfiling(const char* title, |
| 487 bool record_samples) { | 514 bool record_samples) { |
| 488 current_profiles_semaphore_.Wait(); | 515 current_profiles_semaphore_.Wait(); |
| 489 if (current_profiles_.length() >= kMaxSimultaneousProfiles) { | 516 if (current_profiles_.length() >= kMaxSimultaneousProfiles) { |
| 490 current_profiles_semaphore_.Signal(); | 517 current_profiles_semaphore_.Signal(); |
| 491 return false; | 518 return false; |
| 492 } | 519 } |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 549 // As starting / stopping profiles is rare relatively to this | 576 // As starting / stopping profiles is rare relatively to this |
| 550 // method, we don't bother minimizing the duration of lock holding, | 577 // method, we don't bother minimizing the duration of lock holding, |
| 551 // e.g. copying contents of the list to a local vector. | 578 // e.g. copying contents of the list to a local vector. |
| 552 current_profiles_semaphore_.Wait(); | 579 current_profiles_semaphore_.Wait(); |
| 553 for (int i = 0; i < current_profiles_.length(); ++i) { | 580 for (int i = 0; i < current_profiles_.length(); ++i) { |
| 554 current_profiles_[i]->AddPath(timestamp, path, src_line, update_stats); | 581 current_profiles_[i]->AddPath(timestamp, path, src_line, update_stats); |
| 555 } | 582 } |
| 556 current_profiles_semaphore_.Signal(); | 583 current_profiles_semaphore_.Signal(); |
| 557 } | 584 } |
| 558 | 585 |
| 559 | |
| 560 CodeEntry* CpuProfilesCollection::NewCodeEntry( | |
| 561 Logger::LogEventsAndTags tag, const char* name, const char* name_prefix, | |
| 562 const char* resource_name, int line_number, int column_number, | |
| 563 JITLineInfoTable* line_info, Address instruction_start) { | |
| 564 CodeEntry* code_entry = | |
| 565 new CodeEntry(tag, name, name_prefix, resource_name, line_number, | |
| 566 column_number, line_info, instruction_start); | |
| 567 code_entries_.Add(code_entry); | |
| 568 return code_entry; | |
| 569 } | |
| 570 | |
| 571 | |
| 572 const char* const ProfileGenerator::kProgramEntryName = | |
| 573 "(program)"; | |
| 574 const char* const ProfileGenerator::kIdleEntryName = | |
| 575 "(idle)"; | |
| 576 const char* const ProfileGenerator::kGarbageCollectorEntryName = | |
| 577 "(garbage collector)"; | |
| 578 const char* const ProfileGenerator::kUnresolvedFunctionName = | |
| 579 "(unresolved function)"; | |
| 580 | |
| 581 | |
| 582 ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles) | 586 ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles) |
| 583 : profiles_(profiles), | 587 : profiles_(profiles) {} |
| 584 program_entry_( | |
| 585 profiles->NewCodeEntry(Logger::FUNCTION_TAG, kProgramEntryName)), | |
| 586 idle_entry_( | |
| 587 profiles->NewCodeEntry(Logger::FUNCTION_TAG, kIdleEntryName)), | |
| 588 gc_entry_( | |
| 589 profiles->NewCodeEntry(Logger::BUILTIN_TAG, | |
| 590 kGarbageCollectorEntryName)), | |
| 591 unresolved_entry_( | |
| 592 profiles->NewCodeEntry(Logger::FUNCTION_TAG, | |
| 593 kUnresolvedFunctionName)) { | |
| 594 } | |
| 595 | |
| 596 | 588 |
| 597 void ProfileGenerator::RecordTickSample(const TickSample& sample) { | 589 void ProfileGenerator::RecordTickSample(const TickSample& sample) { |
| 598 std::vector<CodeEntry*> entries; | 590 std::vector<CodeEntry*> entries; |
| 599 // Conservatively reserve space for stack frames + pc + function + vm-state. | 591 // Conservatively reserve space for stack frames + pc + function + vm-state. |
| 600 // There could in fact be more of them because of inlined entries. | 592 // There could in fact be more of them because of inlined entries. |
| 601 entries.reserve(sample.frames_count + 3); | 593 entries.reserve(sample.frames_count + 3); |
| 602 | 594 |
| 603 // The ProfileNode knows nothing about all versions of generated code for | 595 // The ProfileNode knows nothing about all versions of generated code for |
| 604 // the same JS function. The line number information associated with | 596 // the same JS function. The line number information associated with |
| 605 // the latest version of generated code is used to find a source line number | 597 // the latest version of generated code is used to find a source line number |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 638 | 630 |
| 639 if (pc_entry->builtin_id() == Builtins::kFunctionPrototypeApply || | 631 if (pc_entry->builtin_id() == Builtins::kFunctionPrototypeApply || |
| 640 pc_entry->builtin_id() == Builtins::kFunctionPrototypeCall) { | 632 pc_entry->builtin_id() == Builtins::kFunctionPrototypeCall) { |
| 641 // When current function is either the Function.prototype.apply or the | 633 // When current function is either the Function.prototype.apply or the |
| 642 // Function.prototype.call builtin the top frame is either frame of | 634 // Function.prototype.call builtin the top frame is either frame of |
| 643 // the calling JS function or internal frame. | 635 // the calling JS function or internal frame. |
| 644 // In the latter case we know the caller for sure but in the | 636 // In the latter case we know the caller for sure but in the |
| 645 // former case we don't so we simply replace the frame with | 637 // former case we don't so we simply replace the frame with |
| 646 // 'unresolved' entry. | 638 // 'unresolved' entry. |
| 647 if (!sample.has_external_callback) { | 639 if (!sample.has_external_callback) { |
| 648 entries.push_back(unresolved_entry_); | 640 entries.push_back(CodeEntry::unresolved_entry()); |
| 649 } | 641 } |
| 650 } | 642 } |
| 651 } | 643 } |
| 652 } | 644 } |
| 653 | 645 |
| 654 for (const Address *stack_pos = sample.stack, | 646 for (const Address *stack_pos = sample.stack, |
| 655 *stack_end = stack_pos + sample.frames_count; | 647 *stack_end = stack_pos + sample.frames_count; |
| 656 stack_pos != stack_end; ++stack_pos) { | 648 stack_pos != stack_end; ++stack_pos) { |
| 657 CodeEntry* entry = code_map_.FindEntry(*stack_pos); | 649 CodeEntry* entry = code_map_.FindEntry(*stack_pos); |
| 658 | 650 |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 695 } | 687 } |
| 696 | 688 |
| 697 profiles_->AddPathToCurrentProfiles(sample.timestamp, entries, src_line, | 689 profiles_->AddPathToCurrentProfiles(sample.timestamp, entries, src_line, |
| 698 sample.update_stats); | 690 sample.update_stats); |
| 699 } | 691 } |
| 700 | 692 |
| 701 | 693 |
| 702 CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { | 694 CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { |
| 703 switch (tag) { | 695 switch (tag) { |
| 704 case GC: | 696 case GC: |
| 705 return gc_entry_; | 697 return CodeEntry::gc_entry(); |
| 706 case JS: | 698 case JS: |
| 707 case COMPILER: | 699 case COMPILER: |
| 708 // DOM events handlers are reported as OTHER / EXTERNAL entries. | 700 // DOM events handlers are reported as OTHER / EXTERNAL entries. |
| 709 // To avoid confusing people, let's put all these entries into | 701 // To avoid confusing people, let's put all these entries into |
| 710 // one bucket. | 702 // one bucket. |
| 711 case OTHER: | 703 case OTHER: |
| 712 case EXTERNAL: | 704 case EXTERNAL: |
| 713 return program_entry_; | 705 return CodeEntry::program_entry(); |
| 714 case IDLE: | 706 case IDLE: |
| 715 return idle_entry_; | 707 return CodeEntry::idle_entry(); |
| 716 default: return NULL; | 708 default: return NULL; |
| 717 } | 709 } |
| 718 } | 710 } |
| 719 | 711 |
| 720 } // namespace internal | 712 } // namespace internal |
| 721 } // namespace v8 | 713 } // namespace v8 |
| OLD | NEW |