| Index: src/inspector/V8HeapProfilerAgentImpl.cpp
|
| diff --git a/src/inspector/V8HeapProfilerAgentImpl.cpp b/src/inspector/V8HeapProfilerAgentImpl.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..acfa2125329512206b3ff15f4edcfce7614f433b
|
| --- /dev/null
|
| +++ b/src/inspector/V8HeapProfilerAgentImpl.cpp
|
| @@ -0,0 +1,424 @@
|
| +// 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/V8HeapProfilerAgentImpl.h"
|
| +
|
| +#include "src/inspector/InjectedScript.h"
|
| +#include "src/inspector/StringUtil.h"
|
| +#include "src/inspector/V8Debugger.h"
|
| +#include "src/inspector/V8InspectorImpl.h"
|
| +#include "src/inspector/V8InspectorSessionImpl.h"
|
| +#include "src/inspector/protocol/Protocol.h"
|
| +#include "src/inspector/public/V8InspectorClient.h"
|
| +
|
| +#include <v8-profiler.h>
|
| +#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";
|
| +#if V8_MAJOR_VERSION >= 5
|
| +static const char samplingHeapProfilerEnabled[] = "samplingHeapProfilerEnabled";
|
| +static const char samplingHeapProfilerInterval[] =
|
| + "samplingHeapProfilerInterval";
|
| +#endif
|
| +}
|
| +
|
| +class HeapSnapshotProgress final : public v8::ActivityControl {
|
| + public:
|
| + 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:
|
| + 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:
|
| + 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 V8_MAJOR_VERSION >= 5
|
| + 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));
|
| + }
|
| +#endif
|
| +}
|
| +
|
| +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 V8_MAJOR_VERSION >= 5
|
| + if (m_state->booleanProperty(
|
| + HeapProfilerAgentState::samplingHeapProfilerEnabled, false)) {
|
| + v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
|
| + if (profiler) profiler->StopSamplingHeapProfiler();
|
| + }
|
| +#endif
|
| + 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(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) {
|
| +#if V8_MAJOR_VERSION >= 5
|
| + 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);
|
| +#if V8_MAJOR_VERSION * 1000 + V8_MINOR_VERSION >= 5002
|
| + profiler->StartSamplingHeapProfiler(
|
| + static_cast<uint64_t>(samplingIntervalValue), 128,
|
| + v8::HeapProfiler::kSamplingForceGC);
|
| +#else
|
| + profiler->StartSamplingHeapProfiler(
|
| + static_cast<uint64_t>(samplingIntervalValue), 128);
|
| +#endif
|
| +#endif
|
| +}
|
| +
|
| +#if V8_MAJOR_VERSION >= 5
|
| +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
|
| +#endif
|
| +
|
| +void V8HeapProfilerAgentImpl::stopSampling(
|
| + ErrorString* errorString,
|
| + std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>* profile) {
|
| +#if V8_MAJOR_VERSION >= 5
|
| + 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();
|
| +#endif
|
| +}
|
| +
|
| +} // namespace v8_inspector
|
|
|