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:
mikhail.naganov@gmail.com 2010-06-17 12:56:55 +00:00
parent 7808d45af5
commit 93387f272e
6 changed files with 165 additions and 64 deletions

View File

@ -196,7 +196,9 @@ class V8EXPORT HeapGraphEdge {
enum Type { enum Type {
CONTEXT_VARIABLE = 0, // A variable from a function context. CONTEXT_VARIABLE = 0, // A variable from a function context.
ELEMENT = 1, // An element of an array. 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). */ /** Returns edge type (see HeapGraphEdge::Type). */

View File

@ -4460,6 +4460,7 @@ Handle<Value> HeapGraphEdge::GetName() const {
reinterpret_cast<const i::HeapGraphEdge*>(this); reinterpret_cast<const i::HeapGraphEdge*>(this);
switch (edge->type()) { switch (edge->type()) {
case i::HeapGraphEdge::CONTEXT_VARIABLE: case i::HeapGraphEdge::CONTEXT_VARIABLE:
case i::HeapGraphEdge::INTERNAL:
case i::HeapGraphEdge::PROPERTY: case i::HeapGraphEdge::PROPERTY:
return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol( return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol(
edge->name()))); edge->name())));

View File

@ -155,9 +155,9 @@ static inline void CheckNonEqualsHelper(const char* file,
static inline void CheckEqualsHelper(const char* file, static inline void CheckEqualsHelper(const char* file,
int line, int line,
const char* expected_source, const char* expected_source,
void* expected, const void* expected,
const char* value_source, const char* value_source,
void* value) { const void* value) {
if (expected != value) { if (expected != value) {
V8_Fatal(file, line, V8_Fatal(file, line,
"CHECK_EQ(%s, %s) failed\n# Expected: %p\n# Found: %p", "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, static inline void CheckNonEqualsHelper(const char* file,
int line, int line,
const char* expected_source, const char* expected_source,
void* expected, const void* expected,
const char* value_source, const char* value_source,
void* value) { const void* value) {
if (expected == value) { if (expected == value) {
V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n# Value: %p", V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n# Value: %p",
expected_source, value_source, value); expected_source, value_source, value);

View File

@ -818,7 +818,7 @@ HeapGraphEdge::HeapGraphEdge(Type type,
HeapEntry* from, HeapEntry* from,
HeapEntry* to) HeapEntry* to)
: type_(type), name_(name), from_(from), to_(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) { void HeapEntry::AddEdge(HeapGraphEdge* edge) {
HeapGraphEdge* edge =
new HeapGraphEdge(HeapGraphEdge::CONTEXT_VARIABLE, name, this, entry);
children_.Add(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) { void HeapEntry::SetElementReference(int index, HeapEntry* entry) {
HeapGraphEdge* edge = new HeapGraphEdge(index, this, entry); AddEdge(new HeapGraphEdge(index, this, entry));
children_.Add(edge); }
entry->retainers_.Add(edge);
void HeapEntry::SetInternalReference(const char* name, HeapEntry* entry) {
AddEdge(new HeapGraphEdge(HeapGraphEdge::INTERNAL, name, this, entry));
} }
void HeapEntry::SetPropertyReference(const char* name, HeapEntry* entry) { void HeapEntry::SetPropertyReference(const char* name, HeapEntry* entry) {
HeapGraphEdge* edge = AddEdge(new HeapGraphEdge(HeapGraphEdge::PROPERTY, name, this, entry));
new HeapGraphEdge(HeapGraphEdge::PROPERTY, name, this, entry);
children_.Add(edge);
entry->retainers_.Add(edge);
} }
@ -1074,7 +1078,7 @@ void HeapEntry::CutEdges() {
void HeapEntry::Print(int max_depth, int indent) { 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) { if (type_ != STRING) {
OS::Print("%s %.40s\n", TypeAsString(), name_); OS::Print("%s %.40s\n", TypeAsString(), name_);
} else { } else {
@ -1100,6 +1104,9 @@ void HeapEntry::Print(int max_depth, int indent) {
case HeapGraphEdge::ELEMENT: case HeapGraphEdge::ELEMENT:
OS::Print(" %*c %d: ", indent, ' ', edge->index()); OS::Print(" %*c %d: ", indent, ' ', edge->index());
break; break;
case HeapGraphEdge::INTERNAL:
OS::Print(" %*c $%s: ", indent, ' ', edge->name());
break;
case HeapGraphEdge::PROPERTY: case HeapGraphEdge::PROPERTY:
OS::Print(" %*c %s: ", indent, ' ', edge->name()); OS::Print(" %*c %s: ", indent, ' ', edge->name());
break; break;
@ -1145,6 +1152,9 @@ void HeapGraphPath::Print() {
case HeapGraphEdge::ELEMENT: case HeapGraphEdge::ELEMENT:
OS::Print("[%d] ", edge->index()); OS::Print("[%d] ", edge->index());
break; break;
case HeapGraphEdge::INTERNAL:
OS::Print("[$%s] ", edge->name());
break;
case HeapGraphEdge::PROPERTY: case HeapGraphEdge::PROPERTY:
OS::Print("[%s] ", edge->name()); OS::Print("[%s] ", edge->name());
break; 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, void HeapSnapshot::SetPropertyReference(HeapEntry* parent,
String* reference_name, String* reference_name,
Object* child) { Object* child) {
@ -1546,6 +1566,7 @@ void HeapSnapshotGenerator::ExtractClosureReferences(JSObject* js_obj,
snapshot_->SetClosureReference(entry, local_name, context->get(idx)); snapshot_->SetClosureReference(entry, local_name, context->get(idx));
} }
} }
snapshot_->SetInternalReference(entry, "code", func->shared());
} }
} }

View File

@ -431,7 +431,8 @@ class HeapGraphEdge {
enum Type { enum Type {
CONTEXT_VARIABLE = v8::HeapGraphEdge::CONTEXT_VARIABLE, CONTEXT_VARIABLE = v8::HeapGraphEdge::CONTEXT_VARIABLE,
ELEMENT = v8::HeapGraphEdge::ELEMENT, 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); HeapGraphEdge(Type type, const char* name, HeapEntry* from, HeapEntry* to);
@ -443,7 +444,7 @@ class HeapGraphEdge {
return index_; return index_;
} }
const char* name() const { const char* name() const {
ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY); ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY || type_ == INTERNAL);
return name_; return name_;
} }
HeapEntry* from() const { return from_; } HeapEntry* from() const { return from_; }
@ -533,6 +534,7 @@ class HeapEntry {
void PaintReachableFromOthers() { painted_ = kPaintReachableFromOthers; } void PaintReachableFromOthers() { painted_ = kPaintReachableFromOthers; }
void SetClosureReference(const char* name, HeapEntry* entry); void SetClosureReference(const char* name, HeapEntry* entry);
void SetElementReference(int index, HeapEntry* entry); void SetElementReference(int index, HeapEntry* entry);
void SetInternalReference(const char* name, HeapEntry* entry);
void SetPropertyReference(const char* name, HeapEntry* entry); void SetPropertyReference(const char* name, HeapEntry* entry);
void SetAutoIndexReference(HeapEntry* entry); void SetAutoIndexReference(HeapEntry* entry);
@ -542,6 +544,7 @@ class HeapEntry {
void Print(int max_depth, int indent); void Print(int max_depth, int indent);
private: private:
void AddEdge(HeapGraphEdge* edge);
int CalculateTotalSize(); int CalculateTotalSize();
int CalculateNonSharedTotalSize(); int CalculateNonSharedTotalSize();
void FindRetainingPaths(HeapEntry* node, CachedHeapGraphPath* prev_path); void FindRetainingPaths(HeapEntry* node, CachedHeapGraphPath* prev_path);
@ -641,6 +644,8 @@ class HeapSnapshot {
void SetClosureReference( void SetClosureReference(
HeapEntry* parent, String* reference_name, Object* child); HeapEntry* parent, String* reference_name, Object* child);
void SetElementReference(HeapEntry* parent, int index, Object* child); void SetElementReference(HeapEntry* parent, int index, Object* child);
void SetInternalReference(
HeapEntry* parent, const char* reference_name, Object* child);
void SetPropertyReference( void SetPropertyReference(
HeapEntry* parent, String* reference_name, Object* child); HeapEntry* parent, String* reference_name, Object* child);

View File

@ -428,6 +428,53 @@ class NamedEntriesDetector {
} // namespace } // 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) { TEST(HeapSnapshot) {
v8::HandleScope scope; v8::HandleScope scope;
@ -458,53 +505,20 @@ TEST(HeapSnapshot) {
"var c2 = new C2(a2);"); "var c2 = new C2(a2);");
const v8::HeapSnapshot* snapshot_env2 = const v8::HeapSnapshot* snapshot_env2 =
v8::HeapProfiler::TakeSnapshot(v8::String::New("env2")); v8::HeapProfiler::TakeSnapshot(v8::String::New("env2"));
const v8::HeapGraphNode* global_env2; const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_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();
}
// Verify, that JS global object of env2 doesn't have '..1' // Verify, that JS global object of env2 doesn't have '..1'
// properties, but has '..2' properties. // properties, but has '..2' properties.
bool has_a1 = false, has_b1_1 = false, has_b1_2 = false, has_c1 = false; CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "a1"));
bool has_a2 = false, has_b2_1 = false, has_b2_2 = false, has_c2 = false; CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b1_1"));
// This will be needed further. CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b1_2"));
const v8::HeapGraphNode* a2_node = NULL; CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "c1"));
for (int i = 0, count = global_env2->GetChildrenCount(); i < count; ++i) { const v8::HeapGraphNode* a2_node =
const v8::HeapGraphEdge* prop = global_env2->GetChild(i); GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "a2");
v8::String::AsciiValue prop_name(prop->GetName()); CHECK_NE(NULL, a2_node);
if (strcmp("a1", *prop_name) == 0) has_a1 = true; CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b2_1"));
if (strcmp("b1_1", *prop_name) == 0) has_b1_1 = true; CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b2_2"));
if (strcmp("b1_2", *prop_name) == 0) has_b1_2 = true; CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "c2"));
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. // Verify that anything related to '[ABC]1' is not reachable.
NamedEntriesDetector det; NamedEntriesDetector det;
@ -565,4 +579,62 @@ TEST(HeapSnapshot) {
CHECK(has_b2_2_x_ref); 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 #endif // ENABLE_LOGGING_AND_PROFILING