Heap profiler: publish API and add test.

Review URL: http://codereview.chromium.org/2822009

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4864 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 31cb9f70
......@@ -184,6 +184,145 @@ class V8EXPORT CpuProfiler {
};
class HeapGraphNode;
/**
* HeapSnapshotEdge represents a directed connection between heap
* graph nodes: from retaners to retained nodes.
*/
class V8EXPORT HeapGraphEdge {
public:
enum Type {
CONTEXT_VARIABLE = 0, // A variable from a function context.
ELEMENT = 1, // An element of an array.
PROPERTY = 2 // A named object property.
};
/** Returns edge type (see HeapGraphEdge::Type). */
Type GetType() const;
/**
* Returns edge name. This can be a variable name, an element index, or
* a property name.
*/
Handle<Value> GetName() const;
/** Returns origin node. */
const HeapGraphNode* GetFromNode() const;
/** Returns destination node. */
const HeapGraphNode* GetToNode() const;
};
class V8EXPORT HeapGraphPath {
public:
/** Returns the number of edges in the path. */
int GetEdgesCount() const;
/** Returns an edge from the path. */
const HeapGraphEdge* GetEdge(int index) const;
/** Returns origin node. */
const HeapGraphNode* GetFromNode() const;
/** Returns destination node. */
const HeapGraphNode* GetToNode() const;
};
/**
* HeapGraphNode represents a node in a heap graph.
*/
class V8EXPORT HeapGraphNode {
public:
enum Type {
INTERNAL = 0, // Internal node, a virtual one, for housekeeping.
ARRAY = 1, // An array of elements.
STRING = 2, // A string.
OBJECT = 3, // A JS object (except for arrays and strings).
CODE = 4, // Compiled code.
CLOSURE = 5 // Function closure.
};
/** Returns node type (see HeapGraphNode::Type). */
Type GetType() const;
/**
* Returns node name. Depending on node's type this can be the name
* of the constructor (for objects), the name of the function (for
* closures), string value, or an empty string (for compiled code).
*/
Handle<String> GetName() const;
/** Returns node's own size, in bytes. */
int GetSelfSize() const;
/** Returns node's network (self + reachable nodes) size, in bytes. */
int GetTotalSize() const;
/**
* Returns node's private size, in bytes. That is, the size of memory
* that will be reclaimed having this node collected.
*/
int GetPrivateSize() const;
/** Returns child nodes count of the node. */
int GetChildrenCount() const;
/** Retrieves a child by index. */
const HeapGraphEdge* GetChild(int index) const;
/** Returns retainer nodes count of the node. */
int GetRetainersCount() const;
/** Returns a retainer by index. */
const HeapGraphEdge* GetRetainer(int index) const;
/** Returns the number of simple retaining paths from the root to the node. */
int GetRetainingPathsCount() const;
/** Returns a retaining path by index. */
const HeapGraphPath* GetRetainingPath(int index) const;
};
/**
* HeapSnapshots record the state of the JS heap at some moment.
*/
class V8EXPORT HeapSnapshot {
public:
/** Returns heap snapshot UID (assigned by the profiler.) */
unsigned GetUid() const;
/** Returns heap snapshot title. */
Handle<String> GetTitle() const;
/** Returns the root node of the heap graph. */
const HeapGraphNode* GetHead() const;
};
/**
* Interface for controlling heap profiling.
*/
class V8EXPORT HeapProfiler {
public:
/** Returns the number of snapshots taken. */
static int GetSnapshotsCount();
/** Returns a snapshot by index. */
static const HeapSnapshot* GetSnapshot(int index);
/** Returns a profile by uid. */
static const HeapSnapshot* FindSnapshot(unsigned uid);
/** Takes a heap snapshot and returns it. Title may be an empty string. */
static const HeapSnapshot* TakeSnapshot(Handle<String> title);
};
} // namespace v8
......
......@@ -34,6 +34,7 @@
#include "debug.h"
#include "execution.h"
#include "global-handles.h"
#include "heap-profiler.h"
#include "messages.h"
#include "platform.h"
#include "profile-generator-inl.h"
......@@ -4446,6 +4447,196 @@ const CpuProfile* CpuProfiler::StopProfiling(Handle<String> title,
*Utils::OpenHandle(*title)));
}
HeapGraphEdge::Type HeapGraphEdge::GetType() const {
IsDeadCheck("v8::HeapGraphEdge::GetType");
return static_cast<HeapGraphEdge::Type>(
reinterpret_cast<const i::HeapGraphEdge*>(this)->type());
}
Handle<Value> HeapGraphEdge::GetName() const {
IsDeadCheck("v8::HeapGraphEdge::GetName");
const i::HeapGraphEdge* edge =
reinterpret_cast<const i::HeapGraphEdge*>(this);
switch (edge->type()) {
case i::HeapGraphEdge::CONTEXT_VARIABLE:
case i::HeapGraphEdge::PROPERTY:
return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol(
edge->name())));
case i::HeapGraphEdge::ELEMENT:
return Handle<Number>(ToApi<Number>(i::Factory::NewNumberFromInt(
edge->index())));
default: UNREACHABLE();
}
return ImplementationUtilities::Undefined();
}
const HeapGraphNode* HeapGraphEdge::GetFromNode() const {
IsDeadCheck("v8::HeapGraphEdge::GetFromNode");
const i::HeapEntry* from =
reinterpret_cast<const i::HeapGraphEdge*>(this)->from();
return reinterpret_cast<const HeapGraphNode*>(from);
}
const HeapGraphNode* HeapGraphEdge::GetToNode() const {
IsDeadCheck("v8::HeapGraphEdge::GetToNode");
const i::HeapEntry* to =
reinterpret_cast<const i::HeapGraphEdge*>(this)->to();
return reinterpret_cast<const HeapGraphNode*>(to);
}
int HeapGraphPath::GetEdgesCount() const {
return reinterpret_cast<const i::HeapGraphPath*>(this)->path()->length();
}
const HeapGraphEdge* HeapGraphPath::GetEdge(int index) const {
return reinterpret_cast<const HeapGraphEdge*>(
reinterpret_cast<const i::HeapGraphPath*>(this)->path()->at(index));
}
const HeapGraphNode* HeapGraphPath::GetFromNode() const {
return GetEdgesCount() > 0 ? GetEdge(0)->GetFromNode() : NULL;
}
const HeapGraphNode* HeapGraphPath::GetToNode() const {
const int count = GetEdgesCount();
return count > 0 ? GetEdge(count - 1)->GetToNode() : NULL;
}
HeapGraphNode::Type HeapGraphNode::GetType() const {
IsDeadCheck("v8::HeapGraphNode::GetType");
return static_cast<HeapGraphNode::Type>(
reinterpret_cast<const i::HeapEntry*>(this)->type());
}
Handle<String> HeapGraphNode::GetName() const {
IsDeadCheck("v8::HeapGraphNode::GetName");
return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol(
reinterpret_cast<const i::HeapEntry*>(this)->name())));
}
int HeapGraphNode::GetSelfSize() const {
IsDeadCheck("v8::HeapGraphNode::GetSelfSize");
return reinterpret_cast<const i::HeapEntry*>(this)->self_size();
}
int HeapGraphNode::GetTotalSize() const {
IsDeadCheck("v8::HeapSnapshot::GetHead");
return const_cast<i::HeapEntry*>(
reinterpret_cast<const i::HeapEntry*>(this))->TotalSize();
}
int HeapGraphNode::GetPrivateSize() const {
IsDeadCheck("v8::HeapSnapshot::GetPrivateSize");
return const_cast<i::HeapEntry*>(
reinterpret_cast<const i::HeapEntry*>(this))->NonSharedTotalSize();
}
int HeapGraphNode::GetChildrenCount() const {
IsDeadCheck("v8::HeapSnapshot::GetChildrenCount");
return reinterpret_cast<const i::HeapEntry*>(this)->children()->length();
}
const HeapGraphEdge* HeapGraphNode::GetChild(int index) const {
IsDeadCheck("v8::HeapSnapshot::GetChild");
return reinterpret_cast<const HeapGraphEdge*>(
reinterpret_cast<const i::HeapEntry*>(this)->children()->at(index));
}
int HeapGraphNode::GetRetainersCount() const {
IsDeadCheck("v8::HeapSnapshot::GetRetainersCount");
return reinterpret_cast<const i::HeapEntry*>(this)->retainers()->length();
}
const HeapGraphEdge* HeapGraphNode::GetRetainer(int index) const {
IsDeadCheck("v8::HeapSnapshot::GetRetainer");
return reinterpret_cast<const HeapGraphEdge*>(
reinterpret_cast<const i::HeapEntry*>(this)->retainers()->at(index));
}
int HeapGraphNode::GetRetainingPathsCount() const {
IsDeadCheck("v8::HeapSnapshot::GetRetainingPathsCount");
return const_cast<i::HeapEntry*>(
reinterpret_cast<const i::HeapEntry*>(
this))->GetRetainingPaths()->length();
}
const HeapGraphPath* HeapGraphNode::GetRetainingPath(int index) const {
IsDeadCheck("v8::HeapSnapshot::GetRetainingPath");
return reinterpret_cast<const HeapGraphPath*>(
const_cast<i::HeapEntry*>(
reinterpret_cast<const i::HeapEntry*>(
this))->GetRetainingPaths()->at(index));
}
unsigned HeapSnapshot::GetUid() const {
IsDeadCheck("v8::HeapSnapshot::GetUid");
return reinterpret_cast<const i::HeapSnapshot*>(this)->uid();
}
Handle<String> HeapSnapshot::GetTitle() const {
IsDeadCheck("v8::HeapSnapshot::GetTitle");
const i::HeapSnapshot* snapshot =
reinterpret_cast<const i::HeapSnapshot*>(this);
return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol(
snapshot->title())));
}
const HeapGraphNode* HeapSnapshot::GetHead() const {
IsDeadCheck("v8::HeapSnapshot::GetHead");
const i::HeapSnapshot* snapshot =
reinterpret_cast<const i::HeapSnapshot*>(this);
return reinterpret_cast<const HeapGraphNode*>(snapshot->const_root());
}
int HeapProfiler::GetSnapshotsCount() {
IsDeadCheck("v8::HeapProfiler::GetSnapshotsCount");
return i::HeapProfiler::GetSnapshotsCount();
}
const HeapSnapshot* HeapProfiler::GetSnapshot(int index) {
IsDeadCheck("v8::HeapProfiler::GetSnapshot");
return reinterpret_cast<const HeapSnapshot*>(
i::HeapProfiler::GetSnapshot(index));
}
const HeapSnapshot* HeapProfiler::FindSnapshot(unsigned uid) {
IsDeadCheck("v8::HeapProfiler::FindSnapshot");
return reinterpret_cast<const HeapSnapshot*>(
i::HeapProfiler::FindSnapshot(uid));
}
const HeapSnapshot* HeapProfiler::TakeSnapshot(Handle<String> title) {
IsDeadCheck("v8::HeapProfiler::TakeSnapshot");
return reinterpret_cast<const HeapSnapshot*>(
i::HeapProfiler::TakeSnapshot(*Utils::OpenHandle(*title)));
}
#endif // ENABLE_LOGGING_AND_PROFILING
......
......@@ -30,8 +30,8 @@
#include "heap-profiler.h"
#include "frames-inl.h"
#include "global-handles.h"
#include "profile-generator.h"
#include "string-stream.h"
#include "zone-inl.h"
namespace v8 {
namespace internal {
......@@ -314,6 +314,75 @@ void RetainerTreeAggregator::Call(const JSObjectsCluster& cluster,
} // namespace
HeapProfiler* HeapProfiler::singleton_ = NULL;
HeapProfiler::HeapProfiler()
: snapshots_(new HeapSnapshotsCollection()),
next_snapshot_uid_(1) {
}
HeapProfiler::~HeapProfiler() {
delete snapshots_;
}
void HeapProfiler::Setup() {
if (singleton_ == NULL) {
singleton_ = new HeapProfiler();
}
}
void HeapProfiler::TearDown() {
delete singleton_;
singleton_ = NULL;
}
HeapSnapshot* HeapProfiler::TakeSnapshot(const char* name) {
ASSERT(singleton_ != NULL);
return singleton_->TakeSnapshotImpl(name);
}
HeapSnapshot* HeapProfiler::TakeSnapshot(String* name) {
ASSERT(singleton_ != NULL);
return singleton_->TakeSnapshotImpl(name);
}
HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name) {
HeapSnapshot* result = snapshots_->NewSnapshot(name, next_snapshot_uid_++);
HeapSnapshotGenerator generator(result);
generator.GenerateSnapshot();
return result;
}
HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name) {
return TakeSnapshotImpl(snapshots_->GetName(name));
}
int HeapProfiler::GetSnapshotsCount() {
ASSERT(singleton_ != NULL);
return singleton_->snapshots_->snapshots()->length();
}
HeapSnapshot* HeapProfiler::GetSnapshot(int index) {
ASSERT(singleton_ != NULL);
return singleton_->snapshots_->snapshots()->at(index);
}
HeapSnapshot* HeapProfiler::FindSnapshot(unsigned uid) {
ASSERT(singleton_ != NULL);
return singleton_->snapshots_->GetSnapshot(uid);
}
const JSObjectsClusterTreeConfig::Key JSObjectsClusterTreeConfig::kNoKey;
const JSObjectsClusterTreeConfig::Value JSObjectsClusterTreeConfig::kNoValue;
......
......@@ -28,23 +28,46 @@
#ifndef V8_HEAP_PROFILER_H_
#define V8_HEAP_PROFILER_H_
#include "zone.h"
#include "zone-inl.h"
namespace v8 {
namespace internal {
#ifdef ENABLE_LOGGING_AND_PROFILING
class HeapSnapshot;
class HeapSnapshotsCollection;
// The HeapProfiler writes data to the log files, which can be postprocessed
// to generate .hp files for use by the GHC/Valgrind tool hp2ps.
class HeapProfiler {
public:
static void Setup();
static void TearDown();
static HeapSnapshot* TakeSnapshot(const char* name);
static HeapSnapshot* TakeSnapshot(String* name);
static int GetSnapshotsCount();
static HeapSnapshot* GetSnapshot(int index);
static HeapSnapshot* FindSnapshot(unsigned uid);
// Obsolete interface.
// Write a single heap sample to the log file.
static void WriteSample();
private:
HeapProfiler();
~HeapProfiler();
HeapSnapshot* TakeSnapshotImpl(const char* name);
HeapSnapshot* TakeSnapshotImpl(String* name);
// Obsolete interface.
// Update the array info with stats from obj.
static void CollectStats(HeapObject* obj, HistogramInfo* info);
HeapSnapshotsCollection* snapshots_;
unsigned next_snapshot_uid_;
static HeapProfiler* singleton_;
};
......
......@@ -1114,7 +1114,7 @@ void HeapEntry::Print(int max_depth, int indent) {
const char* HeapEntry::TypeAsString() {
switch (type_) {
case INTERNAL: return "/internal/";
case JS_OBJECT: return "/object/";
case OBJECT: return "/object/";
case CLOSURE: return "/closure/";
case STRING: return "/string/";
case CODE: return "/code/";
......@@ -1262,7 +1262,7 @@ HeapEntry* HeapSnapshot::GetEntry(Object* obj) {
return AddEntry(object, HeapEntry::CLOSURE, collection_->GetName(name));
} else if (object->IsJSObject()) {
return AddEntry(object,
HeapEntry::JS_OBJECT,
HeapEntry::OBJECT,
collection_->GetName(
JSObject::cast(object)->constructor_name()));
} else if (object->IsJSGlobalPropertyCell()) {
......@@ -1276,10 +1276,19 @@ HeapEntry* HeapSnapshot::GetEntry(Object* obj) {
return AddEntry(object,
HeapEntry::STRING,
collection_->GetName(String::cast(object)));
} else if (object->IsCode()
|| object->IsSharedFunctionInfo()
|| object->IsScript()) {
} else if (object->IsCode()) {
return AddEntry(object, HeapEntry::CODE);
} else if (object->IsSharedFunctionInfo()) {
SharedFunctionInfo* shared = SharedFunctionInfo::cast(object);
String* name = String::cast(shared->name())->length() > 0 ?
String::cast(shared->name()) : shared->inferred_name();
return AddEntry(object, HeapEntry::CODE, collection_->GetName(name));
} else if (object->IsScript()) {
Script* script = Script::cast(object);
return AddEntry(object,
HeapEntry::CODE,
script->name()->IsString() ?
collection_->GetName(String::cast(script->name())) : "");
} else if (object->IsFixedArray()) {
return AddEntry(object, HeapEntry::ARRAY);
}
......
......@@ -429,9 +429,9 @@ class HeapEntry;
class HeapGraphEdge {
public:
enum Type {
CONTEXT_VARIABLE,
ELEMENT,
PROPERTY
CONTEXT_VARIABLE = v8::HeapGraphEdge::CONTEXT_VARIABLE,
ELEMENT = v8::HeapGraphEdge::ELEMENT,
PROPERTY = v8::HeapGraphEdge::PROPERTY
};
HeapGraphEdge(Type type, const char* name, HeapEntry* from, HeapEntry* to);
......@@ -468,12 +468,12 @@ class CachedHeapGraphPath;
class HeapEntry {
public:
enum Type {
INTERNAL,
ARRAY,
STRING,
JS_OBJECT,
CODE,
CLOSURE
INTERNAL = v8::HeapGraphNode::INTERNAL,
ARRAY = v8::HeapGraphNode::ARRAY,
STRING = v8::HeapGraphNode::STRING,
OBJECT = v8::HeapGraphNode::OBJECT,
CODE = v8::HeapGraphNode::CODE,
CLOSURE = v8::HeapGraphNode::CLOSURE
};
explicit HeapEntry(HeapSnapshot* snapshot)
......
......@@ -32,6 +32,7 @@
#include "serialize.h"
#include "simulator.h"
#include "stub-cache.h"
#include "heap-profiler.h"
#include "oprofile-agent.h"
#include "log.h"
......@@ -61,6 +62,7 @@ bool V8::Initialize(Deserializer* des) {
Logger::Setup();
CpuProfiler::Setup();
HeapProfiler::Setup();
// Setup the platform OS support.
OS::Setup();
......@@ -149,6 +151,8 @@ void V8::TearDown() {
Top::TearDown();
HeapProfiler::TearDown();
CpuProfiler::TearDown();
Heap::TearDown();
......
......@@ -9,6 +9,7 @@
#include "string-stream.h"
#include "cctest.h"
#include "zone-inl.h"
#include "../include/v8-profiler.h"
namespace i = v8::internal;
using i::ClustersCoarser;
......@@ -390,4 +391,163 @@ TEST(RetainerProfile) {
CHECK_EQ("(global property);1", printer.GetRetainers("C"));
}
namespace {
class NamedEntriesDetector {
public:
NamedEntriesDetector()
: has_A1(false), has_B1(false), has_C1(false),
has_A2(false), has_B2(false), has_C2(false) {
}
void Apply(i::HeapEntry* entry) {
const char* node_name = entry->name();
if (strcmp("A1", node_name) == 0
&& entry->GetRetainingPaths()->length() > 0) has_A1 = true;
if (strcmp("B1", node_name) == 0
&& entry->GetRetainingPaths()->length() > 0) has_B1 = true;
if (strcmp("C1", node_name) == 0
&& entry->GetRetainingPaths()->length() > 0) has_C1 = true;
if (strcmp("A2", node_name) == 0
&& entry->GetRetainingPaths()->length() > 0) has_A2 = true;
if (strcmp("B2", node_name) == 0
&& entry->GetRetainingPaths()->length() > 0) has_B2 = true;
if (strcmp("C2", node_name) == 0
&& entry->GetRetainingPaths()->length() > 0) has_C2 = true;
}
bool has_A1;
bool has_B1;
bool has_C1;
bool has_A2;
bool has_B2;
bool has_C2;
};
} // namespace
TEST(HeapSnapshot) {
v8::HandleScope scope;
v8::Handle<v8::String> token1 = v8::String::New("token1");
v8::Handle<v8::Context> env1 = v8::Context::New();
env1->SetSecurityToken(token1);
env1->Enter();
CompileAndRunScript(
"function A1() {}\n"
"function B1(x) { this.x = x; }\n"
"function C1(x) { this.x1 = x; this.x2 = x; }\n"
"var a1 = new A1();\n"
"var b1_1 = new B1(a1), b1_2 = new B1(a1);\n"
"var c1 = new C1(a1);");
v8::Handle<v8::String> token2 = v8::String::New("token2");
v8::Handle<v8::Context> env2 = v8::Context::New();
env2->SetSecurityToken(token2);
env2->Enter();
CompileAndRunScript(
"function A2() {}\n"
"function B2(x) { return function() { return typeof x; }; }\n"
"function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n"
"var a2 = new A2();\n"
"var b2_1 = new B2(a2), b2_2 = new B2(a2);\n"
"var c2 = new C2(a2);");
const v8::HeapSnapshot* snapshot_env2 =
v8::HeapProfiler::TakeSnapshot(v8::String::New("env2"));
CHECK_EQ(1, snapshot_env2->GetHead()->GetChildrenCount());
const v8::HeapGraphNode* global_env2 =
snapshot_env2->GetHead()->GetChild(0)->GetToNode();
// Verify, that JS global object of env2 doesn't have '..1'
// properties, but has '..2' properties.
bool has_a1 = false, has_b1_1 = false, has_b1_2 = false, has_c1 = false;
bool has_a2 = false, has_b2_1 = false, has_b2_2 = false, has_c2 = false;
// This will be needed further.
const v8::HeapGraphNode* a2_node = NULL;
for (int i = 0, count = global_env2->GetChildrenCount(); i < count; ++i) {
const v8::HeapGraphEdge* prop = global_env2->GetChild(i);
v8::String::AsciiValue prop_name(prop->GetName());
if (strcmp("a1", *prop_name) == 0) has_a1 = true;
if (strcmp("b1_1", *prop_name) == 0) has_b1_1 = true;
if (strcmp("b1_2", *prop_name) == 0) has_b1_2 = true;
if (strcmp("c1", *prop_name) == 0) has_c1 = true;
if (strcmp("a2", *prop_name) == 0) {
has_a2 = true;
a2_node = prop->GetToNode();
}
if (strcmp("b2_1", *prop_name) == 0) has_b2_1 = true;
if (strcmp("b2_2", *prop_name) == 0) has_b2_2 = true;
if (strcmp("c2", *prop_name) == 0) has_c2 = true;
}
CHECK(!has_a1);
CHECK(!has_b1_1);
CHECK(!has_b1_2);
CHECK(!has_c1);
CHECK(has_a2);
CHECK(has_b2_1);
CHECK(has_b2_2);
CHECK(has_c2);
// Verify that anything related to '[ABC]1' is not reachable.
NamedEntriesDetector det;
i::HeapSnapshot* i_snapshot_env2 =
const_cast<i::HeapSnapshot*>(
reinterpret_cast<const i::HeapSnapshot*>(snapshot_env2));
i_snapshot_env2->IterateEntries(&det);
CHECK(!det.has_A1);
CHECK(!det.has_B1);
CHECK(!det.has_C1);
CHECK(det.has_A2);
CHECK(det.has_B2);
CHECK(det.has_C2);
// Verify 'a2' object retainers. They are:
// - (global object).a2
// - c2.x1, c2.x2, c2[1]
// - b2_1 and b2_2 closures: via 'x' variable
CHECK_EQ(6, a2_node->GetRetainingPathsCount());
bool has_global_obj_a2_ref = false;
bool has_c2_x1_ref = false, has_c2_x2_ref = false, has_c2_1_ref = false;
bool has_b2_1_x_ref = false, has_b2_2_x_ref = false;
for (int i = 0; i < a2_node->GetRetainingPathsCount(); ++i) {
const v8::HeapGraphPath* path = a2_node->GetRetainingPath(i);
const int edges_count = path->GetEdgesCount();
CHECK_GT(edges_count, 0);
const v8::HeapGraphEdge* last_edge = path->GetEdge(edges_count - 1);
v8::String::AsciiValue last_edge_name(last_edge->GetName());
if (strcmp("a2", *last_edge_name) == 0
&& last_edge->GetType() == v8::HeapGraphEdge::PROPERTY) {
has_global_obj_a2_ref = true;
continue;
}
CHECK_GT(edges_count, 1);
const v8::HeapGraphEdge* prev_edge = path->GetEdge(edges_count - 2);
v8::String::AsciiValue prev_edge_name(prev_edge->GetName());
if (strcmp("x1", *last_edge_name) == 0
&& last_edge->GetType() == v8::HeapGraphEdge::PROPERTY
&& strcmp("c2", *prev_edge_name) == 0) has_c2_x1_ref = true;
if (strcmp("x2", *last_edge_name) == 0
&& last_edge->GetType() == v8::HeapGraphEdge::PROPERTY
&& strcmp("c2", *prev_edge_name) == 0) has_c2_x2_ref = true;
if (strcmp("1", *last_edge_name) == 0
&& last_edge->GetType() == v8::HeapGraphEdge::ELEMENT
&& strcmp("c2", *prev_edge_name) == 0) has_c2_1_ref = true;
if (strcmp("x", *last_edge_name) == 0
&& last_edge->GetType() == v8::HeapGraphEdge::CONTEXT_VARIABLE
&& strcmp("b2_1", *prev_edge_name) == 0) has_b2_1_x_ref = true;
if (strcmp("x", *last_edge_name) == 0
&& last_edge->GetType() == v8::HeapGraphEdge::CONTEXT_VARIABLE
&& strcmp("b2_2", *prev_edge_name) == 0) has_b2_2_x_ref = true;
}
CHECK(has_global_obj_a2_ref);
CHECK(has_c2_x1_ref);
CHECK(has_c2_x2_ref);
CHECK(has_c2_1_ref);
CHECK(has_b2_1_x_ref);
CHECK(has_b2_2_x_ref);
}
#endif // ENABLE_LOGGING_AND_PROFILING
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment