diff options
Diffstat (limited to 'deps/v8/src/inspector/v8-heap-profiler-agent-impl.cc')
-rw-r--r-- | deps/v8/src/inspector/v8-heap-profiler-agent-impl.cc | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/deps/v8/src/inspector/v8-heap-profiler-agent-impl.cc b/deps/v8/src/inspector/v8-heap-profiler-agent-impl.cc new file mode 100644 index 0000000000..84c890bf3f --- /dev/null +++ b/deps/v8/src/inspector/v8-heap-profiler-agent-impl.cc @@ -0,0 +1,407 @@ +// Copyright 2016 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-heap-profiler-agent-impl.h" + +#include "src/inspector/injected-script.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 "include/v8-inspector.h" +#include "include/v8-profiler.h" +#include "include/v8-version.h" + +namespace v8_inspector { + +namespace { + +namespace HeapProfilerAgentState { +static const char heapProfilerEnabled[] = "heapProfilerEnabled"; +static const char heapObjectsTrackingEnabled[] = "heapObjectsTrackingEnabled"; +static const char allocationTrackingEnabled[] = "allocationTrackingEnabled"; +static const char samplingHeapProfilerEnabled[] = "samplingHeapProfilerEnabled"; +static const char samplingHeapProfilerInterval[] = + "samplingHeapProfilerInterval"; +} + +class HeapSnapshotProgress final : public v8::ActivityControl { + public: + explicit HeapSnapshotProgress(protocol::HeapProfiler::Frontend* frontend) + : m_frontend(frontend) {} + ControlOption ReportProgressValue(int done, int total) override { + m_frontend->reportHeapSnapshotProgress(done, total, + protocol::Maybe<bool>()); + if (done >= total) { + m_frontend->reportHeapSnapshotProgress(total, total, true); + } + m_frontend->flush(); + return kContinue; + } + + private: + protocol::HeapProfiler::Frontend* m_frontend; +}; + +class GlobalObjectNameResolver final + : public v8::HeapProfiler::ObjectNameResolver { + public: + explicit GlobalObjectNameResolver(V8InspectorSessionImpl* session) + : m_offset(0), m_strings(10000), m_session(session) {} + + const char* GetName(v8::Local<v8::Object> object) override { + InspectedContext* context = m_session->inspector()->getContext( + m_session->contextGroupId(), + V8Debugger::contextId(object->CreationContext())); + if (!context) return ""; + String16 name = context->origin(); + size_t length = name.length(); + if (m_offset + length + 1 >= m_strings.size()) return ""; + for (size_t i = 0; i < length; ++i) { + UChar ch = name[i]; + m_strings[m_offset + i] = ch > 0xff ? '?' : static_cast<char>(ch); + } + m_strings[m_offset + length] = '\0'; + char* result = &*m_strings.begin() + m_offset; + m_offset += length + 1; + return result; + } + + private: + size_t m_offset; + std::vector<char> m_strings; + V8InspectorSessionImpl* m_session; +}; + +class HeapSnapshotOutputStream final : public v8::OutputStream { + public: + explicit HeapSnapshotOutputStream(protocol::HeapProfiler::Frontend* frontend) + : m_frontend(frontend) {} + void EndOfStream() override {} + int GetChunkSize() override { return 102400; } + WriteResult WriteAsciiChunk(char* data, int size) override { + m_frontend->addHeapSnapshotChunk(String16(data, size)); + m_frontend->flush(); + return kContinue; + } + + private: + protocol::HeapProfiler::Frontend* m_frontend; +}; + +v8::Local<v8::Object> objectByHeapObjectId(v8::Isolate* isolate, int id) { + v8::HeapProfiler* profiler = isolate->GetHeapProfiler(); + v8::Local<v8::Value> value = profiler->FindObjectById(id); + if (value.IsEmpty() || !value->IsObject()) return v8::Local<v8::Object>(); + return value.As<v8::Object>(); +} + +class InspectableHeapObject final : public V8InspectorSession::Inspectable { + public: + explicit InspectableHeapObject(int heapObjectId) + : m_heapObjectId(heapObjectId) {} + v8::Local<v8::Value> get(v8::Local<v8::Context> context) override { + return objectByHeapObjectId(context->GetIsolate(), m_heapObjectId); + } + + private: + int m_heapObjectId; +}; + +class HeapStatsStream final : public v8::OutputStream { + public: + explicit HeapStatsStream(protocol::HeapProfiler::Frontend* frontend) + : m_frontend(frontend) {} + + void EndOfStream() override {} + + WriteResult WriteAsciiChunk(char* data, int size) override { + DCHECK(false); + return kAbort; + } + + WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* updateData, + int count) override { + DCHECK_GT(count, 0); + std::unique_ptr<protocol::Array<int>> statsDiff = + protocol::Array<int>::create(); + for (int i = 0; i < count; ++i) { + statsDiff->addItem(updateData[i].index); + statsDiff->addItem(updateData[i].count); + statsDiff->addItem(updateData[i].size); + } + m_frontend->heapStatsUpdate(std::move(statsDiff)); + return kContinue; + } + + private: + protocol::HeapProfiler::Frontend* m_frontend; +}; + +} // namespace + +V8HeapProfilerAgentImpl::V8HeapProfilerAgentImpl( + V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel, + protocol::DictionaryValue* state) + : m_session(session), + m_isolate(session->inspector()->isolate()), + m_frontend(frontendChannel), + m_state(state), + m_hasTimer(false) {} + +V8HeapProfilerAgentImpl::~V8HeapProfilerAgentImpl() {} + +void V8HeapProfilerAgentImpl::restore() { + if (m_state->booleanProperty(HeapProfilerAgentState::heapProfilerEnabled, + false)) + m_frontend.resetProfiles(); + if (m_state->booleanProperty( + HeapProfilerAgentState::heapObjectsTrackingEnabled, false)) + startTrackingHeapObjectsInternal(m_state->booleanProperty( + HeapProfilerAgentState::allocationTrackingEnabled, false)); + if (m_state->booleanProperty( + HeapProfilerAgentState::samplingHeapProfilerEnabled, false)) { + ErrorString error; + double samplingInterval = m_state->doubleProperty( + HeapProfilerAgentState::samplingHeapProfilerInterval, -1); + DCHECK_GE(samplingInterval, 0); + startSampling(&error, Maybe<double>(samplingInterval)); + } +} + +void V8HeapProfilerAgentImpl::collectGarbage(ErrorString*) { + m_isolate->LowMemoryNotification(); +} + +void V8HeapProfilerAgentImpl::startTrackingHeapObjects( + ErrorString*, const protocol::Maybe<bool>& trackAllocations) { + m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled, true); + bool allocationTrackingEnabled = trackAllocations.fromMaybe(false); + m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled, + allocationTrackingEnabled); + startTrackingHeapObjectsInternal(allocationTrackingEnabled); +} + +void V8HeapProfilerAgentImpl::stopTrackingHeapObjects( + ErrorString* error, const protocol::Maybe<bool>& reportProgress) { + requestHeapStatsUpdate(); + takeHeapSnapshot(error, reportProgress); + stopTrackingHeapObjectsInternal(); +} + +void V8HeapProfilerAgentImpl::enable(ErrorString*) { + m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, true); +} + +void V8HeapProfilerAgentImpl::disable(ErrorString* error) { + stopTrackingHeapObjectsInternal(); + if (m_state->booleanProperty( + HeapProfilerAgentState::samplingHeapProfilerEnabled, false)) { + v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); + if (profiler) profiler->StopSamplingHeapProfiler(); + } + m_isolate->GetHeapProfiler()->ClearObjectIds(); + m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, false); +} + +void V8HeapProfilerAgentImpl::takeHeapSnapshot( + ErrorString* errorString, const protocol::Maybe<bool>& reportProgress) { + v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); + if (!profiler) { + *errorString = "Cannot access v8 heap profiler"; + return; + } + std::unique_ptr<HeapSnapshotProgress> progress; + if (reportProgress.fromMaybe(false)) + progress = wrapUnique(new HeapSnapshotProgress(&m_frontend)); + + GlobalObjectNameResolver resolver(m_session); + const v8::HeapSnapshot* snapshot = + profiler->TakeHeapSnapshot(progress.get(), &resolver); + if (!snapshot) { + *errorString = "Failed to take heap snapshot"; + return; + } + HeapSnapshotOutputStream stream(&m_frontend); + snapshot->Serialize(&stream); + const_cast<v8::HeapSnapshot*>(snapshot)->Delete(); +} + +void V8HeapProfilerAgentImpl::getObjectByHeapObjectId( + ErrorString* error, const String16& heapSnapshotObjectId, + const protocol::Maybe<String16>& objectGroup, + std::unique_ptr<protocol::Runtime::RemoteObject>* result) { + bool ok; + int id = heapSnapshotObjectId.toInteger(&ok); + if (!ok) { + *error = "Invalid heap snapshot object id"; + return; + } + + v8::HandleScope handles(m_isolate); + v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id); + if (heapObject.IsEmpty()) { + *error = "Object is not available"; + return; + } + + if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject)) { + *error = "Object is not available"; + return; + } + + *result = m_session->wrapObject(heapObject->CreationContext(), heapObject, + objectGroup.fromMaybe(""), false); + if (!result) *error = "Object is not available"; +} + +void V8HeapProfilerAgentImpl::addInspectedHeapObject( + ErrorString* errorString, const String16& inspectedHeapObjectId) { + bool ok; + int id = inspectedHeapObjectId.toInteger(&ok); + if (!ok) { + *errorString = "Invalid heap snapshot object id"; + return; + } + + v8::HandleScope handles(m_isolate); + v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id); + if (heapObject.IsEmpty()) { + *errorString = "Object is not available"; + return; + } + + if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject)) { + *errorString = "Object is not available"; + return; + } + + m_session->addInspectedObject(wrapUnique(new InspectableHeapObject(id))); +} + +void V8HeapProfilerAgentImpl::getHeapObjectId(ErrorString* errorString, + const String16& objectId, + String16* heapSnapshotObjectId) { + v8::HandleScope handles(m_isolate); + v8::Local<v8::Value> value; + v8::Local<v8::Context> context; + if (!m_session->unwrapObject(errorString, objectId, &value, &context, + nullptr) || + value->IsUndefined()) + return; + + v8::SnapshotObjectId id = m_isolate->GetHeapProfiler()->GetObjectId(value); + *heapSnapshotObjectId = String16::fromInteger(static_cast<size_t>(id)); +} + +void V8HeapProfilerAgentImpl::requestHeapStatsUpdate() { + HeapStatsStream stream(&m_frontend); + v8::SnapshotObjectId lastSeenObjectId = + m_isolate->GetHeapProfiler()->GetHeapStats(&stream); + m_frontend.lastSeenObjectId( + lastSeenObjectId, m_session->inspector()->client()->currentTimeMS()); +} + +// static +void V8HeapProfilerAgentImpl::onTimer(void* data) { + reinterpret_cast<V8HeapProfilerAgentImpl*>(data)->requestHeapStatsUpdate(); +} + +void V8HeapProfilerAgentImpl::startTrackingHeapObjectsInternal( + bool trackAllocations) { + m_isolate->GetHeapProfiler()->StartTrackingHeapObjects(trackAllocations); + if (!m_hasTimer) { + m_hasTimer = true; + m_session->inspector()->client()->startRepeatingTimer( + 0.05, &V8HeapProfilerAgentImpl::onTimer, reinterpret_cast<void*>(this)); + } +} + +void V8HeapProfilerAgentImpl::stopTrackingHeapObjectsInternal() { + if (m_hasTimer) { + m_session->inspector()->client()->cancelTimer( + reinterpret_cast<void*>(this)); + m_hasTimer = false; + } + m_isolate->GetHeapProfiler()->StopTrackingHeapObjects(); + m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled, + false); + m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled, false); +} + +void V8HeapProfilerAgentImpl::startSampling( + ErrorString* errorString, const Maybe<double>& samplingInterval) { + v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); + if (!profiler) { + *errorString = "Cannot access v8 heap profiler"; + return; + } + const unsigned defaultSamplingInterval = 1 << 15; + double samplingIntervalValue = + samplingInterval.fromMaybe(defaultSamplingInterval); + m_state->setDouble(HeapProfilerAgentState::samplingHeapProfilerInterval, + samplingIntervalValue); + m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled, + true); + profiler->StartSamplingHeapProfiler( + static_cast<uint64_t>(samplingIntervalValue), 128, + v8::HeapProfiler::kSamplingForceGC); +} + +namespace { +std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode> +buildSampingHeapProfileNode(const v8::AllocationProfile::Node* node) { + auto children = protocol::Array< + protocol::HeapProfiler::SamplingHeapProfileNode>::create(); + for (const auto* child : node->children) + children->addItem(buildSampingHeapProfileNode(child)); + size_t selfSize = 0; + for (const auto& allocation : node->allocations) + selfSize += allocation.size * allocation.count; + std::unique_ptr<protocol::Runtime::CallFrame> callFrame = + protocol::Runtime::CallFrame::create() + .setFunctionName(toProtocolString(node->name)) + .setScriptId(String16::fromInteger(node->script_id)) + .setUrl(toProtocolString(node->script_name)) + .setLineNumber(node->line_number - 1) + .setColumnNumber(node->column_number - 1) + .build(); + std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode> result = + protocol::HeapProfiler::SamplingHeapProfileNode::create() + .setCallFrame(std::move(callFrame)) + .setSelfSize(selfSize) + .setChildren(std::move(children)) + .build(); + return result; +} +} // namespace + +void V8HeapProfilerAgentImpl::stopSampling( + ErrorString* errorString, + std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>* profile) { + v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); + if (!profiler) { + *errorString = "Cannot access v8 heap profiler"; + return; + } + v8::HandleScope scope( + m_isolate); // Allocation profile contains Local handles. + std::unique_ptr<v8::AllocationProfile> v8Profile( + profiler->GetAllocationProfile()); + profiler->StopSamplingHeapProfiler(); + m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled, + false); + if (!v8Profile) { + *errorString = "Cannot access v8 sampled heap profile."; + return; + } + v8::AllocationProfile::Node* root = v8Profile->GetRootNode(); + *profile = protocol::HeapProfiler::SamplingHeapProfile::create() + .setHead(buildSampingHeapProfileNode(root)) + .build(); +} + +} // namespace v8_inspector |