summaryrefslogtreecommitdiff
path: root/deps/v8/src/inspector/v8-heap-profiler-agent-impl.cc
diff options
context:
space:
mode:
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.cc407
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