| 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" |
| 11 #include "src/profiler/cpu-profiler.h" | 11 #include "src/profiler/cpu-profiler.h" |
| 12 #include "src/profiler/profile-generator-inl.h" | 12 #include "src/profiler/profile-generator-inl.h" |
| 13 #include "src/tracing/trace-event.h" |
| 14 #include "src/tracing/traced-value.h" |
| 13 #include "src/unicode.h" | 15 #include "src/unicode.h" |
| 14 | 16 |
| 15 namespace v8 { | 17 namespace v8 { |
| 16 namespace internal { | 18 namespace internal { |
| 17 | 19 |
| 18 | 20 |
| 19 JITLineInfoTable::JITLineInfoTable() {} | 21 JITLineInfoTable::JITLineInfoTable() {} |
| 20 | 22 |
| 21 | 23 |
| 22 JITLineInfoTable::~JITLineInfoTable() {} | 24 JITLineInfoTable::~JITLineInfoTable() {} |
| (...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 207 children_.Lookup(entry, CodeEntryHash(entry)); | 209 children_.Lookup(entry, CodeEntryHash(entry)); |
| 208 return map_entry != NULL ? | 210 return map_entry != NULL ? |
| 209 reinterpret_cast<ProfileNode*>(map_entry->value) : NULL; | 211 reinterpret_cast<ProfileNode*>(map_entry->value) : NULL; |
| 210 } | 212 } |
| 211 | 213 |
| 212 | 214 |
| 213 ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) { | 215 ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) { |
| 214 base::HashMap::Entry* map_entry = | 216 base::HashMap::Entry* map_entry = |
| 215 children_.LookupOrInsert(entry, CodeEntryHash(entry)); | 217 children_.LookupOrInsert(entry, CodeEntryHash(entry)); |
| 216 ProfileNode* node = reinterpret_cast<ProfileNode*>(map_entry->value); | 218 ProfileNode* node = reinterpret_cast<ProfileNode*>(map_entry->value); |
| 217 if (node == NULL) { | 219 if (!node) { |
| 218 // New node added. | 220 node = new ProfileNode(tree_, entry, this); |
| 219 node = new ProfileNode(tree_, entry); | |
| 220 map_entry->value = node; | 221 map_entry->value = node; |
| 221 children_list_.Add(node); | 222 children_list_.Add(node); |
| 222 } | 223 } |
| 223 return node; | 224 return node; |
| 224 } | 225 } |
| 225 | 226 |
| 226 | 227 |
| 227 void ProfileNode::IncrementLineTicks(int src_line) { | 228 void ProfileNode::IncrementLineTicks(int src_line) { |
| 228 if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) return; | 229 if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) return; |
| 229 // Increment a hit counter of a certain source line. | 230 // Increment a hit counter of a certain source line. |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 298 void AfterAllChildrenTraversed(ProfileNode* node) { | 299 void AfterAllChildrenTraversed(ProfileNode* node) { |
| 299 delete node; | 300 delete node; |
| 300 } | 301 } |
| 301 | 302 |
| 302 void AfterChildTraversed(ProfileNode*, ProfileNode*) { } | 303 void AfterChildTraversed(ProfileNode*, ProfileNode*) { } |
| 303 }; | 304 }; |
| 304 | 305 |
| 305 ProfileTree::ProfileTree(Isolate* isolate) | 306 ProfileTree::ProfileTree(Isolate* isolate) |
| 306 : root_entry_(CodeEventListener::FUNCTION_TAG, "(root)"), | 307 : root_entry_(CodeEventListener::FUNCTION_TAG, "(root)"), |
| 307 next_node_id_(1), | 308 next_node_id_(1), |
| 308 root_(new ProfileNode(this, &root_entry_)), | 309 root_(new ProfileNode(this, &root_entry_, nullptr)), |
| 309 isolate_(isolate), | 310 isolate_(isolate), |
| 310 next_function_id_(1), | 311 next_function_id_(1), |
| 311 function_ids_(ProfileNode::CodeEntriesMatch) {} | 312 function_ids_(ProfileNode::CodeEntriesMatch) {} |
| 312 | 313 |
| 313 ProfileTree::~ProfileTree() { | 314 ProfileTree::~ProfileTree() { |
| 314 DeleteNodesCallback cb; | 315 DeleteNodesCallback cb; |
| 315 TraverseDepthFirst(&cb); | 316 TraverseDepthFirst(&cb); |
| 316 } | 317 } |
| 317 | 318 |
| 318 | 319 |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 390 Position& parent = stack[stack.length() - 2]; | 391 Position& parent = stack[stack.length() - 2]; |
| 391 callback->AfterChildTraversed(parent.node, current.node); | 392 callback->AfterChildTraversed(parent.node, current.node); |
| 392 parent.next_child(); | 393 parent.next_child(); |
| 393 } | 394 } |
| 394 // Remove child from the stack. | 395 // Remove child from the stack. |
| 395 stack.RemoveLast(); | 396 stack.RemoveLast(); |
| 396 } | 397 } |
| 397 } | 398 } |
| 398 } | 399 } |
| 399 | 400 |
| 401 using v8::tracing::TracedValue; |
| 402 |
| 400 CpuProfile::CpuProfile(CpuProfiler* profiler, const char* title, | 403 CpuProfile::CpuProfile(CpuProfiler* profiler, const char* title, |
| 401 bool record_samples) | 404 bool record_samples) |
| 402 : title_(title), | 405 : title_(title), |
| 403 record_samples_(record_samples), | 406 record_samples_(record_samples), |
| 404 start_time_(base::TimeTicks::HighResolutionNow()), | 407 start_time_(base::TimeTicks::HighResolutionNow()), |
| 405 top_down_(profiler->isolate()), | 408 top_down_(profiler->isolate()), |
| 406 profiler_(profiler) {} | 409 profiler_(profiler), |
| 410 streaming_next_sample_(0) { |
| 411 auto value = TracedValue::Create(); |
| 412 value->SetDouble("startTime", |
| 413 (start_time_ - base::TimeTicks()).InMicroseconds()); |
| 414 TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"), |
| 415 "CpuProfile", this, "data", std::move(value)); |
| 416 } |
| 407 | 417 |
| 408 void CpuProfile::AddPath(base::TimeTicks timestamp, | 418 void CpuProfile::AddPath(base::TimeTicks timestamp, |
| 409 const std::vector<CodeEntry*>& path, int src_line, | 419 const std::vector<CodeEntry*>& path, int src_line, |
| 410 bool update_stats) { | 420 bool update_stats) { |
| 411 ProfileNode* top_frame_node = | 421 ProfileNode* top_frame_node = |
| 412 top_down_.AddPathFromEnd(path, src_line, update_stats); | 422 top_down_.AddPathFromEnd(path, src_line, update_stats); |
| 413 if (record_samples_ && !timestamp.IsNull()) { | 423 if (record_samples_ && !timestamp.IsNull()) { |
| 414 timestamps_.Add(timestamp); | 424 timestamps_.Add(timestamp); |
| 415 samples_.Add(top_frame_node); | 425 samples_.Add(top_frame_node); |
| 416 } | 426 } |
| 427 const int kSamplesFlushCount = 100; |
| 428 const int kNodesFlushCount = 10; |
| 429 if (samples_.length() - streaming_next_sample_ >= kSamplesFlushCount || |
| 430 top_down_.pending_nodes_count() >= kNodesFlushCount) { |
| 431 StreamPendingTraceEvents(); |
| 432 } |
| 417 } | 433 } |
| 418 | 434 |
| 419 void CpuProfile::CalculateTotalTicksAndSamplingRate() { | 435 namespace { |
| 436 |
| 437 void BuildNodeValue(const ProfileNode* node, TracedValue* value) { |
| 438 const CodeEntry* entry = node->entry(); |
| 439 value->BeginDictionary("callFrame"); |
| 440 value->SetString("functionName", entry->name()); |
| 441 if (*entry->resource_name()) { |
| 442 value->SetString("url", entry->resource_name()); |
| 443 } |
| 444 value->SetInteger("scriptId", entry->script_id()); |
| 445 if (entry->line_number()) { |
| 446 value->SetInteger("lineNumber", entry->line_number() - 1); |
| 447 } |
| 448 if (entry->column_number()) { |
| 449 value->SetInteger("columnNumber", entry->column_number() - 1); |
| 450 } |
| 451 value->EndDictionary(); |
| 452 value->SetInteger("id", node->id()); |
| 453 if (node->parent()) { |
| 454 value->SetInteger("parent", node->parent()->id()); |
| 455 } |
| 456 const char* deopt_reason = entry->bailout_reason(); |
| 457 if (deopt_reason && deopt_reason[0] && strcmp(deopt_reason, "no reason")) { |
| 458 value->SetString("deoptReason", deopt_reason); |
| 459 } |
| 460 } |
| 461 |
| 462 } // namespace |
| 463 |
| 464 void CpuProfile::StreamPendingTraceEvents() { |
| 465 std::vector<const ProfileNode*> pending_nodes = top_down_.TakePendingNodes(); |
| 466 if (pending_nodes.empty() && !samples_.length()) return; |
| 467 auto value = TracedValue::Create(); |
| 468 |
| 469 if (!pending_nodes.empty()) { |
| 470 value->BeginArray("nodes"); |
| 471 for (auto node : pending_nodes) { |
| 472 value->BeginDictionary(); |
| 473 BuildNodeValue(node, value.get()); |
| 474 value->EndDictionary(); |
| 475 } |
| 476 value->EndArray(); |
| 477 } |
| 478 |
| 479 if (streaming_next_sample_ != samples_.length()) { |
| 480 value->BeginArray("samples"); |
| 481 for (int i = streaming_next_sample_; i < samples_.length(); ++i) { |
| 482 value->AppendInteger(samples_[i]->id()); |
| 483 } |
| 484 value->EndArray(); |
| 485 value->BeginArray("timeDeltas"); |
| 486 base::TimeTicks lastTimestamp = |
| 487 streaming_next_sample_ ? timestamps_[streaming_next_sample_ - 1] |
| 488 : start_time(); |
| 489 for (int i = streaming_next_sample_; i < timestamps_.length(); ++i) { |
| 490 value->AppendInteger( |
| 491 static_cast<int>((timestamps_[i] - lastTimestamp).InMicroseconds())); |
| 492 lastTimestamp = timestamps_[i]; |
| 493 } |
| 494 value->EndArray(); |
| 495 DCHECK(samples_.length() == timestamps_.length()); |
| 496 streaming_next_sample_ = samples_.length(); |
| 497 } |
| 498 |
| 499 TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"), |
| 500 "CpuProfileChunk", this, "data", |
| 501 std::move(value)); |
| 502 } |
| 503 |
| 504 void CpuProfile::FinishProfile() { |
| 420 end_time_ = base::TimeTicks::HighResolutionNow(); | 505 end_time_ = base::TimeTicks::HighResolutionNow(); |
| 506 StreamPendingTraceEvents(); |
| 507 auto value = TracedValue::Create(); |
| 508 value->SetDouble("endTime", (end_time_ - base::TimeTicks()).InMicroseconds()); |
| 509 TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"), |
| 510 "CpuProfileChunk", this, "data", |
| 511 std::move(value)); |
| 421 } | 512 } |
| 422 | 513 |
| 423 void CpuProfile::Print() { | 514 void CpuProfile::Print() { |
| 424 base::OS::Print("[Top down]:\n"); | 515 base::OS::Print("[Top down]:\n"); |
| 425 top_down_.Print(); | 516 top_down_.Print(); |
| 426 } | 517 } |
| 427 | 518 |
| 428 void CodeMap::AddCode(Address addr, CodeEntry* entry, unsigned size) { | 519 void CodeMap::AddCode(Address addr, CodeEntry* entry, unsigned size) { |
| 429 DeleteAllCoveredCode(addr, addr + size); | 520 DeleteAllCoveredCode(addr, addr + size); |
| 430 code_map_.insert({addr, CodeEntryInfo(entry, size)}); | 521 code_map_.insert({addr, CodeEntryInfo(entry, size)}); |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 497 } | 588 } |
| 498 } | 589 } |
| 499 current_profiles_.Add(new CpuProfile(profiler_, title, record_samples)); | 590 current_profiles_.Add(new CpuProfile(profiler_, title, record_samples)); |
| 500 current_profiles_semaphore_.Signal(); | 591 current_profiles_semaphore_.Signal(); |
| 501 return true; | 592 return true; |
| 502 } | 593 } |
| 503 | 594 |
| 504 | 595 |
| 505 CpuProfile* CpuProfilesCollection::StopProfiling(const char* title) { | 596 CpuProfile* CpuProfilesCollection::StopProfiling(const char* title) { |
| 506 const int title_len = StrLength(title); | 597 const int title_len = StrLength(title); |
| 507 CpuProfile* profile = NULL; | 598 CpuProfile* profile = nullptr; |
| 508 current_profiles_semaphore_.Wait(); | 599 current_profiles_semaphore_.Wait(); |
| 509 for (int i = current_profiles_.length() - 1; i >= 0; --i) { | 600 for (int i = current_profiles_.length() - 1; i >= 0; --i) { |
| 510 if (title_len == 0 || strcmp(current_profiles_[i]->title(), title) == 0) { | 601 if (title_len == 0 || strcmp(current_profiles_[i]->title(), title) == 0) { |
| 511 profile = current_profiles_.Remove(i); | 602 profile = current_profiles_.Remove(i); |
| 512 break; | 603 break; |
| 513 } | 604 } |
| 514 } | 605 } |
| 515 current_profiles_semaphore_.Signal(); | 606 current_profiles_semaphore_.Signal(); |
| 516 | 607 |
| 517 if (profile == NULL) return NULL; | 608 if (!profile) return nullptr; |
| 518 profile->CalculateTotalTicksAndSamplingRate(); | 609 profile->FinishProfile(); |
| 519 finished_profiles_.Add(profile); | 610 finished_profiles_.Add(profile); |
| 520 return profile; | 611 return profile; |
| 521 } | 612 } |
| 522 | 613 |
| 523 | 614 |
| 524 bool CpuProfilesCollection::IsLastProfile(const char* title) { | 615 bool CpuProfilesCollection::IsLastProfile(const char* title) { |
| 525 // Called from VM thread, and only it can mutate the list, | 616 // Called from VM thread, and only it can mutate the list, |
| 526 // so no locking is needed here. | 617 // so no locking is needed here. |
| 527 if (current_profiles_.length() != 1) return false; | 618 if (current_profiles_.length() != 1) return false; |
| 528 return StrLength(title) == 0 | 619 return StrLength(title) == 0 |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 676 case EXTERNAL: | 767 case EXTERNAL: |
| 677 return CodeEntry::program_entry(); | 768 return CodeEntry::program_entry(); |
| 678 case IDLE: | 769 case IDLE: |
| 679 return CodeEntry::idle_entry(); | 770 return CodeEntry::idle_entry(); |
| 680 default: return NULL; | 771 default: return NULL; |
| 681 } | 772 } |
| 682 } | 773 } |
| 683 | 774 |
| 684 } // namespace internal | 775 } // namespace internal |
| 685 } // namespace v8 | 776 } // namespace v8 |
| OLD | NEW |