From d4bc8e1585af89d9798558383d0e9ea00c53b238 Mon Sep 17 00:00:00 2001 From: "mikhail.naganov@gmail.com" Date: Mon, 18 Oct 2010 09:15:38 +0000 Subject: [PATCH] New Heap profiler: add dumping HeapNumbers and InternalFields to snapshot. HeapNumbers do consume memory, so it's worth dumping them. However, we don't dump their values, as they are not as self-descriptive as values of strings, and they will increase snapshot size. Storing heap numbers values can be added if we will feel a sufficient demand for that. InternalFields are used, e.g. for storing references to DOM nodes event handlers. Review URL: http://codereview.chromium.org/3769007 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5635 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- include/v8-profiler.h | 3 +- src/profile-generator.cc | 84 ++++++++++++++++++++++--------- src/profile-generator.h | 18 +++++-- test/cctest/test-heap-profiler.cc | 67 +++++++++++++++++------- 4 files changed, 126 insertions(+), 46 deletions(-) diff --git a/include/v8-profiler.h b/include/v8-profiler.h index b59d1556a5..fb492d955c 100644 --- a/include/v8-profiler.h +++ b/include/v8-profiler.h @@ -246,7 +246,8 @@ class V8EXPORT HeapGraphNode { kObject = 3, // A JS object (except for arrays and strings). kCode = 4, // Compiled code. kClosure = 5, // Function closure. - kRegExp = 6 // RegExp. + kRegExp = 6, // RegExp. + kHeapNumber = 7 // Number stored in the heap. }; /** Returns node type (see HeapGraphNode::Type). */ diff --git a/src/profile-generator.cc b/src/profile-generator.cc index fe3bc669cb..977c67c8cc 100644 --- a/src/profile-generator.cc +++ b/src/profile-generator.cc @@ -94,12 +94,18 @@ StringsStorage::StringsStorage() } +static void DeleteIndexName(char** name_ptr) { + DeleteArray(*name_ptr); +} + + StringsStorage::~StringsStorage() { for (HashMap::Entry* p = names_.Start(); p != NULL; p = names_.Next(p)) { DeleteArray(reinterpret_cast(p->value)); } + index_names_.Iterate(DeleteIndexName); } @@ -120,6 +126,22 @@ const char* StringsStorage::GetName(String* name) { } +const char* StringsStorage::GetName(int index) { + ASSERT(index >= 0); + if (index_names_.length() <= index) { + index_names_.AddBlock( + NULL, index - index_names_.length() + 1); + } + if (index_names_[index] == NULL) { + const int kMaximumNameLength = 32; + char* name = NewArray(kMaximumNameLength); + OS::SNPrintF(Vector(name, kMaximumNameLength), "%d", index); + index_names_[index] = name; + } + return index_names_[index]; +} + + const char* CodeEntry::kEmptyNamePrefix = ""; @@ -485,11 +507,6 @@ CpuProfilesCollection::CpuProfilesCollection() } -static void DeleteArgsCountName(char** name_ptr) { - DeleteArray(*name_ptr); -} - - static void DeleteCodeEntry(CodeEntry** entry_ptr) { delete *entry_ptr; } @@ -508,7 +525,6 @@ CpuProfilesCollection::~CpuProfilesCollection() { current_profiles_.Iterate(DeleteCpuProfile); profiles_by_token_.Iterate(DeleteProfilesList); code_entries_.Iterate(DeleteCodeEntry); - args_count_names_.Iterate(DeleteArgsCountName); } @@ -706,22 +722,6 @@ CodeEntry* CpuProfilesCollection::NewCodeEntry(int security_token_id) { } -const char* CpuProfilesCollection::GetName(int args_count) { - ASSERT(args_count >= 0); - if (args_count_names_.length() <= args_count) { - args_count_names_.AddBlock( - NULL, args_count - args_count_names_.length() + 1); - } - if (args_count_names_[args_count] == NULL) { - const int kMaximumNameLength = 32; - char* name = NewArray(kMaximumNameLength); - OS::SNPrintF(Vector(name, kMaximumNameLength), "%d", args_count); - args_count_names_[args_count] = name; - } - return args_count_names_[args_count]; -} - - void CpuProfilesCollection::AddPathToCurrentProfiles( const Vector& path) { // As starting / stopping profiles is rare relatively to this @@ -1002,6 +1002,7 @@ const char* HeapEntry::TypeAsString() { case kCode: return "/code/"; case kArray: return "/array/"; case kRegExp: return "/regexp/"; + case kHeapNumber: return "/number/"; default: return "???"; } } @@ -1339,6 +1340,12 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object, "", children_count, retainers_count); + } else if (object->IsHeapNumber()) { + return AddEntry(object, + HeapEntry::kHeapNumber, + "number", + children_count, + retainers_count); } // No interest in this object. return NULL; @@ -1354,7 +1361,8 @@ bool HeapSnapshot::WillAddEntry(HeapObject* object) { || object->IsCode() || object->IsSharedFunctionInfo() || object->IsScript() - || object->IsFixedArray(); + || object->IsFixedArray() + || object->IsHeapNumber(); } @@ -1911,6 +1919,7 @@ void HeapSnapshotGenerator::ExtractReferences(HeapObject* obj) { ExtractClosureReferences(js_obj, entry); ExtractPropertyReferences(js_obj, entry); ExtractElementReferences(js_obj, entry); + ExtractInternalReferences(js_obj, entry); SetPropertyReference( obj, entry, Heap::Proto_symbol(), js_obj->GetPrototype()); if (obj->IsJSFunction()) { @@ -2019,6 +2028,16 @@ void HeapSnapshotGenerator::ExtractElementReferences(JSObject* js_obj, } +void HeapSnapshotGenerator::ExtractInternalReferences(JSObject* js_obj, + HeapEntry* entry) { + int length = js_obj->GetInternalFieldCount(); + for (int i = 0; i < length; ++i) { + Object* o = js_obj->GetInternalField(i); + SetInternalReference(js_obj, entry, i, o); + } +} + + void HeapSnapshotGenerator::SetClosureReference(HeapObject* parent_obj, HeapEntry* parent_entry, String* reference_name, @@ -2063,6 +2082,22 @@ void HeapSnapshotGenerator::SetInternalReference(HeapObject* parent_obj, } +void HeapSnapshotGenerator::SetInternalReference(HeapObject* parent_obj, + HeapEntry* parent_entry, + int index, + Object* child_obj) { + HeapEntry* child_entry = GetEntry(child_obj); + if (child_entry != NULL) { + filler_->SetNamedReference(HeapGraphEdge::kInternal, + parent_obj, + parent_entry, + collection_->GetName(index), + child_obj, + child_entry); + } +} + + void HeapSnapshotGenerator::SetPropertyReference(HeapObject* parent_obj, HeapEntry* parent_entry, String* reference_name, @@ -2368,7 +2403,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() { "," JSON_S("object") "," JSON_S("code") "," JSON_S("closure") - "," JSON_S("regexp")) + "," JSON_S("regexp") + "," JSON_S("number")) "," JSON_S("string") "," JSON_S("number") "," JSON_S("number") diff --git a/src/profile-generator.h b/src/profile-generator.h index 3b67204712..6f63f6a122 100644 --- a/src/profile-generator.h +++ b/src/profile-generator.h @@ -67,6 +67,7 @@ class StringsStorage { ~StringsStorage(); const char* GetName(String* name); + const char* GetName(int index); inline const char* GetFunctionName(String* name); inline const char* GetFunctionName(const char* name); @@ -78,6 +79,8 @@ class StringsStorage { // Mapping of strings by String::Hash to const char* strings. HashMap names_; + // Mapping from ints to char* strings. + List index_names_; DISALLOW_COPY_AND_ASSIGN(StringsStorage); }; @@ -284,6 +287,9 @@ class CpuProfilesCollection { const char* GetName(String* name) { return function_and_resource_names_.GetName(name); } + const char* GetName(int args_count) { + return function_and_resource_names_.GetName(args_count); + } CpuProfile* GetProfile(int security_token_id, unsigned uid); bool IsLastProfile(const char* title); @@ -302,7 +308,6 @@ class CpuProfilesCollection { static const int kMaxSimultaneousProfiles = 100; private: - const char* GetName(int args_count); const char* GetFunctionName(String* name) { return function_and_resource_names_.GetFunctionName(name); } @@ -317,8 +322,6 @@ class CpuProfilesCollection { } StringsStorage function_and_resource_names_; - // Mapping from args_count (int) to char* strings. - List args_count_names_; List code_entries_; List* > profiles_by_token_; // Mapping from profiles' uids to indexes in the second nested list @@ -503,7 +506,8 @@ class HeapEntry BASE_EMBEDDED { kObject = v8::HeapGraphNode::kObject, kCode = v8::HeapGraphNode::kCode, kClosure = v8::HeapGraphNode::kClosure, - kRegExp = v8::HeapGraphNode::kRegExp + kRegExp = v8::HeapGraphNode::kRegExp, + kHeapNumber = v8::HeapGraphNode::kHeapNumber }; HeapEntry() { } @@ -825,6 +829,7 @@ class HeapSnapshotsCollection { HeapSnapshot* GetSnapshot(unsigned uid); const char* GetName(String* name) { return names_.GetName(name); } + const char* GetName(int index) { return names_.GetName(index); } const char* GetFunctionName(String* name) { return names_.GetFunctionName(name); } @@ -949,6 +954,7 @@ class HeapSnapshotGenerator { void ExtractClosureReferences(JSObject* js_obj, HeapEntry* entry); void ExtractPropertyReferences(JSObject* js_obj, HeapEntry* entry); void ExtractElementReferences(JSObject* js_obj, HeapEntry* entry); + void ExtractInternalReferences(JSObject* js_obj, HeapEntry* entry); void SetClosureReference(HeapObject* parent_obj, HeapEntry* parent, String* reference_name, @@ -961,6 +967,10 @@ class HeapSnapshotGenerator { HeapEntry* parent, const char* reference_name, Object* child); + void SetInternalReference(HeapObject* parent_obj, + HeapEntry* parent, + int index, + Object* child); void SetPropertyReference(HeapObject* parent_obj, HeapEntry* parent, String* reference_name, diff --git a/test/cctest/test-heap-profiler.cc b/test/cctest/test-heap-profiler.cc index 59b0b5bcb5..b86a336160 100644 --- a/test/cctest/test-heap-profiler.cc +++ b/test/cctest/test-heap-profiler.cc @@ -20,11 +20,6 @@ using i::JSObjectsClusterTree; using i::RetainerHeapProfile; -static void CompileAndRunScript(const char *src) { - v8::Script::Compile(v8::String::New(src))->Run(); -} - - namespace { class ConstructorHeapProfileTestHelper : public i::ConstructorHeapProfile { @@ -58,7 +53,7 @@ TEST(ConstructorProfile) { v8::HandleScope scope; LocalContext env; - CompileAndRunScript( + CompileRun( "function F() {} // A constructor\n" "var f1 = new F();\n" "var f2 = new F();\n"); @@ -359,7 +354,7 @@ TEST(RetainerProfile) { v8::HandleScope scope; LocalContext env; - CompileAndRunScript( + CompileRun( "function A() {}\n" "function B(x) { this.x = x; }\n" "function C(x) { this.x1 = x; this.x2 = x; }\n" @@ -473,7 +468,7 @@ TEST(HeapSnapshot) { LocalContext env1; env1->SetSecurityToken(token1); - CompileAndRunScript( + CompileRun( "function A1() {}\n" "function B1(x) { this.x = x; }\n" "function C1(x) { this.x1 = x; this.x2 = x; }\n" @@ -485,7 +480,7 @@ TEST(HeapSnapshot) { LocalContext env2; env2->SetSecurityToken(token2); - CompileAndRunScript( + CompileRun( "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" @@ -583,7 +578,7 @@ TEST(HeapSnapshotObjectSizes) { // -a-> X1 --a // x -b-> X2 <-| - CompileAndRunScript( + CompileRun( "function X(a, b) { this.a = a; this.b = b; }\n" "x = new X(new X(), new X());\n" "x.a.a = x.b;"); @@ -624,7 +619,7 @@ TEST(HeapSnapshotEntryChildren) { v8::HandleScope scope; LocalContext env; - CompileAndRunScript( + CompileRun( "function A() { }\n" "a = new A;"); const v8::HeapSnapshot* snapshot = @@ -648,7 +643,7 @@ TEST(HeapSnapshotCodeObjects) { v8::HandleScope scope; LocalContext env; - CompileAndRunScript( + CompileRun( "function lazy(x) { return x - 1; }\n" "function compiled(x) { return x + 1; }\n" "var anonymous = (function() { return function() { return 0; } })();\n" @@ -709,6 +704,44 @@ TEST(HeapSnapshotCodeObjects) { } +TEST(HeapSnapshotHeapNumbers) { + v8::HandleScope scope; + LocalContext env; + CompileRun( + "a = 1; // a is Smi\n" + "b = 2.5; // b is HeapNumber"); + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8::String::New("numbers")); + const v8::HeapGraphNode* global = GetGlobalObject(snapshot); + CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kProperty, "a")); + const v8::HeapGraphNode* b = + GetProperty(global, v8::HeapGraphEdge::kProperty, "b"); + CHECK_NE(NULL, b); + CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType()); +} + + +TEST(HeapSnapshotInternalReferences) { + v8::HandleScope scope; + v8::Local global_template = v8::ObjectTemplate::New(); + global_template->SetInternalFieldCount(2); + LocalContext env(NULL, global_template); + v8::Handle global_proxy = env->Global(); + v8::Handle global = global_proxy->GetPrototype().As(); + CHECK_EQ(2, global->InternalFieldCount()); + v8::Local obj = v8::Object::New(); + global->SetInternalField(0, v8_num(17)); + global->SetInternalField(1, obj); + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8::String::New("internals")); + const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot); + // The first reference will not present, because it's a Smi. + CHECK_EQ(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0")); + // The second reference is to an object. + CHECK_NE(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "1")); +} + + // Trying to introduce a check helper for uint64_t causes many // overloading ambiguities, so it seems easier just to cast // them to a signed type. @@ -721,7 +754,7 @@ TEST(HeapEntryIdsAndGC) { v8::HandleScope scope; LocalContext env; - CompileAndRunScript( + CompileRun( "function A() {}\n" "function B(x) { this.x = x; }\n" "var a = new A();\n" @@ -777,7 +810,7 @@ TEST(HeapSnapshotsDiff) { v8::HandleScope scope; LocalContext env; - CompileAndRunScript( + CompileRun( "function A() {}\n" "function B(x) { this.x = x; }\n" "function A2(a) { for (var i = 0; i < a; ++i) this[i] = i; }\n" @@ -786,7 +819,7 @@ TEST(HeapSnapshotsDiff) { const v8::HeapSnapshot* snapshot1 = v8::HeapProfiler::TakeSnapshot(v8::String::New("s1")); - CompileAndRunScript( + CompileRun( "delete a;\n" "b.x = null;\n" "var a = new A2(20);\n" @@ -921,7 +954,7 @@ TEST(AggregatedHeapSnapshot) { v8::HandleScope scope; LocalContext env; - CompileAndRunScript( + CompileRun( "function A() {}\n" "function B(x) { this.x = x; }\n" "var a = new A();\n" @@ -1042,7 +1075,7 @@ TEST(HeapSnapshotJSONSerialization) { #define STRING_LITERAL_FOR_TEST \ "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\"" - CompileAndRunScript( + CompileRun( "function A(s) { this.s = s; }\n" "function B(x) { this.x = x; }\n" "var a = new A(" STRING_LITERAL_FOR_TEST ");\n"