Chromium Code Reviews| 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 |