New Heap profiler: add dumping HeapNumbers and InternalFields to snapshot.
authormikhail.naganov@gmail.com <mikhail.naganov@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 18 Oct 2010 09:15:38 +0000 (09:15 +0000)
committermikhail.naganov@gmail.com <mikhail.naganov@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 18 Oct 2010 09:15:38 +0000 (09:15 +0000)
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
src/profile-generator.cc
src/profile-generator.h
test/cctest/test-heap-profiler.cc

index b59d155..fb492d9 100644 (file)
@@ -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). */
index fe3bc66..977c67c 100644 (file)
@@ -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<const char*>(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<char>(kMaximumNameLength);
+    OS::SNPrintF(Vector<char>(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<char>(kMaximumNameLength);
-    OS::SNPrintF(Vector<char>(name, kMaximumNameLength), "%d", args_count);
-    args_count_names_[args_count] = name;
-  }
-  return args_count_names_[args_count];
-}
-
-
 void CpuProfilesCollection::AddPathToCurrentProfiles(
     const Vector<CodeEntry*>& 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")
index 3b67204..6f63f6a 100644 (file)
@@ -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<char*> 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<char*> args_count_names_;
   List<CodeEntry*> code_entries_;
   List<List<CpuProfile*>* > 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,
index 59b0b5b..b86a336 100644 (file)
@@ -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<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
+  global_template->SetInternalFieldCount(2);
+  LocalContext env(NULL, global_template);
+  v8::Handle<v8::Object> global_proxy = env->Global();
+  v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
+  CHECK_EQ(2, global->InternalFieldCount());
+  v8::Local<v8::Object> 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"