| OLD | NEW |
| 1 // Copyright 2011 the V8 project authors. All rights reserved. | 1 // Copyright 2011 the V8 project authors. All rights reserved. |
| 2 // | 2 // |
| 3 // Tests for heap profiler | 3 // Tests for heap profiler |
| 4 | 4 |
| 5 #ifdef ENABLE_LOGGING_AND_PROFILING | 5 #ifdef ENABLE_LOGGING_AND_PROFILING |
| 6 | 6 |
| 7 #include "v8.h" | 7 #include "v8.h" |
| 8 | 8 |
| 9 #include "cctest.h" | 9 #include "cctest.h" |
| 10 #include "heap-profiler.h" | 10 #include "heap-profiler.h" |
| 11 #include "snapshot.h" | 11 #include "snapshot.h" |
| 12 #include "string-stream.h" | |
| 13 #include "utils-inl.h" | 12 #include "utils-inl.h" |
| 14 #include "zone-inl.h" | |
| 15 #include "../include/v8-profiler.h" | 13 #include "../include/v8-profiler.h" |
| 16 | 14 |
| 17 namespace i = v8::internal; | 15 namespace i = v8::internal; |
| 18 using i::ClustersCoarser; | |
| 19 using i::JSObjectsCluster; | |
| 20 using i::JSObjectsRetainerTree; | |
| 21 using i::JSObjectsClusterTree; | |
| 22 using i::RetainerHeapProfile; | |
| 23 | |
| 24 | 16 |
| 25 namespace { | 17 namespace { |
| 26 | 18 |
| 27 class ConstructorHeapProfileTestHelper : public i::ConstructorHeapProfile { | |
| 28 public: | |
| 29 ConstructorHeapProfileTestHelper() | |
| 30 : i::ConstructorHeapProfile(), | |
| 31 f_name_(FACTORY->NewStringFromAscii(i::CStrVector("F"))), | |
| 32 f_count_(0) { | |
| 33 } | |
| 34 | |
| 35 void Call(const JSObjectsCluster& cluster, | |
| 36 const i::NumberAndSizeInfo& number_and_size) { | |
| 37 if (f_name_->Equals(cluster.constructor())) { | |
| 38 CHECK_EQ(f_count_, 0); | |
| 39 f_count_ = number_and_size.number(); | |
| 40 CHECK_GT(f_count_, 0); | |
| 41 } | |
| 42 } | |
| 43 | |
| 44 int f_count() { return f_count_; } | |
| 45 | |
| 46 private: | |
| 47 i::Handle<i::String> f_name_; | |
| 48 int f_count_; | |
| 49 }; | |
| 50 | |
| 51 } // namespace | |
| 52 | |
| 53 | |
| 54 TEST(ConstructorProfile) { | |
| 55 v8::HandleScope scope; | |
| 56 LocalContext env; | |
| 57 | |
| 58 CompileRun( | |
| 59 "function F() {} // A constructor\n" | |
| 60 "var f1 = new F();\n" | |
| 61 "var f2 = new F();\n"); | |
| 62 | |
| 63 ConstructorHeapProfileTestHelper cons_profile; | |
| 64 i::AssertNoAllocation no_alloc; | |
| 65 i::HeapIterator iterator; | |
| 66 for (i::HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) | |
| 67 cons_profile.CollectStats(obj); | |
| 68 CHECK_EQ(0, cons_profile.f_count()); | |
| 69 cons_profile.PrintStats(); | |
| 70 CHECK_EQ(2, cons_profile.f_count()); | |
| 71 } | |
| 72 | |
| 73 | |
| 74 static JSObjectsCluster AddHeapObjectToTree(JSObjectsRetainerTree* tree, | |
| 75 i::String* constructor, | |
| 76 int instance, | |
| 77 JSObjectsCluster* ref1 = NULL, | |
| 78 JSObjectsCluster* ref2 = NULL, | |
| 79 JSObjectsCluster* ref3 = NULL) { | |
| 80 JSObjectsCluster o(constructor, reinterpret_cast<i::Object*>(instance)); | |
| 81 JSObjectsClusterTree* o_tree = new JSObjectsClusterTree(); | |
| 82 JSObjectsClusterTree::Locator o_loc; | |
| 83 if (ref1 != NULL) o_tree->Insert(*ref1, &o_loc); | |
| 84 if (ref2 != NULL) o_tree->Insert(*ref2, &o_loc); | |
| 85 if (ref3 != NULL) o_tree->Insert(*ref3, &o_loc); | |
| 86 JSObjectsRetainerTree::Locator loc; | |
| 87 tree->Insert(o, &loc); | |
| 88 loc.set_value(o_tree); | |
| 89 return o; | |
| 90 } | |
| 91 | |
| 92 | |
| 93 static void AddSelfReferenceToTree(JSObjectsRetainerTree* tree, | |
| 94 JSObjectsCluster* self_ref) { | |
| 95 JSObjectsRetainerTree::Locator loc; | |
| 96 CHECK(tree->Find(*self_ref, &loc)); | |
| 97 JSObjectsClusterTree::Locator o_loc; | |
| 98 CHECK_NE(NULL, loc.value()); | |
| 99 loc.value()->Insert(*self_ref, &o_loc); | |
| 100 } | |
| 101 | |
| 102 | |
| 103 static inline void CheckEqualsHelper(const char* file, int line, | |
| 104 const char* expected_source, | |
| 105 const JSObjectsCluster& expected, | |
| 106 const char* value_source, | |
| 107 const JSObjectsCluster& value) { | |
| 108 if (JSObjectsCluster::Compare(expected, value) != 0) { | |
| 109 i::HeapStringAllocator allocator; | |
| 110 i::StringStream stream(&allocator); | |
| 111 stream.Add("# Expected: "); | |
| 112 expected.DebugPrint(&stream); | |
| 113 stream.Add("\n# Found: "); | |
| 114 value.DebugPrint(&stream); | |
| 115 V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n%s", | |
| 116 expected_source, value_source, | |
| 117 *stream.ToCString()); | |
| 118 } | |
| 119 } | |
| 120 | |
| 121 | |
| 122 static inline void CheckNonEqualsHelper(const char* file, int line, | |
| 123 const char* expected_source, | |
| 124 const JSObjectsCluster& expected, | |
| 125 const char* value_source, | |
| 126 const JSObjectsCluster& value) { | |
| 127 if (JSObjectsCluster::Compare(expected, value) == 0) { | |
| 128 i::HeapStringAllocator allocator; | |
| 129 i::StringStream stream(&allocator); | |
| 130 stream.Add("# !Expected: "); | |
| 131 expected.DebugPrint(&stream); | |
| 132 stream.Add("\n# Found: "); | |
| 133 value.DebugPrint(&stream); | |
| 134 V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n%s", | |
| 135 expected_source, value_source, | |
| 136 *stream.ToCString()); | |
| 137 } | |
| 138 } | |
| 139 | |
| 140 | |
| 141 TEST(ClustersCoarserSimple) { | |
| 142 v8::HandleScope scope; | |
| 143 LocalContext env; | |
| 144 | |
| 145 i::ZoneScope zn_scope(i::Isolate::Current(), i::DELETE_ON_EXIT); | |
| 146 | |
| 147 JSObjectsRetainerTree tree; | |
| 148 JSObjectsCluster function(HEAP->function_class_symbol()); | |
| 149 JSObjectsCluster a(*FACTORY->NewStringFromAscii(i::CStrVector("A"))); | |
| 150 JSObjectsCluster b(*FACTORY->NewStringFromAscii(i::CStrVector("B"))); | |
| 151 | |
| 152 // o1 <- Function | |
| 153 JSObjectsCluster o1 = | |
| 154 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x100, &function); | |
| 155 // o2 <- Function | |
| 156 JSObjectsCluster o2 = | |
| 157 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x200, &function); | |
| 158 // o3 <- A, B | |
| 159 JSObjectsCluster o3 = | |
| 160 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x300, &a, &b); | |
| 161 // o4 <- B, A | |
| 162 JSObjectsCluster o4 = | |
| 163 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x400, &b, &a); | |
| 164 // o5 <- A, B, Function | |
| 165 JSObjectsCluster o5 = | |
| 166 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x500, | |
| 167 &a, &b, &function); | |
| 168 | |
| 169 ClustersCoarser coarser; | |
| 170 coarser.Process(&tree); | |
| 171 | |
| 172 CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2)); | |
| 173 CHECK_EQ(coarser.GetCoarseEquivalent(o3), coarser.GetCoarseEquivalent(o4)); | |
| 174 CHECK_NE(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o3)); | |
| 175 CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o5)); | |
| 176 } | |
| 177 | |
| 178 | |
| 179 TEST(ClustersCoarserMultipleConstructors) { | |
| 180 v8::HandleScope scope; | |
| 181 LocalContext env; | |
| 182 | |
| 183 i::ZoneScope zn_scope(i::Isolate::Current(), i::DELETE_ON_EXIT); | |
| 184 | |
| 185 JSObjectsRetainerTree tree; | |
| 186 JSObjectsCluster function(HEAP->function_class_symbol()); | |
| 187 | |
| 188 // o1 <- Function | |
| 189 JSObjectsCluster o1 = | |
| 190 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x100, &function); | |
| 191 // a1 <- Function | |
| 192 JSObjectsCluster a1 = | |
| 193 AddHeapObjectToTree(&tree, HEAP->Array_symbol(), 0x1000, &function); | |
| 194 // o2 <- Function | |
| 195 JSObjectsCluster o2 = | |
| 196 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x200, &function); | |
| 197 // a2 <- Function | |
| 198 JSObjectsCluster a2 = | |
| 199 AddHeapObjectToTree(&tree, HEAP->Array_symbol(), 0x2000, &function); | |
| 200 | |
| 201 ClustersCoarser coarser; | |
| 202 coarser.Process(&tree); | |
| 203 | |
| 204 CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2)); | |
| 205 CHECK_EQ(coarser.GetCoarseEquivalent(a1), coarser.GetCoarseEquivalent(a2)); | |
| 206 } | |
| 207 | |
| 208 | |
| 209 TEST(ClustersCoarserPathsTraversal) { | |
| 210 v8::HandleScope scope; | |
| 211 LocalContext env; | |
| 212 | |
| 213 i::ZoneScope zn_scope(i::Isolate::Current(), i::DELETE_ON_EXIT); | |
| 214 | |
| 215 JSObjectsRetainerTree tree; | |
| 216 | |
| 217 // On the following graph: | |
| 218 // | |
| 219 // p | |
| 220 // <- o21 <- o11 <- | |
| 221 // q o | |
| 222 // <- o22 <- o12 <- | |
| 223 // r | |
| 224 // | |
| 225 // we expect that coarser will deduce equivalences: p ~ q ~ r, | |
| 226 // o21 ~ o22, and o11 ~ o12. | |
| 227 | |
| 228 JSObjectsCluster o = | |
| 229 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x100); | |
| 230 JSObjectsCluster o11 = | |
| 231 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x110, &o); | |
| 232 JSObjectsCluster o12 = | |
| 233 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x120, &o); | |
| 234 JSObjectsCluster o21 = | |
| 235 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x210, &o11); | |
| 236 JSObjectsCluster o22 = | |
| 237 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x220, &o12); | |
| 238 JSObjectsCluster p = | |
| 239 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x300, &o21); | |
| 240 JSObjectsCluster q = | |
| 241 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x310, &o21, &o22); | |
| 242 JSObjectsCluster r = | |
| 243 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x320, &o22); | |
| 244 | |
| 245 ClustersCoarser coarser; | |
| 246 coarser.Process(&tree); | |
| 247 | |
| 248 CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o)); | |
| 249 CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(o11)); | |
| 250 CHECK_EQ(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(o12)); | |
| 251 CHECK_EQ(coarser.GetCoarseEquivalent(o21), coarser.GetCoarseEquivalent(o22)); | |
| 252 CHECK_NE(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(o21)); | |
| 253 CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(p)); | |
| 254 CHECK_EQ(coarser.GetCoarseEquivalent(p), coarser.GetCoarseEquivalent(q)); | |
| 255 CHECK_EQ(coarser.GetCoarseEquivalent(q), coarser.GetCoarseEquivalent(r)); | |
| 256 CHECK_NE(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(p)); | |
| 257 CHECK_NE(coarser.GetCoarseEquivalent(o21), coarser.GetCoarseEquivalent(p)); | |
| 258 } | |
| 259 | |
| 260 | |
| 261 TEST(ClustersCoarserSelf) { | |
| 262 v8::HandleScope scope; | |
| 263 LocalContext env; | |
| 264 | |
| 265 i::ZoneScope zn_scope(i::Isolate::Current(), i::DELETE_ON_EXIT); | |
| 266 | |
| 267 JSObjectsRetainerTree tree; | |
| 268 | |
| 269 // On the following graph: | |
| 270 // | |
| 271 // p (self-referencing) | |
| 272 // <- o1 <- | |
| 273 // q (self-referencing) o | |
| 274 // <- o2 <- | |
| 275 // r (self-referencing) | |
| 276 // | |
| 277 // we expect that coarser will deduce equivalences: p ~ q ~ r, o1 ~ o2; | |
| 278 | |
| 279 JSObjectsCluster o = | |
| 280 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x100); | |
| 281 JSObjectsCluster o1 = | |
| 282 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x110, &o); | |
| 283 JSObjectsCluster o2 = | |
| 284 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x120, &o); | |
| 285 JSObjectsCluster p = | |
| 286 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x300, &o1); | |
| 287 AddSelfReferenceToTree(&tree, &p); | |
| 288 JSObjectsCluster q = | |
| 289 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x310, &o1, &o2); | |
| 290 AddSelfReferenceToTree(&tree, &q); | |
| 291 JSObjectsCluster r = | |
| 292 AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x320, &o2); | |
| 293 AddSelfReferenceToTree(&tree, &r); | |
| 294 | |
| 295 ClustersCoarser coarser; | |
| 296 coarser.Process(&tree); | |
| 297 | |
| 298 CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o)); | |
| 299 CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(o1)); | |
| 300 CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2)); | |
| 301 CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(p)); | |
| 302 CHECK_EQ(coarser.GetCoarseEquivalent(p), coarser.GetCoarseEquivalent(q)); | |
| 303 CHECK_EQ(coarser.GetCoarseEquivalent(q), coarser.GetCoarseEquivalent(r)); | |
| 304 CHECK_NE(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(p)); | |
| 305 } | |
| 306 | |
| 307 | |
| 308 namespace { | |
| 309 | |
| 310 class RetainerProfilePrinter : public RetainerHeapProfile::Printer { | |
| 311 public: | |
| 312 RetainerProfilePrinter() : stream_(&allocator_), lines_(100) {} | |
| 313 | |
| 314 void PrintRetainers(const JSObjectsCluster& cluster, | |
| 315 const i::StringStream& retainers) { | |
| 316 cluster.Print(&stream_); | |
| 317 stream_.Add("%s", *(retainers.ToCString())); | |
| 318 stream_.Put('\0'); | |
| 319 } | |
| 320 | |
| 321 const char* GetRetainers(const char* constructor) { | |
| 322 FillLines(); | |
| 323 const size_t cons_len = strlen(constructor); | |
| 324 for (int i = 0; i < lines_.length(); ++i) { | |
| 325 if (strncmp(constructor, lines_[i], cons_len) == 0 && | |
| 326 lines_[i][cons_len] == ',') { | |
| 327 return lines_[i] + cons_len + 1; | |
| 328 } | |
| 329 } | |
| 330 return NULL; | |
| 331 } | |
| 332 | |
| 333 private: | |
| 334 void FillLines() { | |
| 335 if (lines_.length() > 0) return; | |
| 336 stream_.Put('\0'); | |
| 337 stream_str_ = stream_.ToCString(); | |
| 338 const char* pos = *stream_str_; | |
| 339 while (pos != NULL && *pos != '\0') { | |
| 340 lines_.Add(pos); | |
| 341 pos = strchr(pos, '\0'); | |
| 342 if (pos != NULL) ++pos; | |
| 343 } | |
| 344 } | |
| 345 | |
| 346 i::HeapStringAllocator allocator_; | |
| 347 i::StringStream stream_; | |
| 348 i::SmartPointer<const char> stream_str_; | |
| 349 i::List<const char*> lines_; | |
| 350 }; | |
| 351 | |
| 352 } // namespace | |
| 353 | |
| 354 | |
| 355 TEST(RetainerProfile) { | |
| 356 v8::HandleScope scope; | |
| 357 LocalContext env; | |
| 358 | |
| 359 CompileRun( | |
| 360 "function A() {}\n" | |
| 361 "function B(x) { this.x = x; }\n" | |
| 362 "function C(x) { this.x1 = x; this.x2 = x; }\n" | |
| 363 "var a = new A();\n" | |
| 364 "var b1 = new B(a), b2 = new B(a);\n" | |
| 365 "var c = new C(a);"); | |
| 366 | |
| 367 RetainerHeapProfile ret_profile; | |
| 368 i::AssertNoAllocation no_alloc; | |
| 369 i::HeapIterator iterator; | |
| 370 for (i::HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) | |
| 371 ret_profile.CollectStats(obj); | |
| 372 ret_profile.CoarseAndAggregate(); | |
| 373 RetainerProfilePrinter printer; | |
| 374 ret_profile.DebugPrintStats(&printer); | |
| 375 const char* retainers_of_a = printer.GetRetainers("A"); | |
| 376 // The order of retainers is unspecified, so we check string length, and | |
| 377 // verify each retainer separately. | |
| 378 CHECK_EQ(i::StrLength("(global property);1,B;2,C;2"), | |
| 379 i::StrLength(retainers_of_a)); | |
| 380 CHECK(strstr(retainers_of_a, "(global property);1") != NULL); | |
| 381 CHECK(strstr(retainers_of_a, "B;2") != NULL); | |
| 382 CHECK(strstr(retainers_of_a, "C;2") != NULL); | |
| 383 CHECK_EQ("(global property);2", printer.GetRetainers("B")); | |
| 384 CHECK_EQ("(global property);1", printer.GetRetainers("C")); | |
| 385 } | |
| 386 | |
| 387 | |
| 388 namespace { | |
| 389 | |
| 390 class NamedEntriesDetector { | 19 class NamedEntriesDetector { |
| 391 public: | 20 public: |
| 392 NamedEntriesDetector() | 21 NamedEntriesDetector() |
| 393 : has_A2(false), has_B2(false), has_C2(false) { | 22 : has_A2(false), has_B2(false), has_C2(false) { |
| 394 } | 23 } |
| 395 | 24 |
| 396 void Apply(i::HeapEntry** entry_ptr) { | 25 void Apply(i::HeapEntry** entry_ptr) { |
| 397 if (IsReachableNodeWithName(*entry_ptr, "A2")) has_A2 = true; | 26 if (IsReachableNodeWithName(*entry_ptr, "A2")) has_A2 = true; |
| 398 if (IsReachableNodeWithName(*entry_ptr, "B2")) has_B2 = true; | 27 if (IsReachableNodeWithName(*entry_ptr, "B2")) has_B2 = true; |
| 399 if (IsReachableNodeWithName(*entry_ptr, "C2")) has_C2 = true; | 28 if (IsReachableNodeWithName(*entry_ptr, "C2")) has_C2 = true; |
| (...skipping 319 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 719 const v8::HeapSnapshot* snapshot = | 348 const v8::HeapSnapshot* snapshot = |
| 720 v8::HeapProfiler::TakeSnapshot(v8::String::New("s")); | 349 v8::HeapProfiler::TakeSnapshot(v8::String::New("s")); |
| 721 const v8::HeapGraphNode* root1 = snapshot->GetRoot(); | 350 const v8::HeapGraphNode* root1 = snapshot->GetRoot(); |
| 722 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>( | 351 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>( |
| 723 snapshot))->GetSortedEntriesList(); | 352 snapshot))->GetSortedEntriesList(); |
| 724 const v8::HeapGraphNode* root2 = snapshot->GetRoot(); | 353 const v8::HeapGraphNode* root2 = snapshot->GetRoot(); |
| 725 CHECK_EQ(root1, root2); | 354 CHECK_EQ(root1, root2); |
| 726 } | 355 } |
| 727 | 356 |
| 728 | 357 |
| 729 static const v8::HeapGraphNode* GetChild( | |
| 730 const v8::HeapGraphNode* node, | |
| 731 v8::HeapGraphNode::Type type, | |
| 732 const char* name, | |
| 733 const v8::HeapGraphNode* after = NULL) { | |
| 734 bool ignore_child = after == NULL ? false : true; | |
| 735 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { | |
| 736 const v8::HeapGraphEdge* prop = node->GetChild(i); | |
| 737 const v8::HeapGraphNode* child = prop->GetToNode(); | |
| 738 v8::String::AsciiValue child_name(child->GetName()); | |
| 739 if (!ignore_child | |
| 740 && child->GetType() == type | |
| 741 && strcmp(name, *child_name) == 0) | |
| 742 return child; | |
| 743 if (after != NULL && child == after) ignore_child = false; | |
| 744 } | |
| 745 return NULL; | |
| 746 } | |
| 747 | |
| 748 static bool IsNodeRetainedAs(const v8::HeapGraphNode* node, | |
| 749 int element) { | |
| 750 for (int i = 0, count = node->GetRetainersCount(); i < count; ++i) { | |
| 751 const v8::HeapGraphEdge* prop = node->GetRetainer(i); | |
| 752 if (prop->GetType() == v8::HeapGraphEdge::kElement | |
| 753 && element == prop->GetName()->Int32Value()) | |
| 754 return true; | |
| 755 } | |
| 756 return false; | |
| 757 } | |
| 758 | |
| 759 TEST(AggregatedHeapSnapshot) { | |
| 760 v8::HandleScope scope; | |
| 761 LocalContext env; | |
| 762 | |
| 763 CompileRun( | |
| 764 "function A() {}\n" | |
| 765 "function B(x) { this.x = x; }\n" | |
| 766 "var a = new A();\n" | |
| 767 "var b = new B(a);"); | |
| 768 const v8::HeapSnapshot* snapshot = | |
| 769 v8::HeapProfiler::TakeSnapshot( | |
| 770 v8::String::New("agg"), v8::HeapSnapshot::kAggregated); | |
| 771 const v8::HeapGraphNode* strings = GetChild(snapshot->GetRoot(), | |
| 772 v8::HeapGraphNode::kHidden, | |
| 773 "STRING_TYPE"); | |
| 774 CHECK_NE(NULL, strings); | |
| 775 CHECK_NE(0, strings->GetSelfSize()); | |
| 776 CHECK_NE(0, strings->GetInstancesCount()); | |
| 777 const v8::HeapGraphNode* maps = GetChild(snapshot->GetRoot(), | |
| 778 v8::HeapGraphNode::kHidden, | |
| 779 "MAP_TYPE"); | |
| 780 CHECK_NE(NULL, maps); | |
| 781 CHECK_NE(0, maps->GetSelfSize()); | |
| 782 CHECK_NE(0, maps->GetInstancesCount()); | |
| 783 | |
| 784 const v8::HeapGraphNode* a = GetChild(snapshot->GetRoot(), | |
| 785 v8::HeapGraphNode::kObject, | |
| 786 "A"); | |
| 787 CHECK_NE(NULL, a); | |
| 788 CHECK_NE(0, a->GetSelfSize()); | |
| 789 CHECK_EQ(1, a->GetInstancesCount()); | |
| 790 | |
| 791 const v8::HeapGraphNode* b = GetChild(snapshot->GetRoot(), | |
| 792 v8::HeapGraphNode::kObject, | |
| 793 "B"); | |
| 794 CHECK_NE(NULL, b); | |
| 795 CHECK_NE(0, b->GetSelfSize()); | |
| 796 CHECK_EQ(1, b->GetInstancesCount()); | |
| 797 | |
| 798 const v8::HeapGraphNode* glob_prop = GetChild(snapshot->GetRoot(), | |
| 799 v8::HeapGraphNode::kObject, | |
| 800 "(global property)", | |
| 801 b); | |
| 802 CHECK_NE(NULL, glob_prop); | |
| 803 CHECK_EQ(0, glob_prop->GetSelfSize()); | |
| 804 CHECK_EQ(0, glob_prop->GetInstancesCount()); | |
| 805 CHECK_NE(0, glob_prop->GetChildrenCount()); | |
| 806 | |
| 807 const v8::HeapGraphNode* a_from_glob_prop = GetChild( | |
| 808 glob_prop, | |
| 809 v8::HeapGraphNode::kObject, | |
| 810 "A"); | |
| 811 CHECK_NE(NULL, a_from_glob_prop); | |
| 812 CHECK_EQ(0, a_from_glob_prop->GetSelfSize()); | |
| 813 CHECK_EQ(0, a_from_glob_prop->GetInstancesCount()); | |
| 814 CHECK_EQ(0, a_from_glob_prop->GetChildrenCount()); // Retains nothing. | |
| 815 CHECK(IsNodeRetainedAs(a_from_glob_prop, 1)); // (global propery) has 1 ref. | |
| 816 | |
| 817 const v8::HeapGraphNode* b_with_children = GetChild( | |
| 818 snapshot->GetRoot(), | |
| 819 v8::HeapGraphNode::kObject, | |
| 820 "B", | |
| 821 b); | |
| 822 CHECK_NE(NULL, b_with_children); | |
| 823 CHECK_EQ(0, b_with_children->GetSelfSize()); | |
| 824 CHECK_EQ(0, b_with_children->GetInstancesCount()); | |
| 825 CHECK_NE(0, b_with_children->GetChildrenCount()); | |
| 826 | |
| 827 const v8::HeapGraphNode* a_from_b = GetChild( | |
| 828 b_with_children, | |
| 829 v8::HeapGraphNode::kObject, | |
| 830 "A"); | |
| 831 CHECK_NE(NULL, a_from_b); | |
| 832 CHECK_EQ(0, a_from_b->GetSelfSize()); | |
| 833 CHECK_EQ(0, a_from_b->GetInstancesCount()); | |
| 834 CHECK_EQ(0, a_from_b->GetChildrenCount()); // Retains nothing. | |
| 835 CHECK(IsNodeRetainedAs(a_from_b, 1)); // B has 1 ref to A. | |
| 836 } | |
| 837 | |
| 838 | |
| 839 TEST(HeapEntryDominator) { | 358 TEST(HeapEntryDominator) { |
| 840 // The graph looks like this: | 359 // The graph looks like this: |
| 841 // | 360 // |
| 842 // -> node1 | 361 // -> node1 |
| 843 // a |^ | 362 // a |^ |
| 844 // -> node5 ba | 363 // -> node5 ba |
| 845 // a v| | 364 // a v| |
| 846 // node6 -> node2 | 365 // node6 -> node2 |
| 847 // b a |^ | 366 // b a |^ |
| 848 // -> node4 ba | 367 // -> node4 ba |
| (...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1041 LocalContext env; | 560 LocalContext env; |
| 1042 const v8::HeapSnapshot* snapshot = | 561 const v8::HeapSnapshot* snapshot = |
| 1043 v8::HeapProfiler::TakeSnapshot(v8::String::New("abort")); | 562 v8::HeapProfiler::TakeSnapshot(v8::String::New("abort")); |
| 1044 TestJSONStream stream(5); | 563 TestJSONStream stream(5); |
| 1045 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON); | 564 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON); |
| 1046 CHECK_GT(stream.size(), 0); | 565 CHECK_GT(stream.size(), 0); |
| 1047 CHECK_EQ(0, stream.eos_signaled()); | 566 CHECK_EQ(0, stream.eos_signaled()); |
| 1048 } | 567 } |
| 1049 | 568 |
| 1050 | 569 |
| 1051 // Must not crash in debug mode. | |
| 1052 TEST(AggregatedHeapSnapshotJSONSerialization) { | |
| 1053 v8::HandleScope scope; | |
| 1054 LocalContext env; | |
| 1055 | |
| 1056 const v8::HeapSnapshot* snapshot = | |
| 1057 v8::HeapProfiler::TakeSnapshot( | |
| 1058 v8::String::New("agg"), v8::HeapSnapshot::kAggregated); | |
| 1059 TestJSONStream stream; | |
| 1060 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON); | |
| 1061 CHECK_GT(stream.size(), 0); | |
| 1062 CHECK_EQ(1, stream.eos_signaled()); | |
| 1063 } | |
| 1064 | |
| 1065 | |
| 1066 TEST(HeapSnapshotGetNodeById) { | 570 TEST(HeapSnapshotGetNodeById) { |
| 1067 v8::HandleScope scope; | 571 v8::HandleScope scope; |
| 1068 LocalContext env; | 572 LocalContext env; |
| 1069 | 573 |
| 1070 const v8::HeapSnapshot* snapshot = | 574 const v8::HeapSnapshot* snapshot = |
| 1071 v8::HeapProfiler::TakeSnapshot(v8::String::New("id")); | 575 v8::HeapProfiler::TakeSnapshot(v8::String::New("id")); |
| 1072 const v8::HeapGraphNode* root = snapshot->GetRoot(); | 576 const v8::HeapGraphNode* root = snapshot->GetRoot(); |
| 1073 CHECK_EQ(root, snapshot->GetNodeById(root->GetId())); | 577 CHECK_EQ(root, snapshot->GetNodeById(root->GetId())); |
| 1074 for (int i = 0, count = root->GetChildrenCount(); i < count; ++i) { | 578 for (int i = 0, count = root->GetChildrenCount(); i < count; ++i) { |
| 1075 const v8::HeapGraphEdge* prop = root->GetChild(i); | 579 const v8::HeapGraphEdge* prop = root->GetChild(i); |
| (...skipping 308 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1384 const int nodes_count = snapshot->GetNodesCount(); | 888 const int nodes_count = snapshot->GetNodesCount(); |
| 1385 int count = 0; | 889 int count = 0; |
| 1386 for (int i = 0; i < nodes_count; ++i) { | 890 for (int i = 0; i < nodes_count; ++i) { |
| 1387 if (snapshot->GetNode(i) == global) | 891 if (snapshot->GetNode(i) == global) |
| 1388 ++count; | 892 ++count; |
| 1389 } | 893 } |
| 1390 CHECK_EQ(1, count); | 894 CHECK_EQ(1, count); |
| 1391 } | 895 } |
| 1392 | 896 |
| 1393 #endif // ENABLE_LOGGING_AND_PROFILING | 897 #endif // ENABLE_LOGGING_AND_PROFILING |
| OLD | NEW |