diff options
Diffstat (limited to 'deps/v8/src/inspector/v8-profiler-agent-impl.cc')
-rw-r--r-- | deps/v8/src/inspector/v8-profiler-agent-impl.cc | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/deps/v8/src/inspector/v8-profiler-agent-impl.cc b/deps/v8/src/inspector/v8-profiler-agent-impl.cc new file mode 100644 index 0000000000..0511ca39b5 --- /dev/null +++ b/deps/v8/src/inspector/v8-profiler-agent-impl.cc @@ -0,0 +1,321 @@ +// 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/v8-profiler-agent-impl.h" + +#include <vector> + +#include "src/base/atomicops.h" +#include "src/inspector/protocol/Protocol.h" +#include "src/inspector/string-util.h" +#include "src/inspector/v8-debugger.h" +#include "src/inspector/v8-inspector-impl.h" +#include "src/inspector/v8-inspector-session-impl.h" +#include "src/inspector/v8-stack-trace-impl.h" + +#include "include/v8-profiler.h" + +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 (m_profiler) m_profiler->Dispose(); +} + +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; + DCHECK(!m_profiler); + m_profiler = v8::CpuProfiler::New(m_isolate); + 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); + m_profiler->Dispose(); + m_profiler = nullptr; + 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); + m_profiler->SetSamplingInterval(interval); +} + +void V8ProfilerAgentImpl::restore() { + DCHECK(!m_enabled); + if (!m_state->booleanProperty(ProfilerAgentState::profilerEnabled, false)) + return; + m_enabled = true; + DCHECK(!m_profiler); + m_profiler = v8::CpuProfiler::New(m_isolate); + int interval = 0; + m_state->getInteger(ProfilerAgentState::samplingInterval, &interval); + if (interval) m_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( + v8::base::NoBarrier_AtomicIncrement(&s_lastProfileId, 1)); +} + +void V8ProfilerAgentImpl::startProfiling(const String16& title) { + v8::HandleScope handleScope(m_isolate); + m_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 = + m_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(); +} + +bool V8ProfilerAgentImpl::idleStarted() { + if (m_profiler) m_profiler->SetIdle(true); + return m_profiler; +} + +bool V8ProfilerAgentImpl::idleFinished() { + if (m_profiler) m_profiler->SetIdle(false); + return m_profiler; +} + +void V8ProfilerAgentImpl::collectSample() { + if (m_profiler) m_profiler->CollectSample(); +} + +} // namespace v8_inspector |