Index: src/profiler/profile-generator.cc |
diff --git a/src/profiler/profile-generator.cc b/src/profiler/profile-generator.cc |
index 583ef0f4e381e17f4c06452e4401ba6be0f8f82c..a670729b794961da40ef3668a326cb11cf851f90 100644 |
--- a/src/profiler/profile-generator.cc |
+++ b/src/profiler/profile-generator.cc |
@@ -10,6 +10,8 @@ |
#include "src/global-handles.h" |
#include "src/profiler/cpu-profiler.h" |
#include "src/profiler/profile-generator-inl.h" |
+#include "src/tracing/trace-event.h" |
+#include "src/tracing/traced-value.h" |
#include "src/unicode.h" |
namespace v8 { |
@@ -214,9 +216,8 @@ ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) { |
base::HashMap::Entry* map_entry = |
children_.LookupOrInsert(entry, CodeEntryHash(entry)); |
ProfileNode* node = reinterpret_cast<ProfileNode*>(map_entry->value); |
- if (node == NULL) { |
- // New node added. |
- node = new ProfileNode(tree_, entry); |
+ if (!node) { |
+ node = new ProfileNode(tree_, entry, this); |
map_entry->value = node; |
children_list_.Add(node); |
} |
@@ -305,7 +306,7 @@ class DeleteNodesCallback { |
ProfileTree::ProfileTree(Isolate* isolate) |
: root_entry_(CodeEventListener::FUNCTION_TAG, "(root)"), |
next_node_id_(1), |
- root_(new ProfileNode(this, &root_entry_)), |
+ root_(new ProfileNode(this, &root_entry_, nullptr)), |
isolate_(isolate), |
next_function_id_(1), |
function_ids_(ProfileNode::CodeEntriesMatch) {} |
@@ -397,13 +398,22 @@ void ProfileTree::TraverseDepthFirst(Callback* callback) { |
} |
} |
+using v8::tracing::TracedValue; |
+ |
CpuProfile::CpuProfile(CpuProfiler* profiler, const char* title, |
bool record_samples) |
: title_(title), |
record_samples_(record_samples), |
start_time_(base::TimeTicks::HighResolutionNow()), |
top_down_(profiler->isolate()), |
- profiler_(profiler) {} |
+ profiler_(profiler), |
+ streaming_next_sample_(0) { |
+ auto value = TracedValue::Create(); |
+ value->SetDouble("startTime", |
+ (start_time_ - base::TimeTicks()).InMicroseconds()); |
+ TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"), |
+ "CpuProfile", this, "data", std::move(value)); |
+} |
void CpuProfile::AddPath(base::TimeTicks timestamp, |
const std::vector<CodeEntry*>& path, int src_line, |
@@ -414,10 +424,91 @@ void CpuProfile::AddPath(base::TimeTicks timestamp, |
timestamps_.Add(timestamp); |
samples_.Add(top_frame_node); |
} |
+ const int kSamplesFlushCount = 100; |
+ const int kNodesFlushCount = 10; |
+ if (samples_.length() - streaming_next_sample_ >= kSamplesFlushCount || |
+ top_down_.pending_nodes_count() >= kNodesFlushCount) { |
+ StreamPendingTraceEvents(); |
+ } |
+} |
+ |
+namespace { |
+ |
+void BuildNodeValue(const ProfileNode* node, TracedValue* value) { |
+ const CodeEntry* entry = node->entry(); |
+ value->BeginDictionary("callFrame"); |
+ value->SetString("functionName", entry->name()); |
+ if (*entry->resource_name()) { |
+ value->SetString("url", entry->resource_name()); |
+ } |
+ value->SetInteger("scriptId", entry->script_id()); |
+ if (entry->line_number()) { |
+ value->SetInteger("lineNumber", entry->line_number() - 1); |
+ } |
+ if (entry->column_number()) { |
+ value->SetInteger("columnNumber", entry->column_number() - 1); |
+ } |
+ value->EndDictionary(); |
+ value->SetInteger("id", node->id()); |
+ if (node->parent()) { |
+ value->SetInteger("parent", node->parent()->id()); |
+ } |
+ const char* deopt_reason = entry->bailout_reason(); |
+ if (deopt_reason && deopt_reason[0] && strcmp(deopt_reason, "no reason")) { |
+ value->SetString("deoptReason", deopt_reason); |
+ } |
+} |
+ |
+} // namespace |
+ |
+void CpuProfile::StreamPendingTraceEvents() { |
+ std::vector<const ProfileNode*> pending_nodes = top_down_.TakePendingNodes(); |
+ if (pending_nodes.empty() && !samples_.length()) return; |
+ auto value = TracedValue::Create(); |
+ |
+ if (!pending_nodes.empty()) { |
+ value->BeginArray("nodes"); |
+ for (auto node : pending_nodes) { |
+ value->BeginDictionary(); |
+ BuildNodeValue(node, value.get()); |
+ value->EndDictionary(); |
+ } |
+ value->EndArray(); |
+ } |
+ |
+ if (streaming_next_sample_ != samples_.length()) { |
+ value->BeginArray("samples"); |
+ for (int i = streaming_next_sample_; i < samples_.length(); ++i) { |
+ value->AppendInteger(samples_[i]->id()); |
+ } |
+ value->EndArray(); |
+ value->BeginArray("timeDeltas"); |
+ base::TimeTicks lastTimestamp = |
+ streaming_next_sample_ ? timestamps_[streaming_next_sample_ - 1] |
+ : start_time(); |
+ for (int i = streaming_next_sample_; i < timestamps_.length(); ++i) { |
+ value->AppendInteger( |
+ static_cast<int>((timestamps_[i] - lastTimestamp).InMicroseconds())); |
+ lastTimestamp = timestamps_[i]; |
+ } |
+ value->EndArray(); |
+ DCHECK(samples_.length() == timestamps_.length()); |
+ streaming_next_sample_ = samples_.length(); |
+ } |
+ |
+ TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"), |
+ "CpuProfileChunk", this, "data", |
+ std::move(value)); |
} |
-void CpuProfile::CalculateTotalTicksAndSamplingRate() { |
+void CpuProfile::FinishProfile() { |
end_time_ = base::TimeTicks::HighResolutionNow(); |
+ StreamPendingTraceEvents(); |
+ auto value = TracedValue::Create(); |
+ value->SetDouble("endTime", (end_time_ - base::TimeTicks()).InMicroseconds()); |
+ TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"), |
+ "CpuProfileChunk", this, "data", |
+ std::move(value)); |
} |
void CpuProfile::Print() { |
@@ -504,7 +595,7 @@ bool CpuProfilesCollection::StartProfiling(const char* title, |
CpuProfile* CpuProfilesCollection::StopProfiling(const char* title) { |
const int title_len = StrLength(title); |
- CpuProfile* profile = NULL; |
+ CpuProfile* profile = nullptr; |
current_profiles_semaphore_.Wait(); |
for (int i = current_profiles_.length() - 1; i >= 0; --i) { |
if (title_len == 0 || strcmp(current_profiles_[i]->title(), title) == 0) { |
@@ -514,8 +605,8 @@ CpuProfile* CpuProfilesCollection::StopProfiling(const char* title) { |
} |
current_profiles_semaphore_.Signal(); |
- if (profile == NULL) return NULL; |
- profile->CalculateTotalTicksAndSamplingRate(); |
+ if (!profile) return nullptr; |
+ profile->FinishProfile(); |
finished_profiles_.Add(profile); |
return profile; |
} |