Index: src/inspector/V8ProfilerAgentImpl.cpp |
diff --git a/src/inspector/V8ProfilerAgentImpl.cpp b/src/inspector/V8ProfilerAgentImpl.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e6cb1236d16efe33e2753127c4e57fc7edb0f1eb |
--- /dev/null |
+++ b/src/inspector/V8ProfilerAgentImpl.cpp |
@@ -0,0 +1,324 @@ |
+// Copyright 2015 the V8 project authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "src/inspector/V8ProfilerAgentImpl.h" |
+ |
+#include <v8-profiler.h> |
+#include "src/inspector/Atomics.h" |
+#include "src/inspector/StringUtil.h" |
+#include "src/inspector/V8Debugger.h" |
+#include "src/inspector/V8InspectorImpl.h" |
+#include "src/inspector/V8InspectorSessionImpl.h" |
+#include "src/inspector/V8StackTraceImpl.h" |
+#include "src/inspector/protocol/Protocol.h" |
+ |
+#include <vector> |
+ |
+#define ENSURE_V8_VERSION(major, minor) \ |
+ (V8_MAJOR_VERSION * 1000 + V8_MINOR_VERSION >= (major)*1000 + (minor)) |
+ |
+namespace v8_inspector { |
+ |
+namespace ProfilerAgentState { |
+static const char samplingInterval[] = "samplingInterval"; |
+static const char userInitiatedProfiling[] = "userInitiatedProfiling"; |
+static const char profilerEnabled[] = "profilerEnabled"; |
+} |
+ |
+namespace { |
+ |
+std::unique_ptr<protocol::Array<protocol::Profiler::PositionTickInfo>> |
+buildInspectorObjectForPositionTicks(const v8::CpuProfileNode* node) { |
+ unsigned lineCount = node->GetHitLineCount(); |
+ if (!lineCount) return nullptr; |
+ auto array = protocol::Array<protocol::Profiler::PositionTickInfo>::create(); |
+ std::vector<v8::CpuProfileNode::LineTick> entries(lineCount); |
+ if (node->GetLineTicks(&entries[0], lineCount)) { |
+ for (unsigned i = 0; i < lineCount; i++) { |
+ std::unique_ptr<protocol::Profiler::PositionTickInfo> line = |
+ protocol::Profiler::PositionTickInfo::create() |
+ .setLine(entries[i].line) |
+ .setTicks(entries[i].hit_count) |
+ .build(); |
+ array->addItem(std::move(line)); |
+ } |
+ } |
+ return array; |
+} |
+ |
+std::unique_ptr<protocol::Profiler::ProfileNode> buildInspectorObjectFor( |
+ v8::Isolate* isolate, const v8::CpuProfileNode* node) { |
+ v8::HandleScope handleScope(isolate); |
+ auto callFrame = |
+ protocol::Runtime::CallFrame::create() |
+ .setFunctionName(toProtocolString(node->GetFunctionName())) |
+ .setScriptId(String16::fromInteger(node->GetScriptId())) |
+ .setUrl(toProtocolString(node->GetScriptResourceName())) |
+ .setLineNumber(node->GetLineNumber() - 1) |
+ .setColumnNumber(node->GetColumnNumber() - 1) |
+ .build(); |
+ auto result = protocol::Profiler::ProfileNode::create() |
+ .setCallFrame(std::move(callFrame)) |
+ .setHitCount(node->GetHitCount()) |
+ .setId(node->GetNodeId()) |
+ .build(); |
+ |
+ const int childrenCount = node->GetChildrenCount(); |
+ if (childrenCount) { |
+ auto children = protocol::Array<int>::create(); |
+ for (int i = 0; i < childrenCount; i++) |
+ children->addItem(node->GetChild(i)->GetNodeId()); |
+ result->setChildren(std::move(children)); |
+ } |
+ |
+ const char* deoptReason = node->GetBailoutReason(); |
+ if (deoptReason && deoptReason[0] && strcmp(deoptReason, "no reason")) |
+ result->setDeoptReason(deoptReason); |
+ |
+ auto positionTicks = buildInspectorObjectForPositionTicks(node); |
+ if (positionTicks) result->setPositionTicks(std::move(positionTicks)); |
+ |
+ return result; |
+} |
+ |
+std::unique_ptr<protocol::Array<int>> buildInspectorObjectForSamples( |
+ v8::CpuProfile* v8profile) { |
+ auto array = protocol::Array<int>::create(); |
+ int count = v8profile->GetSamplesCount(); |
+ for (int i = 0; i < count; i++) |
+ array->addItem(v8profile->GetSample(i)->GetNodeId()); |
+ return array; |
+} |
+ |
+std::unique_ptr<protocol::Array<int>> buildInspectorObjectForTimestamps( |
+ v8::CpuProfile* v8profile) { |
+ auto array = protocol::Array<int>::create(); |
+ int count = v8profile->GetSamplesCount(); |
+ uint64_t lastTime = v8profile->GetStartTime(); |
+ for (int i = 0; i < count; i++) { |
+ uint64_t ts = v8profile->GetSampleTimestamp(i); |
+ array->addItem(static_cast<int>(ts - lastTime)); |
+ lastTime = ts; |
+ } |
+ return array; |
+} |
+ |
+void flattenNodesTree(v8::Isolate* isolate, const v8::CpuProfileNode* node, |
+ protocol::Array<protocol::Profiler::ProfileNode>* list) { |
+ list->addItem(buildInspectorObjectFor(isolate, node)); |
+ const int childrenCount = node->GetChildrenCount(); |
+ for (int i = 0; i < childrenCount; i++) |
+ flattenNodesTree(isolate, node->GetChild(i), list); |
+} |
+ |
+std::unique_ptr<protocol::Profiler::Profile> createCPUProfile( |
+ v8::Isolate* isolate, v8::CpuProfile* v8profile) { |
+ auto nodes = protocol::Array<protocol::Profiler::ProfileNode>::create(); |
+ flattenNodesTree(isolate, v8profile->GetTopDownRoot(), nodes.get()); |
+ return protocol::Profiler::Profile::create() |
+ .setNodes(std::move(nodes)) |
+ .setStartTime(static_cast<double>(v8profile->GetStartTime())) |
+ .setEndTime(static_cast<double>(v8profile->GetEndTime())) |
+ .setSamples(buildInspectorObjectForSamples(v8profile)) |
+ .setTimeDeltas(buildInspectorObjectForTimestamps(v8profile)) |
+ .build(); |
+} |
+ |
+std::unique_ptr<protocol::Debugger::Location> currentDebugLocation( |
+ V8InspectorImpl* inspector) { |
+ std::unique_ptr<V8StackTraceImpl> callStack = |
+ inspector->debugger()->captureStackTrace(false /* fullStack */); |
+ auto location = protocol::Debugger::Location::create() |
+ .setScriptId(toString16(callStack->topScriptId())) |
+ .setLineNumber(callStack->topLineNumber()) |
+ .build(); |
+ location->setColumnNumber(callStack->topColumnNumber()); |
+ return location; |
+} |
+ |
+volatile int s_lastProfileId = 0; |
+ |
+} // namespace |
+ |
+class V8ProfilerAgentImpl::ProfileDescriptor { |
+ public: |
+ ProfileDescriptor(const String16& id, const String16& title) |
+ : m_id(id), m_title(title) {} |
+ String16 m_id; |
+ String16 m_title; |
+}; |
+ |
+V8ProfilerAgentImpl::V8ProfilerAgentImpl( |
+ V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel, |
+ protocol::DictionaryValue* state) |
+ : m_session(session), |
+ m_isolate(m_session->inspector()->isolate()), |
+ m_profiler(nullptr), |
+ m_state(state), |
+ m_frontend(frontendChannel), |
+ m_enabled(false), |
+ m_recordingCPUProfile(false) {} |
+ |
+V8ProfilerAgentImpl::~V8ProfilerAgentImpl() { |
+#if ENSURE_V8_VERSION(5, 4) |
+ if (m_profiler) m_profiler->Dispose(); |
+#endif |
+} |
+ |
+void V8ProfilerAgentImpl::consoleProfile(const String16& title) { |
+ if (!m_enabled) return; |
+ String16 id = nextProfileId(); |
+ m_startedProfiles.push_back(ProfileDescriptor(id, title)); |
+ startProfiling(id); |
+ m_frontend.consoleProfileStarted( |
+ id, currentDebugLocation(m_session->inspector()), title); |
+} |
+ |
+void V8ProfilerAgentImpl::consoleProfileEnd(const String16& title) { |
+ if (!m_enabled) return; |
+ String16 id; |
+ String16 resolvedTitle; |
+ // Take last started profile if no title was passed. |
+ if (title.isEmpty()) { |
+ if (m_startedProfiles.empty()) return; |
+ id = m_startedProfiles.back().m_id; |
+ resolvedTitle = m_startedProfiles.back().m_title; |
+ m_startedProfiles.pop_back(); |
+ } else { |
+ for (size_t i = 0; i < m_startedProfiles.size(); i++) { |
+ if (m_startedProfiles[i].m_title == title) { |
+ resolvedTitle = title; |
+ id = m_startedProfiles[i].m_id; |
+ m_startedProfiles.erase(m_startedProfiles.begin() + i); |
+ break; |
+ } |
+ } |
+ if (id.isEmpty()) return; |
+ } |
+ std::unique_ptr<protocol::Profiler::Profile> profile = |
+ stopProfiling(id, true); |
+ if (!profile) return; |
+ std::unique_ptr<protocol::Debugger::Location> location = |
+ currentDebugLocation(m_session->inspector()); |
+ m_frontend.consoleProfileFinished(id, std::move(location), std::move(profile), |
+ resolvedTitle); |
+} |
+ |
+void V8ProfilerAgentImpl::enable(ErrorString*) { |
+ if (m_enabled) return; |
+ m_enabled = true; |
+#if ENSURE_V8_VERSION(5, 4) |
+ DCHECK(!m_profiler); |
+ m_profiler = v8::CpuProfiler::New(m_isolate); |
+#endif |
+ m_state->setBoolean(ProfilerAgentState::profilerEnabled, true); |
+} |
+ |
+void V8ProfilerAgentImpl::disable(ErrorString* errorString) { |
+ if (!m_enabled) return; |
+ for (size_t i = m_startedProfiles.size(); i > 0; --i) |
+ stopProfiling(m_startedProfiles[i - 1].m_id, false); |
+ m_startedProfiles.clear(); |
+ stop(nullptr, nullptr); |
+#if ENSURE_V8_VERSION(5, 4) |
+ m_profiler->Dispose(); |
+ m_profiler = nullptr; |
+#endif |
+ m_enabled = false; |
+ m_state->setBoolean(ProfilerAgentState::profilerEnabled, false); |
+} |
+ |
+void V8ProfilerAgentImpl::setSamplingInterval(ErrorString* error, |
+ int interval) { |
+ if (m_recordingCPUProfile) { |
+ *error = "Cannot change sampling interval when profiling."; |
+ return; |
+ } |
+ m_state->setInteger(ProfilerAgentState::samplingInterval, interval); |
+ profiler()->SetSamplingInterval(interval); |
+} |
+ |
+void V8ProfilerAgentImpl::restore() { |
+ DCHECK(!m_enabled); |
+ if (!m_state->booleanProperty(ProfilerAgentState::profilerEnabled, false)) |
+ return; |
+ m_enabled = true; |
+#if ENSURE_V8_VERSION(5, 4) |
+ DCHECK(!m_profiler); |
+ m_profiler = v8::CpuProfiler::New(m_isolate); |
+#endif |
+ int interval = 0; |
+ m_state->getInteger(ProfilerAgentState::samplingInterval, &interval); |
+ if (interval) profiler()->SetSamplingInterval(interval); |
+ if (m_state->booleanProperty(ProfilerAgentState::userInitiatedProfiling, |
+ false)) { |
+ ErrorString error; |
+ start(&error); |
+ } |
+} |
+ |
+void V8ProfilerAgentImpl::start(ErrorString* error) { |
+ if (m_recordingCPUProfile) return; |
+ if (!m_enabled) { |
+ *error = "Profiler is not enabled"; |
+ return; |
+ } |
+ m_recordingCPUProfile = true; |
+ m_frontendInitiatedProfileId = nextProfileId(); |
+ startProfiling(m_frontendInitiatedProfileId); |
+ m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, true); |
+} |
+ |
+void V8ProfilerAgentImpl::stop( |
+ ErrorString* errorString, |
+ std::unique_ptr<protocol::Profiler::Profile>* profile) { |
+ if (!m_recordingCPUProfile) { |
+ if (errorString) *errorString = "No recording profiles found"; |
+ return; |
+ } |
+ m_recordingCPUProfile = false; |
+ std::unique_ptr<protocol::Profiler::Profile> cpuProfile = |
+ stopProfiling(m_frontendInitiatedProfileId, !!profile); |
+ if (profile) { |
+ *profile = std::move(cpuProfile); |
+ if (!profile->get() && errorString) *errorString = "Profile is not found"; |
+ } |
+ m_frontendInitiatedProfileId = String16(); |
+ m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, false); |
+} |
+ |
+String16 V8ProfilerAgentImpl::nextProfileId() { |
+ return String16::fromInteger(atomicIncrement(&s_lastProfileId)); |
+} |
+ |
+void V8ProfilerAgentImpl::startProfiling(const String16& title) { |
+ v8::HandleScope handleScope(m_isolate); |
+ profiler()->StartProfiling(toV8String(m_isolate, title), true); |
+} |
+ |
+std::unique_ptr<protocol::Profiler::Profile> V8ProfilerAgentImpl::stopProfiling( |
+ const String16& title, bool serialize) { |
+ v8::HandleScope handleScope(m_isolate); |
+ v8::CpuProfile* profile = |
+ profiler()->StopProfiling(toV8String(m_isolate, title)); |
+ if (!profile) return nullptr; |
+ std::unique_ptr<protocol::Profiler::Profile> result; |
+ if (serialize) result = createCPUProfile(m_isolate, profile); |
+ profile->Delete(); |
+ return result; |
+} |
+ |
+bool V8ProfilerAgentImpl::isRecording() const { |
+ return m_recordingCPUProfile || !m_startedProfiles.empty(); |
+} |
+ |
+v8::CpuProfiler* V8ProfilerAgentImpl::profiler() { |
+#if ENSURE_V8_VERSION(5, 4) |
+ return m_profiler; |
+#else |
+ return m_isolate->GetCpuProfiler(); |
+#endif |
+} |
+ |
+} // namespace v8_inspector |