Heap profiler: add a missing link between a function closure and shared function info.
Review URL: http://codereview.chromium.org/2846012 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4891 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
7808d45af5
commit
93387f272e
@ -196,7 +196,9 @@ class V8EXPORT HeapGraphEdge {
|
||||
enum Type {
|
||||
CONTEXT_VARIABLE = 0, // A variable from a function context.
|
||||
ELEMENT = 1, // An element of an array.
|
||||
PROPERTY = 2 // A named object property.
|
||||
PROPERTY = 2, // A named object property.
|
||||
INTERNAL = 3 // A link that can't be accessed from JS,
|
||||
// thus, its name isn't a real property name.
|
||||
};
|
||||
|
||||
/** Returns edge type (see HeapGraphEdge::Type). */
|
||||
|
@ -4460,6 +4460,7 @@ Handle<Value> HeapGraphEdge::GetName() const {
|
||||
reinterpret_cast<const i::HeapGraphEdge*>(this);
|
||||
switch (edge->type()) {
|
||||
case i::HeapGraphEdge::CONTEXT_VARIABLE:
|
||||
case i::HeapGraphEdge::INTERNAL:
|
||||
case i::HeapGraphEdge::PROPERTY:
|
||||
return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol(
|
||||
edge->name())));
|
||||
|
@ -155,9 +155,9 @@ static inline void CheckNonEqualsHelper(const char* file,
|
||||
static inline void CheckEqualsHelper(const char* file,
|
||||
int line,
|
||||
const char* expected_source,
|
||||
void* expected,
|
||||
const void* expected,
|
||||
const char* value_source,
|
||||
void* value) {
|
||||
const void* value) {
|
||||
if (expected != value) {
|
||||
V8_Fatal(file, line,
|
||||
"CHECK_EQ(%s, %s) failed\n# Expected: %p\n# Found: %p",
|
||||
@ -170,9 +170,9 @@ static inline void CheckEqualsHelper(const char* file,
|
||||
static inline void CheckNonEqualsHelper(const char* file,
|
||||
int line,
|
||||
const char* expected_source,
|
||||
void* expected,
|
||||
const void* expected,
|
||||
const char* value_source,
|
||||
void* value) {
|
||||
const void* value) {
|
||||
if (expected == value) {
|
||||
V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n# Value: %p",
|
||||
expected_source, value_source, value);
|
||||
|
@ -818,7 +818,7 @@ HeapGraphEdge::HeapGraphEdge(Type type,
|
||||
HeapEntry* from,
|
||||
HeapEntry* to)
|
||||
: type_(type), name_(name), from_(from), to_(to) {
|
||||
ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY);
|
||||
ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY || type_ == INTERNAL);
|
||||
}
|
||||
|
||||
|
||||
@ -845,26 +845,30 @@ HeapEntry::~HeapEntry() {
|
||||
}
|
||||
|
||||
|
||||
void HeapEntry::SetClosureReference(const char* name, HeapEntry* entry) {
|
||||
HeapGraphEdge* edge =
|
||||
new HeapGraphEdge(HeapGraphEdge::CONTEXT_VARIABLE, name, this, entry);
|
||||
void HeapEntry::AddEdge(HeapGraphEdge* edge) {
|
||||
children_.Add(edge);
|
||||
entry->retainers_.Add(edge);
|
||||
edge->to()->retainers_.Add(edge);
|
||||
}
|
||||
|
||||
|
||||
void HeapEntry::SetClosureReference(const char* name, HeapEntry* entry) {
|
||||
AddEdge(
|
||||
new HeapGraphEdge(HeapGraphEdge::CONTEXT_VARIABLE, name, this, entry));
|
||||
}
|
||||
|
||||
|
||||
void HeapEntry::SetElementReference(int index, HeapEntry* entry) {
|
||||
HeapGraphEdge* edge = new HeapGraphEdge(index, this, entry);
|
||||
children_.Add(edge);
|
||||
entry->retainers_.Add(edge);
|
||||
AddEdge(new HeapGraphEdge(index, this, entry));
|
||||
}
|
||||
|
||||
|
||||
void HeapEntry::SetInternalReference(const char* name, HeapEntry* entry) {
|
||||
AddEdge(new HeapGraphEdge(HeapGraphEdge::INTERNAL, name, this, entry));
|
||||
}
|
||||
|
||||
|
||||
void HeapEntry::SetPropertyReference(const char* name, HeapEntry* entry) {
|
||||
HeapGraphEdge* edge =
|
||||
new HeapGraphEdge(HeapGraphEdge::PROPERTY, name, this, entry);
|
||||
children_.Add(edge);
|
||||
entry->retainers_.Add(edge);
|
||||
AddEdge(new HeapGraphEdge(HeapGraphEdge::PROPERTY, name, this, entry));
|
||||
}
|
||||
|
||||
|
||||
@ -1074,7 +1078,7 @@ void HeapEntry::CutEdges() {
|
||||
|
||||
|
||||
void HeapEntry::Print(int max_depth, int indent) {
|
||||
OS::Print("%6d %6d %6d", self_size_, TotalSize(), NonSharedTotalSize());
|
||||
OS::Print("%6d %6d %6d ", self_size_, TotalSize(), NonSharedTotalSize());
|
||||
if (type_ != STRING) {
|
||||
OS::Print("%s %.40s\n", TypeAsString(), name_);
|
||||
} else {
|
||||
@ -1100,6 +1104,9 @@ void HeapEntry::Print(int max_depth, int indent) {
|
||||
case HeapGraphEdge::ELEMENT:
|
||||
OS::Print(" %*c %d: ", indent, ' ', edge->index());
|
||||
break;
|
||||
case HeapGraphEdge::INTERNAL:
|
||||
OS::Print(" %*c $%s: ", indent, ' ', edge->name());
|
||||
break;
|
||||
case HeapGraphEdge::PROPERTY:
|
||||
OS::Print(" %*c %s: ", indent, ' ', edge->name());
|
||||
break;
|
||||
@ -1145,6 +1152,9 @@ void HeapGraphPath::Print() {
|
||||
case HeapGraphEdge::ELEMENT:
|
||||
OS::Print("[%d] ", edge->index());
|
||||
break;
|
||||
case HeapGraphEdge::INTERNAL:
|
||||
OS::Print("[$%s] ", edge->name());
|
||||
break;
|
||||
case HeapGraphEdge::PROPERTY:
|
||||
OS::Print("[%s] ", edge->name());
|
||||
break;
|
||||
@ -1318,6 +1328,16 @@ void HeapSnapshot::SetElementReference(HeapEntry* parent,
|
||||
}
|
||||
|
||||
|
||||
void HeapSnapshot::SetInternalReference(HeapEntry* parent,
|
||||
const char* reference_name,
|
||||
Object* child) {
|
||||
HeapEntry* child_entry = GetEntry(child);
|
||||
if (child_entry != NULL) {
|
||||
parent->SetInternalReference(reference_name, child_entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HeapSnapshot::SetPropertyReference(HeapEntry* parent,
|
||||
String* reference_name,
|
||||
Object* child) {
|
||||
@ -1546,6 +1566,7 @@ void HeapSnapshotGenerator::ExtractClosureReferences(JSObject* js_obj,
|
||||
snapshot_->SetClosureReference(entry, local_name, context->get(idx));
|
||||
}
|
||||
}
|
||||
snapshot_->SetInternalReference(entry, "code", func->shared());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -431,7 +431,8 @@ class HeapGraphEdge {
|
||||
enum Type {
|
||||
CONTEXT_VARIABLE = v8::HeapGraphEdge::CONTEXT_VARIABLE,
|
||||
ELEMENT = v8::HeapGraphEdge::ELEMENT,
|
||||
PROPERTY = v8::HeapGraphEdge::PROPERTY
|
||||
PROPERTY = v8::HeapGraphEdge::PROPERTY,
|
||||
INTERNAL = v8::HeapGraphEdge::INTERNAL
|
||||
};
|
||||
|
||||
HeapGraphEdge(Type type, const char* name, HeapEntry* from, HeapEntry* to);
|
||||
@ -443,7 +444,7 @@ class HeapGraphEdge {
|
||||
return index_;
|
||||
}
|
||||
const char* name() const {
|
||||
ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY);
|
||||
ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY || type_ == INTERNAL);
|
||||
return name_;
|
||||
}
|
||||
HeapEntry* from() const { return from_; }
|
||||
@ -533,6 +534,7 @@ class HeapEntry {
|
||||
void PaintReachableFromOthers() { painted_ = kPaintReachableFromOthers; }
|
||||
void SetClosureReference(const char* name, HeapEntry* entry);
|
||||
void SetElementReference(int index, HeapEntry* entry);
|
||||
void SetInternalReference(const char* name, HeapEntry* entry);
|
||||
void SetPropertyReference(const char* name, HeapEntry* entry);
|
||||
void SetAutoIndexReference(HeapEntry* entry);
|
||||
|
||||
@ -542,6 +544,7 @@ class HeapEntry {
|
||||
void Print(int max_depth, int indent);
|
||||
|
||||
private:
|
||||
void AddEdge(HeapGraphEdge* edge);
|
||||
int CalculateTotalSize();
|
||||
int CalculateNonSharedTotalSize();
|
||||
void FindRetainingPaths(HeapEntry* node, CachedHeapGraphPath* prev_path);
|
||||
@ -641,6 +644,8 @@ class HeapSnapshot {
|
||||
void SetClosureReference(
|
||||
HeapEntry* parent, String* reference_name, Object* child);
|
||||
void SetElementReference(HeapEntry* parent, int index, Object* child);
|
||||
void SetInternalReference(
|
||||
HeapEntry* parent, const char* reference_name, Object* child);
|
||||
void SetPropertyReference(
|
||||
HeapEntry* parent, String* reference_name, Object* child);
|
||||
|
||||
|
@ -428,6 +428,53 @@ class NamedEntriesDetector {
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
static const v8::HeapGraphNode* GetGlobalObject(
|
||||
const v8::HeapSnapshot* snapshot) {
|
||||
if (i::Snapshot::IsEnabled()) {
|
||||
// In case if snapshots are enabled, there will present a
|
||||
// vanilla deserealized global object, without properties
|
||||
// added by the test code.
|
||||
CHECK_EQ(2, snapshot->GetHead()->GetChildrenCount());
|
||||
// Choose the global object of a bigger size.
|
||||
const v8::HeapGraphNode* node0 =
|
||||
snapshot->GetHead()->GetChild(0)->GetToNode();
|
||||
const v8::HeapGraphNode* node1 =
|
||||
snapshot->GetHead()->GetChild(1)->GetToNode();
|
||||
return node0->GetTotalSize() > node1->GetTotalSize() ? node0 : node1;
|
||||
} else {
|
||||
CHECK_EQ(1, snapshot->GetHead()->GetChildrenCount());
|
||||
return snapshot->GetHead()->GetChild(0)->GetToNode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node,
|
||||
v8::HeapGraphEdge::Type type,
|
||||
const char* name) {
|
||||
for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
|
||||
const v8::HeapGraphEdge* prop = node->GetChild(i);
|
||||
v8::String::AsciiValue prop_name(prop->GetName());
|
||||
if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
|
||||
return prop->GetToNode();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static bool HasString(const v8::HeapGraphNode* node, const char* contents) {
|
||||
for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
|
||||
const v8::HeapGraphEdge* prop = node->GetChild(i);
|
||||
const v8::HeapGraphNode* node = prop->GetToNode();
|
||||
if (node->GetType() == v8::HeapGraphNode::STRING) {
|
||||
v8::String::AsciiValue node_name(node->GetName());
|
||||
if (strcmp(contents, *node_name) == 0) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
TEST(HeapSnapshot) {
|
||||
v8::HandleScope scope;
|
||||
|
||||
@ -458,53 +505,20 @@ TEST(HeapSnapshot) {
|
||||
"var c2 = new C2(a2);");
|
||||
const v8::HeapSnapshot* snapshot_env2 =
|
||||
v8::HeapProfiler::TakeSnapshot(v8::String::New("env2"));
|
||||
const v8::HeapGraphNode* global_env2;
|
||||
if (i::Snapshot::IsEnabled()) {
|
||||
// In case if snapshots are enabled, there will present a
|
||||
// vanilla deserealized global object, without properties
|
||||
// added by the test code.
|
||||
CHECK_EQ(2, snapshot_env2->GetHead()->GetChildrenCount());
|
||||
// Choose the global object of a bigger size.
|
||||
const v8::HeapGraphNode* node0 =
|
||||
snapshot_env2->GetHead()->GetChild(0)->GetToNode();
|
||||
const v8::HeapGraphNode* node1 =
|
||||
snapshot_env2->GetHead()->GetChild(1)->GetToNode();
|
||||
global_env2 = node0->GetTotalSize() > node1->GetTotalSize() ?
|
||||
node0 : node1;
|
||||
} else {
|
||||
CHECK_EQ(1, snapshot_env2->GetHead()->GetChildrenCount());
|
||||
global_env2 = snapshot_env2->GetHead()->GetChild(0)->GetToNode();
|
||||
}
|
||||
const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2);
|
||||
|
||||
// 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);
|
||||
CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "a1"));
|
||||
CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b1_1"));
|
||||
CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b1_2"));
|
||||
CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "c1"));
|
||||
const v8::HeapGraphNode* a2_node =
|
||||
GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "a2");
|
||||
CHECK_NE(NULL, a2_node);
|
||||
CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b2_1"));
|
||||
CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b2_2"));
|
||||
CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "c2"));
|
||||
|
||||
// Verify that anything related to '[ABC]1' is not reachable.
|
||||
NamedEntriesDetector det;
|
||||
@ -565,4 +579,62 @@ TEST(HeapSnapshot) {
|
||||
CHECK(has_b2_2_x_ref);
|
||||
}
|
||||
|
||||
|
||||
TEST(HeapSnapshotCodeObjects) {
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::Context> env = v8::Context::New();
|
||||
env->Enter();
|
||||
|
||||
CompileAndRunScript(
|
||||
"function lazy(x) { return x - 1; }\n"
|
||||
"function compiled(x) { return x + 1; }\n"
|
||||
"compiled(1)");
|
||||
const v8::HeapSnapshot* snapshot =
|
||||
v8::HeapProfiler::TakeSnapshot(v8::String::New("code"));
|
||||
|
||||
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
||||
const v8::HeapGraphNode* compiled =
|
||||
GetProperty(global, v8::HeapGraphEdge::PROPERTY, "compiled");
|
||||
CHECK_NE(NULL, compiled);
|
||||
CHECK_EQ(v8::HeapGraphNode::CLOSURE, compiled->GetType());
|
||||
const v8::HeapGraphNode* lazy =
|
||||
GetProperty(global, v8::HeapGraphEdge::PROPERTY, "lazy");
|
||||
CHECK_NE(NULL, lazy);
|
||||
CHECK_EQ(v8::HeapGraphNode::CLOSURE, lazy->GetType());
|
||||
|
||||
// Find references to code.
|
||||
const v8::HeapGraphNode* compiled_code =
|
||||
GetProperty(compiled, v8::HeapGraphEdge::INTERNAL, "code");
|
||||
CHECK_NE(NULL, compiled_code);
|
||||
const v8::HeapGraphNode* lazy_code =
|
||||
GetProperty(lazy, v8::HeapGraphEdge::INTERNAL, "code");
|
||||
CHECK_NE(NULL, lazy_code);
|
||||
|
||||
// Verify that non-compiled code doesn't contain references to "x"
|
||||
// literal, while compiled code does.
|
||||
bool compiled_references_x = false, lazy_references_x = false;
|
||||
for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) {
|
||||
const v8::HeapGraphEdge* prop = compiled_code->GetChild(i);
|
||||
const v8::HeapGraphNode* node = prop->GetToNode();
|
||||
if (node->GetType() == v8::HeapGraphNode::CODE) {
|
||||
if (HasString(node, "x")) {
|
||||
compiled_references_x = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) {
|
||||
const v8::HeapGraphEdge* prop = lazy_code->GetChild(i);
|
||||
const v8::HeapGraphNode* node = prop->GetToNode();
|
||||
if (node->GetType() == v8::HeapGraphNode::CODE) {
|
||||
if (HasString(node, "x")) {
|
||||
lazy_references_x = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
CHECK(compiled_references_x);
|
||||
CHECK(!lazy_references_x);
|
||||
}
|
||||
|
||||
#endif // ENABLE_LOGGING_AND_PROFILING
|
||||
|
Loading…
Reference in New Issue
Block a user