OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 939 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
950 index_ = index; | 950 index_ = index; |
951 to_ = to; | 951 to_ = to; |
952 } | 952 } |
953 | 953 |
954 | 954 |
955 void HeapGraphEdge::Init(int child_index, int index, HeapEntry* to) { | 955 void HeapGraphEdge::Init(int child_index, int index, HeapEntry* to) { |
956 Init(child_index, kElement, index, to); | 956 Init(child_index, kElement, index, to); |
957 } | 957 } |
958 | 958 |
959 | 959 |
960 HeapEntry* HeapGraphEdge::From() { | |
961 return reinterpret_cast<HeapEntry*>(this - child_index_) - 1; | |
962 } | |
963 | |
964 | |
965 void HeapEntry::Init(HeapSnapshot* snapshot, | 960 void HeapEntry::Init(HeapSnapshot* snapshot, |
966 Type type, | 961 Type type, |
967 const char* name, | 962 const char* name, |
968 SnapshotObjectId id, | 963 SnapshotObjectId id, |
969 int self_size, | 964 int self_size, |
970 int children_count, | 965 int children_count, |
971 int retainers_count) { | 966 int retainers_count) { |
972 snapshot_ = snapshot; | 967 snapshot_ = snapshot; |
973 type_ = type; | 968 type_ = type; |
974 painted_ = false; | 969 painted_ = false; |
| 970 reachable_from_window_ = false; |
975 name_ = name; | 971 name_ = name; |
976 self_size_ = self_size; | 972 self_size_ = self_size; |
977 retained_size_ = 0; | 973 retained_size_ = 0; |
978 entry_index_ = -1; | 974 entry_index_ = -1; |
979 children_count_ = children_count; | 975 children_count_ = children_count; |
980 retainers_count_ = retainers_count; | 976 retainers_count_ = retainers_count; |
981 dominator_ = NULL; | 977 dominator_ = NULL; |
982 id_ = id; | 978 id_ = id; |
983 } | 979 } |
984 | 980 |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1127 const char* title, | 1123 const char* title, |
1128 unsigned uid) | 1124 unsigned uid) |
1129 : collection_(collection), | 1125 : collection_(collection), |
1130 type_(type), | 1126 type_(type), |
1131 title_(title), | 1127 title_(title), |
1132 uid_(uid), | 1128 uid_(uid), |
1133 root_entry_(NULL), | 1129 root_entry_(NULL), |
1134 gc_roots_entry_(NULL), | 1130 gc_roots_entry_(NULL), |
1135 natives_root_entry_(NULL), | 1131 natives_root_entry_(NULL), |
1136 raw_entries_(NULL), | 1132 raw_entries_(NULL), |
| 1133 number_of_edges_(0), |
1137 max_snapshot_js_object_id_(0) { | 1134 max_snapshot_js_object_id_(0) { |
1138 STATIC_CHECK( | 1135 STATIC_CHECK( |
1139 sizeof(HeapGraphEdge) == | 1136 sizeof(HeapGraphEdge) == |
1140 SnapshotSizeConstants<kPointerSize>::kExpectedHeapGraphEdgeSize); | 1137 SnapshotSizeConstants<kPointerSize>::kExpectedHeapGraphEdgeSize); |
1141 STATIC_CHECK( | 1138 STATIC_CHECK( |
1142 sizeof(HeapEntry) == | 1139 sizeof(HeapEntry) == |
1143 SnapshotSizeConstants<kPointerSize>::kExpectedHeapEntrySize); | 1140 SnapshotSizeConstants<kPointerSize>::kExpectedHeapEntrySize); |
1144 for (int i = 0; i < VisitorSynchronization::kNumberOfSyncTags; ++i) { | 1141 for (int i = 0; i < VisitorSynchronization::kNumberOfSyncTags; ++i) { |
1145 gc_subroot_entries_[i] = NULL; | 1142 gc_subroot_entries_[i] = NULL; |
1146 } | 1143 } |
(...skipping 13 matching lines...) Expand all Loading... |
1160 | 1157 |
1161 void HeapSnapshot::RememberLastJSObjectId() { | 1158 void HeapSnapshot::RememberLastJSObjectId() { |
1162 max_snapshot_js_object_id_ = collection_->last_assigned_id(); | 1159 max_snapshot_js_object_id_ = collection_->last_assigned_id(); |
1163 } | 1160 } |
1164 | 1161 |
1165 | 1162 |
1166 void HeapSnapshot::AllocateEntries(int entries_count, | 1163 void HeapSnapshot::AllocateEntries(int entries_count, |
1167 int children_count, | 1164 int children_count, |
1168 int retainers_count) { | 1165 int retainers_count) { |
1169 ASSERT(raw_entries_ == NULL); | 1166 ASSERT(raw_entries_ == NULL); |
| 1167 number_of_edges_ = children_count; |
1170 raw_entries_size_ = | 1168 raw_entries_size_ = |
1171 HeapEntry::EntriesSize(entries_count, children_count, retainers_count); | 1169 HeapEntry::EntriesSize(entries_count, children_count, retainers_count); |
1172 raw_entries_ = NewArray<char>(raw_entries_size_); | 1170 raw_entries_ = NewArray<char>(raw_entries_size_); |
1173 } | 1171 } |
1174 | 1172 |
1175 | 1173 |
1176 static void HeapEntryClearPaint(HeapEntry** entry_ptr) { | 1174 static void HeapEntryClearPaint(HeapEntry** entry_ptr) { |
1177 (*entry_ptr)->clear_paint(); | 1175 (*entry_ptr)->clear_paint(); |
1178 } | 1176 } |
1179 | 1177 |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1305 const SnapshotObjectId HeapObjectsMap::kInternalRootObjectId = 1; | 1303 const SnapshotObjectId HeapObjectsMap::kInternalRootObjectId = 1; |
1306 const SnapshotObjectId HeapObjectsMap::kGcRootsObjectId = | 1304 const SnapshotObjectId HeapObjectsMap::kGcRootsObjectId = |
1307 HeapObjectsMap::kInternalRootObjectId + HeapObjectsMap::kObjectIdStep; | 1305 HeapObjectsMap::kInternalRootObjectId + HeapObjectsMap::kObjectIdStep; |
1308 const SnapshotObjectId HeapObjectsMap::kGcRootsFirstSubrootId = | 1306 const SnapshotObjectId HeapObjectsMap::kGcRootsFirstSubrootId = |
1309 HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep; | 1307 HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep; |
1310 const SnapshotObjectId HeapObjectsMap::kFirstAvailableObjectId = | 1308 const SnapshotObjectId HeapObjectsMap::kFirstAvailableObjectId = |
1311 HeapObjectsMap::kGcRootsFirstSubrootId + | 1309 HeapObjectsMap::kGcRootsFirstSubrootId + |
1312 VisitorSynchronization::kNumberOfSyncTags * HeapObjectsMap::kObjectIdStep; | 1310 VisitorSynchronization::kNumberOfSyncTags * HeapObjectsMap::kObjectIdStep; |
1313 | 1311 |
1314 HeapObjectsMap::HeapObjectsMap() | 1312 HeapObjectsMap::HeapObjectsMap() |
1315 : initial_fill_mode_(true), | 1313 : next_id_(kFirstAvailableObjectId), |
1316 next_id_(kFirstAvailableObjectId), | 1314 entries_map_(AddressesMatch) { |
1317 entries_map_(AddressesMatch), | |
1318 entries_(new List<EntryInfo>()) { | |
1319 // This dummy element solves a problem with entries_map_. | 1315 // This dummy element solves a problem with entries_map_. |
1320 // When we do lookup in HashMap we see no difference between two cases: | 1316 // When we do lookup in HashMap we see no difference between two cases: |
1321 // it has an entry with NULL as the value or it has created | 1317 // it has an entry with NULL as the value or it has created |
1322 // a new entry on the fly with NULL as the default value. | 1318 // a new entry on the fly with NULL as the default value. |
1323 // With such dummy element we have a guaranty that all entries_map_ entries | 1319 // With such dummy element we have a guaranty that all entries_map_ entries |
1324 // will have the value field grater than 0. | 1320 // will have the value field grater than 0. |
1325 // This fact is using in MoveObject method. | 1321 // This fact is using in MoveObject method. |
1326 entries_->Add(EntryInfo(0, NULL)); | 1322 entries_.Add(EntryInfo(0, NULL, 0)); |
1327 } | |
1328 | |
1329 | |
1330 HeapObjectsMap::~HeapObjectsMap() { | |
1331 delete entries_; | |
1332 } | 1323 } |
1333 | 1324 |
1334 | 1325 |
1335 void HeapObjectsMap::SnapshotGenerationFinished() { | 1326 void HeapObjectsMap::SnapshotGenerationFinished() { |
1336 initial_fill_mode_ = false; | |
1337 RemoveDeadEntries(); | 1327 RemoveDeadEntries(); |
1338 } | 1328 } |
1339 | 1329 |
1340 | 1330 |
1341 SnapshotObjectId HeapObjectsMap::FindObject(Address addr) { | |
1342 if (!initial_fill_mode_) { | |
1343 SnapshotObjectId existing = FindEntry(addr); | |
1344 if (existing != 0) return existing; | |
1345 } | |
1346 SnapshotObjectId id = next_id_; | |
1347 next_id_ += kObjectIdStep; | |
1348 AddEntry(addr, id); | |
1349 // Here and in other places the length of entries_ list has to be | |
1350 // the same or greater than the length of entries_map_. But entries_ list | |
1351 // has a dummy element at index 0. | |
1352 ASSERT(static_cast<uint32_t>(entries_->length()) > entries_map_.occupancy()); | |
1353 return id; | |
1354 } | |
1355 | |
1356 | |
1357 void HeapObjectsMap::MoveObject(Address from, Address to) { | 1331 void HeapObjectsMap::MoveObject(Address from, Address to) { |
1358 ASSERT(to != NULL); | 1332 ASSERT(to != NULL); |
1359 ASSERT(from != NULL); | 1333 ASSERT(from != NULL); |
1360 if (from == to) return; | 1334 if (from == to) return; |
1361 void* from_value = entries_map_.Remove(from, AddressHash(from)); | 1335 void* from_value = entries_map_.Remove(from, AddressHash(from)); |
1362 if (from_value == NULL) return; | 1336 if (from_value == NULL) return; |
1363 int from_entry_info_index = | 1337 int from_entry_info_index = |
1364 static_cast<int>(reinterpret_cast<intptr_t>(from_value)); | 1338 static_cast<int>(reinterpret_cast<intptr_t>(from_value)); |
1365 entries_->at(from_entry_info_index).addr = to; | 1339 entries_.at(from_entry_info_index).addr = to; |
1366 HashMap::Entry* to_entry = entries_map_.Lookup(to, AddressHash(to), true); | 1340 HashMap::Entry* to_entry = entries_map_.Lookup(to, AddressHash(to), true); |
1367 if (to_entry->value != NULL) { | 1341 if (to_entry->value != NULL) { |
1368 int to_entry_info_index = | 1342 int to_entry_info_index = |
1369 static_cast<int>(reinterpret_cast<intptr_t>(to_entry->value)); | 1343 static_cast<int>(reinterpret_cast<intptr_t>(to_entry->value)); |
1370 // Without this operation we will have two EntryInfo's with the same | 1344 // Without this operation we will have two EntryInfo's with the same |
1371 // value in addr field. It is bad because later at RemoveDeadEntries | 1345 // value in addr field. It is bad because later at RemoveDeadEntries |
1372 // one of this entry will be removed with the corresponding entries_map_ | 1346 // one of this entry will be removed with the corresponding entries_map_ |
1373 // entry. | 1347 // entry. |
1374 entries_->at(to_entry_info_index).addr = NULL; | 1348 entries_.at(to_entry_info_index).addr = NULL; |
1375 } | 1349 } |
1376 to_entry->value = reinterpret_cast<void*>(from_entry_info_index); | 1350 to_entry->value = reinterpret_cast<void*>(from_entry_info_index); |
1377 } | 1351 } |
1378 | 1352 |
1379 | 1353 |
1380 void HeapObjectsMap::AddEntry(Address addr, SnapshotObjectId id) { | 1354 SnapshotObjectId HeapObjectsMap::FindEntry(Address addr) { |
1381 HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), true); | 1355 HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), false); |
1382 ASSERT(entry->value == NULL); | 1356 if (entry == NULL) return 0; |
1383 ASSERT(entries_->length() > 0 && | 1357 int entry_index = static_cast<int>(reinterpret_cast<intptr_t>(entry->value)); |
1384 entries_->at(0).id == 0 && | 1358 EntryInfo& entry_info = entries_.at(entry_index); |
1385 entries_->at(0).addr == NULL); | 1359 ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy()); |
1386 ASSERT(entries_->at(entries_->length() - 1).id < id); | 1360 return entry_info.id; |
1387 entry->value = reinterpret_cast<void*>(entries_->length()); | |
1388 entries_->Add(EntryInfo(id, addr)); | |
1389 ASSERT(static_cast<uint32_t>(entries_->length()) > entries_map_.occupancy()); | |
1390 } | 1361 } |
1391 | 1362 |
1392 | 1363 |
1393 SnapshotObjectId HeapObjectsMap::FindEntry(Address addr) { | 1364 SnapshotObjectId HeapObjectsMap::FindOrAddEntry(Address addr, |
1394 HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), false); | 1365 unsigned int size) { |
1395 if (entry != NULL) { | 1366 ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy()); |
1396 int entry_index = | |
1397 static_cast<int>(reinterpret_cast<intptr_t>(entry->value)); | |
1398 EntryInfo& entry_info = entries_->at(entry_index); | |
1399 entry_info.accessed = true; | |
1400 ASSERT(static_cast<uint32_t>(entries_->length()) > | |
1401 entries_map_.occupancy()); | |
1402 return entry_info.id; | |
1403 } else { | |
1404 return 0; | |
1405 } | |
1406 } | |
1407 | |
1408 | |
1409 SnapshotObjectId HeapObjectsMap::FindOrAddEntry(Address addr) { | |
1410 ASSERT(static_cast<uint32_t>(entries_->length()) > entries_map_.occupancy()); | |
1411 HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), true); | 1367 HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), true); |
1412 if (entry->value != NULL) { | 1368 if (entry->value != NULL) { |
1413 int entry_index = | 1369 int entry_index = |
1414 static_cast<int>(reinterpret_cast<intptr_t>(entry->value)); | 1370 static_cast<int>(reinterpret_cast<intptr_t>(entry->value)); |
1415 EntryInfo& entry_info = entries_->at(entry_index); | 1371 EntryInfo& entry_info = entries_.at(entry_index); |
1416 entry_info.accessed = true; | 1372 entry_info.accessed = true; |
| 1373 entry_info.size = size; |
1417 return entry_info.id; | 1374 return entry_info.id; |
1418 } | 1375 } |
1419 entry->value = reinterpret_cast<void*>(entries_->length()); | 1376 entry->value = reinterpret_cast<void*>(entries_.length()); |
1420 SnapshotObjectId id = next_id_; | 1377 SnapshotObjectId id = next_id_; |
1421 next_id_ += kObjectIdStep; | 1378 next_id_ += kObjectIdStep; |
1422 entries_->Add(EntryInfo(id, addr)); | 1379 entries_.Add(EntryInfo(id, addr, size)); |
1423 ASSERT(static_cast<uint32_t>(entries_->length()) > entries_map_.occupancy()); | 1380 ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy()); |
1424 return id; | 1381 return id; |
1425 } | 1382 } |
1426 | 1383 |
1427 | 1384 |
1428 void HeapObjectsMap::StopHeapObjectsTracking() { | 1385 void HeapObjectsMap::StopHeapObjectsTracking() { |
1429 time_intervals_.Clear(); | 1386 time_intervals_.Clear(); |
1430 } | 1387 } |
1431 | 1388 |
1432 void HeapObjectsMap::UpdateHeapObjectsMap() { | 1389 void HeapObjectsMap::UpdateHeapObjectsMap() { |
1433 HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask, | 1390 HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask, |
1434 "HeapSnapshotsCollection::UpdateHeapObjectsMap"); | 1391 "HeapSnapshotsCollection::UpdateHeapObjectsMap"); |
1435 HeapIterator iterator(HeapIterator::kFilterUnreachable); | 1392 HeapIterator iterator; |
1436 for (HeapObject* obj = iterator.next(); | 1393 for (HeapObject* obj = iterator.next(); |
1437 obj != NULL; | 1394 obj != NULL; |
1438 obj = iterator.next()) { | 1395 obj = iterator.next()) { |
1439 FindOrAddEntry(obj->address()); | 1396 FindOrAddEntry(obj->address(), obj->Size()); |
1440 } | 1397 } |
1441 initial_fill_mode_ = false; | |
1442 RemoveDeadEntries(); | 1398 RemoveDeadEntries(); |
1443 } | 1399 } |
1444 | 1400 |
1445 | 1401 |
1446 void HeapObjectsMap::PushHeapObjectsStats(OutputStream* stream) { | 1402 void HeapObjectsMap::PushHeapObjectsStats(OutputStream* stream) { |
1447 UpdateHeapObjectsMap(); | 1403 UpdateHeapObjectsMap(); |
1448 time_intervals_.Add(TimeInterval(next_id_)); | 1404 time_intervals_.Add(TimeInterval(next_id_)); |
1449 int prefered_chunk_size = stream->GetChunkSize(); | 1405 int prefered_chunk_size = stream->GetChunkSize(); |
1450 List<uint32_t> stats_buffer; | 1406 List<uint32_t> stats_buffer; |
1451 ASSERT(!entries_->is_empty()); | 1407 ASSERT(!entries_.is_empty()); |
1452 EntryInfo* entry_info = &entries_->first(); | 1408 EntryInfo* entry_info = &entries_.first(); |
1453 EntryInfo* end_entry_info = &entries_->last() + 1; | 1409 EntryInfo* end_entry_info = &entries_.last() + 1; |
1454 for (int time_interval_index = 0; | 1410 for (int time_interval_index = 0; |
1455 time_interval_index < time_intervals_.length(); | 1411 time_interval_index < time_intervals_.length(); |
1456 ++time_interval_index) { | 1412 ++time_interval_index) { |
1457 TimeInterval& time_interval = time_intervals_[time_interval_index]; | 1413 TimeInterval& time_interval = time_intervals_[time_interval_index]; |
1458 SnapshotObjectId time_interval_id = time_interval.id; | 1414 SnapshotObjectId time_interval_id = time_interval.id; |
1459 uint32_t entries_count = 0; | 1415 uint32_t entries_size = 0; |
| 1416 EntryInfo* start_entry_info = entry_info; |
1460 while (entry_info < end_entry_info && entry_info->id < time_interval_id) { | 1417 while (entry_info < end_entry_info && entry_info->id < time_interval_id) { |
1461 ++entries_count; | 1418 entries_size += entry_info->size; |
1462 ++entry_info; | 1419 ++entry_info; |
1463 } | 1420 } |
1464 if (time_interval.count != entries_count) { | 1421 uint32_t entries_count = |
| 1422 static_cast<uint32_t>(entry_info - start_entry_info); |
| 1423 if (time_interval.count != entries_count || |
| 1424 time_interval.size != entries_size) { |
1465 stats_buffer.Add(time_interval_index); | 1425 stats_buffer.Add(time_interval_index); |
1466 stats_buffer.Add(time_interval.count = entries_count); | 1426 stats_buffer.Add(time_interval.count = entries_count); |
| 1427 stats_buffer.Add(time_interval.size = entries_size); |
1467 if (stats_buffer.length() >= prefered_chunk_size) { | 1428 if (stats_buffer.length() >= prefered_chunk_size) { |
1468 OutputStream::WriteResult result = stream->WriteUint32Chunk( | 1429 OutputStream::WriteResult result = stream->WriteUint32Chunk( |
1469 &stats_buffer.first(), stats_buffer.length()); | 1430 &stats_buffer.first(), stats_buffer.length()); |
1470 if (result == OutputStream::kAbort) return; | 1431 if (result == OutputStream::kAbort) return; |
1471 stats_buffer.Clear(); | 1432 stats_buffer.Clear(); |
1472 } | 1433 } |
1473 } | 1434 } |
1474 } | 1435 } |
1475 ASSERT(entry_info == end_entry_info); | 1436 ASSERT(entry_info == end_entry_info); |
1476 if (!stats_buffer.is_empty()) { | 1437 if (!stats_buffer.is_empty()) { |
1477 OutputStream::WriteResult result = | 1438 OutputStream::WriteResult result = |
1478 stream->WriteUint32Chunk(&stats_buffer.first(), stats_buffer.length()); | 1439 stream->WriteUint32Chunk(&stats_buffer.first(), stats_buffer.length()); |
1479 if (result == OutputStream::kAbort) return; | 1440 if (result == OutputStream::kAbort) return; |
1480 } | 1441 } |
1481 stream->EndOfStream(); | 1442 stream->EndOfStream(); |
1482 } | 1443 } |
1483 | 1444 |
1484 | 1445 |
1485 void HeapObjectsMap::RemoveDeadEntries() { | 1446 void HeapObjectsMap::RemoveDeadEntries() { |
1486 ASSERT(entries_->length() > 0 && | 1447 ASSERT(entries_.length() > 0 && |
1487 entries_->at(0).id == 0 && | 1448 entries_.at(0).id == 0 && |
1488 entries_->at(0).addr == NULL); | 1449 entries_.at(0).addr == NULL); |
1489 int first_free_entry = 1; | 1450 int first_free_entry = 1; |
1490 for (int i = 1; i < entries_->length(); ++i) { | 1451 for (int i = 1; i < entries_.length(); ++i) { |
1491 EntryInfo& entry_info = entries_->at(i); | 1452 EntryInfo& entry_info = entries_.at(i); |
1492 if (entry_info.accessed) { | 1453 if (entry_info.accessed) { |
1493 if (first_free_entry != i) { | 1454 if (first_free_entry != i) { |
1494 entries_->at(first_free_entry) = entry_info; | 1455 entries_.at(first_free_entry) = entry_info; |
1495 } | 1456 } |
1496 entries_->at(first_free_entry).accessed = false; | 1457 entries_.at(first_free_entry).accessed = false; |
1497 HashMap::Entry* entry = entries_map_.Lookup( | 1458 HashMap::Entry* entry = entries_map_.Lookup( |
1498 entry_info.addr, AddressHash(entry_info.addr), false); | 1459 entry_info.addr, AddressHash(entry_info.addr), false); |
1499 ASSERT(entry); | 1460 ASSERT(entry); |
1500 entry->value = reinterpret_cast<void*>(first_free_entry); | 1461 entry->value = reinterpret_cast<void*>(first_free_entry); |
1501 ++first_free_entry; | 1462 ++first_free_entry; |
1502 } else { | 1463 } else { |
1503 if (entry_info.addr) { | 1464 if (entry_info.addr) { |
1504 entries_map_.Remove(entry_info.addr, AddressHash(entry_info.addr)); | 1465 entries_map_.Remove(entry_info.addr, AddressHash(entry_info.addr)); |
1505 } | 1466 } |
1506 } | 1467 } |
1507 } | 1468 } |
1508 entries_->Rewind(first_free_entry); | 1469 entries_.Rewind(first_free_entry); |
1509 ASSERT(static_cast<uint32_t>(entries_->length()) - 1 == | 1470 ASSERT(static_cast<uint32_t>(entries_.length()) - 1 == |
1510 entries_map_.occupancy()); | 1471 entries_map_.occupancy()); |
1511 } | 1472 } |
1512 | 1473 |
1513 | 1474 |
1514 SnapshotObjectId HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) { | 1475 SnapshotObjectId HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) { |
1515 SnapshotObjectId id = static_cast<SnapshotObjectId>(info->GetHash()); | 1476 SnapshotObjectId id = static_cast<SnapshotObjectId>(info->GetHash()); |
1516 const char* label = info->GetLabel(); | 1477 const char* label = info->GetLabel(); |
1517 id ^= HashSequentialString(label, | 1478 id ^= HashSequentialString(label, |
1518 static_cast<int>(strlen(label)), | 1479 static_cast<int>(strlen(label)), |
1519 HEAP->HashSeed()); | 1480 HEAP->HashSeed()); |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1587 // First perform a full GC in order to avoid dead objects. | 1548 // First perform a full GC in order to avoid dead objects. |
1588 HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask, | 1549 HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask, |
1589 "HeapSnapshotsCollection::FindHeapObjectById"); | 1550 "HeapSnapshotsCollection::FindHeapObjectById"); |
1590 AssertNoAllocation no_allocation; | 1551 AssertNoAllocation no_allocation; |
1591 HeapObject* object = NULL; | 1552 HeapObject* object = NULL; |
1592 HeapIterator iterator(HeapIterator::kFilterUnreachable); | 1553 HeapIterator iterator(HeapIterator::kFilterUnreachable); |
1593 // Make sure that object with the given id is still reachable. | 1554 // Make sure that object with the given id is still reachable. |
1594 for (HeapObject* obj = iterator.next(); | 1555 for (HeapObject* obj = iterator.next(); |
1595 obj != NULL; | 1556 obj != NULL; |
1596 obj = iterator.next()) { | 1557 obj = iterator.next()) { |
1597 if (ids_.FindObject(obj->address()) == id) { | 1558 if (ids_.FindEntry(obj->address()) == id) { |
1598 ASSERT(object == NULL); | 1559 ASSERT(object == NULL); |
1599 object = obj; | 1560 object = obj; |
1600 // Can't break -- kFilterUnreachable requires full heap traversal. | 1561 // Can't break -- kFilterUnreachable requires full heap traversal. |
1601 } | 1562 } |
1602 } | 1563 } |
1603 return object != NULL ? Handle<HeapObject>(object) : Handle<HeapObject>(); | 1564 return object != NULL ? Handle<HeapObject>(object) : Handle<HeapObject>(); |
1604 } | 1565 } |
1605 | 1566 |
1606 | 1567 |
1607 HeapEntry* const HeapEntriesMap::kHeapEntryPlaceholder = | 1568 HeapEntry* const HeapEntriesMap::kHeapEntryPlaceholder = |
(...skipping 280 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1888 children_count, | 1849 children_count, |
1889 retainers_count); | 1850 retainers_count); |
1890 } | 1851 } |
1891 | 1852 |
1892 | 1853 |
1893 HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object, | 1854 HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object, |
1894 HeapEntry::Type type, | 1855 HeapEntry::Type type, |
1895 const char* name, | 1856 const char* name, |
1896 int children_count, | 1857 int children_count, |
1897 int retainers_count) { | 1858 int retainers_count) { |
| 1859 int object_size = object->Size(); |
| 1860 SnapshotObjectId object_id = |
| 1861 collection_->GetObjectId(object->address(), object_size); |
1898 return snapshot_->AddEntry(type, | 1862 return snapshot_->AddEntry(type, |
1899 name, | 1863 name, |
1900 collection_->GetObjectId(object->address()), | 1864 object_id, |
1901 object->Size(), | 1865 object_size, |
1902 children_count, | 1866 children_count, |
1903 retainers_count); | 1867 retainers_count); |
1904 } | 1868 } |
1905 | 1869 |
1906 | 1870 |
1907 class GcSubrootsEnumerator : public ObjectVisitor { | 1871 class GcSubrootsEnumerator : public ObjectVisitor { |
1908 public: | 1872 public: |
1909 GcSubrootsEnumerator( | 1873 GcSubrootsEnumerator( |
1910 SnapshotFillerInterface* filler, V8HeapExplorer* explorer) | 1874 SnapshotFillerInterface* filler, V8HeapExplorer* explorer) |
1911 : filler_(filler), | 1875 : filler_(filler), |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2009 void V8HeapExplorer::ExtractReferences(HeapObject* obj) { | 1973 void V8HeapExplorer::ExtractReferences(HeapObject* obj) { |
2010 HeapEntry* entry = GetEntry(obj); | 1974 HeapEntry* entry = GetEntry(obj); |
2011 if (entry == NULL) return; // No interest in this object. | 1975 if (entry == NULL) return; // No interest in this object. |
2012 | 1976 |
2013 bool extract_indexed_refs = true; | 1977 bool extract_indexed_refs = true; |
2014 if (obj->IsJSGlobalProxy()) { | 1978 if (obj->IsJSGlobalProxy()) { |
2015 // We need to reference JS global objects from snapshot's root. | 1979 // We need to reference JS global objects from snapshot's root. |
2016 // We use JSGlobalProxy because this is what embedder (e.g. browser) | 1980 // We use JSGlobalProxy because this is what embedder (e.g. browser) |
2017 // uses for the global object. | 1981 // uses for the global object. |
2018 JSGlobalProxy* proxy = JSGlobalProxy::cast(obj); | 1982 JSGlobalProxy* proxy = JSGlobalProxy::cast(obj); |
2019 SetRootShortcutReference(proxy->map()->prototype()); | 1983 SetWindowReference(proxy->map()->prototype()); |
2020 } else if (obj->IsJSObject()) { | 1984 } else if (obj->IsJSObject()) { |
2021 JSObject* js_obj = JSObject::cast(obj); | 1985 JSObject* js_obj = JSObject::cast(obj); |
2022 ExtractClosureReferences(js_obj, entry); | 1986 ExtractClosureReferences(js_obj, entry); |
2023 ExtractPropertyReferences(js_obj, entry); | 1987 ExtractPropertyReferences(js_obj, entry); |
2024 ExtractElementReferences(js_obj, entry); | 1988 ExtractElementReferences(js_obj, entry); |
2025 ExtractInternalReferences(js_obj, entry); | 1989 ExtractInternalReferences(js_obj, entry); |
2026 SetPropertyReference( | 1990 SetPropertyReference( |
2027 obj, entry, heap_->Proto_symbol(), js_obj->GetPrototype()); | 1991 obj, entry, heap_->Proto_symbol(), js_obj->GetPrototype()); |
2028 if (obj->IsJSFunction()) { | 1992 if (obj->IsJSFunction()) { |
2029 JSFunction* js_fun = JSFunction::cast(js_obj); | 1993 JSFunction* js_fun = JSFunction::cast(js_obj); |
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2180 obj->Iterate(&refs_extractor); | 2144 obj->Iterate(&refs_extractor); |
2181 } | 2145 } |
2182 } | 2146 } |
2183 | 2147 |
2184 | 2148 |
2185 void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj, | 2149 void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj, |
2186 HeapEntry* entry) { | 2150 HeapEntry* entry) { |
2187 if (!js_obj->IsJSFunction()) return; | 2151 if (!js_obj->IsJSFunction()) return; |
2188 | 2152 |
2189 JSFunction* func = JSFunction::cast(js_obj); | 2153 JSFunction* func = JSFunction::cast(js_obj); |
2190 Context* context = func->context(); | 2154 Context* context = func->context()->declaration_context(); |
2191 ScopeInfo* scope_info = context->closure()->shared()->scope_info(); | 2155 ScopeInfo* scope_info = context->closure()->shared()->scope_info(); |
2192 | 2156 |
2193 if (func->shared()->bound()) { | 2157 if (func->shared()->bound()) { |
2194 FixedArray* bindings = func->function_bindings(); | 2158 FixedArray* bindings = func->function_bindings(); |
2195 SetNativeBindReference(js_obj, entry, "bound_this", | 2159 SetNativeBindReference(js_obj, entry, "bound_this", |
2196 bindings->get(JSFunction::kBoundThisIndex)); | 2160 bindings->get(JSFunction::kBoundThisIndex)); |
2197 SetNativeBindReference(js_obj, entry, "bound_function", | 2161 SetNativeBindReference(js_obj, entry, "bound_function", |
2198 bindings->get(JSFunction::kBoundFunctionIndex)); | 2162 bindings->get(JSFunction::kBoundFunctionIndex)); |
2199 for (int i = JSFunction::kBoundArgumentsStartIndex; | 2163 for (int i = JSFunction::kBoundArgumentsStartIndex; |
2200 i < bindings->length(); i++) { | 2164 i < bindings->length(); i++) { |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2277 break; | 2241 break; |
2278 } | 2242 } |
2279 } | 2243 } |
2280 } else { | 2244 } else { |
2281 StringDictionary* dictionary = js_obj->property_dictionary(); | 2245 StringDictionary* dictionary = js_obj->property_dictionary(); |
2282 int length = dictionary->Capacity(); | 2246 int length = dictionary->Capacity(); |
2283 for (int i = 0; i < length; ++i) { | 2247 for (int i = 0; i < length; ++i) { |
2284 Object* k = dictionary->KeyAt(i); | 2248 Object* k = dictionary->KeyAt(i); |
2285 if (dictionary->IsKey(k)) { | 2249 if (dictionary->IsKey(k)) { |
2286 Object* target = dictionary->ValueAt(i); | 2250 Object* target = dictionary->ValueAt(i); |
2287 SetPropertyReference( | |
2288 js_obj, entry, String::cast(k), target); | |
2289 // We assume that global objects can only have slow properties. | 2251 // We assume that global objects can only have slow properties. |
2290 if (target->IsJSGlobalPropertyCell()) { | 2252 Object* value = target->IsJSGlobalPropertyCell() |
2291 SetPropertyShortcutReference(js_obj, | 2253 ? JSGlobalPropertyCell::cast(target)->value() |
2292 entry, | 2254 : target; |
2293 String::cast(k), | 2255 if (String::cast(k)->length() > 0) { |
2294 JSGlobalPropertyCell::cast( | 2256 SetPropertyReference(js_obj, entry, String::cast(k), value); |
2295 target)->value()); | 2257 } else { |
| 2258 TagObject(value, "(hidden properties)"); |
| 2259 SetInternalReference(js_obj, entry, "hidden_properties", value); |
2296 } | 2260 } |
2297 } | 2261 } |
2298 } | 2262 } |
2299 } | 2263 } |
2300 } | 2264 } |
2301 | 2265 |
2302 | 2266 |
2303 void V8HeapExplorer::ExtractElementReferences(JSObject* js_obj, | 2267 void V8HeapExplorer::ExtractElementReferences(JSObject* js_obj, |
2304 HeapEntry* entry) { | 2268 HeapEntry* entry) { |
2305 if (js_obj->HasFastElements()) { | 2269 if (js_obj->HasFastElements()) { |
(...skipping 348 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2654 | 2618 |
2655 | 2619 |
2656 void V8HeapExplorer::SetRootGcRootsReference() { | 2620 void V8HeapExplorer::SetRootGcRootsReference() { |
2657 filler_->SetIndexedAutoIndexReference( | 2621 filler_->SetIndexedAutoIndexReference( |
2658 HeapGraphEdge::kElement, | 2622 HeapGraphEdge::kElement, |
2659 kInternalRootObject, snapshot_->root(), | 2623 kInternalRootObject, snapshot_->root(), |
2660 kGcRootsObject, snapshot_->gc_roots()); | 2624 kGcRootsObject, snapshot_->gc_roots()); |
2661 } | 2625 } |
2662 | 2626 |
2663 | 2627 |
2664 void V8HeapExplorer::SetRootShortcutReference(Object* child_obj) { | 2628 void V8HeapExplorer::SetWindowReference(Object* child_obj) { |
2665 HeapEntry* child_entry = GetEntry(child_obj); | 2629 HeapEntry* child_entry = GetEntry(child_obj); |
2666 ASSERT(child_entry != NULL); | 2630 ASSERT(child_entry != NULL); |
2667 filler_->SetNamedAutoIndexReference( | 2631 filler_->SetNamedAutoIndexReference( |
2668 HeapGraphEdge::kShortcut, | 2632 HeapGraphEdge::kShortcut, |
2669 kInternalRootObject, snapshot_->root(), | 2633 kInternalRootObject, snapshot_->root(), |
2670 child_obj, child_entry); | 2634 child_obj, child_entry); |
2671 } | 2635 } |
2672 | 2636 |
2673 | 2637 |
2674 void V8HeapExplorer::SetGcRootsReference(VisitorSynchronization::SyncTag tag) { | 2638 void V8HeapExplorer::SetGcRootsReference(VisitorSynchronization::SyncTag tag) { |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2736 isolate->factory()->NewStringFromAscii(CStrVector("document")); | 2700 isolate->factory()->NewStringFromAscii(CStrVector("document")); |
2737 Handle<String> url_string = | 2701 Handle<String> url_string = |
2738 isolate->factory()->NewStringFromAscii(CStrVector("URL")); | 2702 isolate->factory()->NewStringFromAscii(CStrVector("URL")); |
2739 const char** urls = NewArray<const char*>(enumerator.count()); | 2703 const char** urls = NewArray<const char*>(enumerator.count()); |
2740 for (int i = 0, l = enumerator.count(); i < l; ++i) { | 2704 for (int i = 0, l = enumerator.count(); i < l; ++i) { |
2741 urls[i] = NULL; | 2705 urls[i] = NULL; |
2742 HandleScope scope; | 2706 HandleScope scope; |
2743 Handle<JSGlobalObject> global_obj = enumerator.at(i); | 2707 Handle<JSGlobalObject> global_obj = enumerator.at(i); |
2744 Object* obj_document; | 2708 Object* obj_document; |
2745 if (global_obj->GetProperty(*document_string)->ToObject(&obj_document) && | 2709 if (global_obj->GetProperty(*document_string)->ToObject(&obj_document) && |
2746 obj_document->IsJSObject()) { | 2710 obj_document->IsJSObject()) { |
2747 JSObject* document = JSObject::cast(obj_document); | 2711 JSObject* document = JSObject::cast(obj_document); |
2748 Object* obj_url; | 2712 Object* obj_url; |
2749 if (document->GetProperty(*url_string)->ToObject(&obj_url) && | 2713 if (document->GetProperty(*url_string)->ToObject(&obj_url) && |
2750 obj_url->IsString()) { | 2714 obj_url->IsString()) { |
2751 urls[i] = collection_->names()->GetName(String::cast(obj_url)); | 2715 urls[i] = collection_->names()->GetName(String::cast(obj_url)); |
2752 } | 2716 } |
2753 } | 2717 } |
2754 } | 2718 } |
2755 | 2719 |
2756 AssertNoAllocation no_allocation; | 2720 AssertNoAllocation no_allocation; |
(...skipping 533 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3290 // in turn may relocate objects in property maps thus changing the heap | 3254 // in turn may relocate objects in property maps thus changing the heap |
3291 // layout and affecting retainer counts. This is not acceptable because | 3255 // layout and affecting retainer counts. This is not acceptable because |
3292 // number of retainers must not change between count and fill passes. | 3256 // number of retainers must not change between count and fill passes. |
3293 // To avoid this there's a separate postpass that set object names. | 3257 // To avoid this there's a separate postpass that set object names. |
3294 return v8_heap_explorer_.IterateAndExtractReferences(&filler) | 3258 return v8_heap_explorer_.IterateAndExtractReferences(&filler) |
3295 && dom_explorer_.IterateAndExtractReferences(&filler) | 3259 && dom_explorer_.IterateAndExtractReferences(&filler) |
3296 && v8_heap_explorer_.IterateAndSetObjectNames(&filler); | 3260 && v8_heap_explorer_.IterateAndSetObjectNames(&filler); |
3297 } | 3261 } |
3298 | 3262 |
3299 | 3263 |
3300 void HeapSnapshotGenerator::FillReversePostorderIndexes( | 3264 bool HeapSnapshotGenerator::IsWindowReference(const HeapGraphEdge& edge) { |
| 3265 ASSERT(edge.from() == snapshot_->root()); |
| 3266 return edge.type() == HeapGraphEdge::kShortcut; |
| 3267 } |
| 3268 |
| 3269 |
| 3270 void HeapSnapshotGenerator::MarkWindowReachableObjects() { |
| 3271 List<HeapEntry*> worklist; |
| 3272 |
| 3273 Vector<HeapGraphEdge> children = snapshot_->root()->children(); |
| 3274 for (int i = 0; i < children.length(); ++i) { |
| 3275 if (IsWindowReference(children[i])) { |
| 3276 worklist.Add(children[i].to()); |
| 3277 } |
| 3278 } |
| 3279 |
| 3280 while (!worklist.is_empty()) { |
| 3281 HeapEntry* entry = worklist.RemoveLast(); |
| 3282 if (entry->reachable_from_window()) continue; |
| 3283 entry->set_reachable_from_window(); |
| 3284 Vector<HeapGraphEdge> children = entry->children(); |
| 3285 for (int i = 0; i < children.length(); ++i) { |
| 3286 HeapEntry* child = children[i].to(); |
| 3287 if (!child->reachable_from_window()) { |
| 3288 worklist.Add(child); |
| 3289 } |
| 3290 } |
| 3291 } |
| 3292 } |
| 3293 |
| 3294 |
| 3295 static bool IsRetainingEdge(HeapGraphEdge* edge) { |
| 3296 if (edge->type() == HeapGraphEdge::kShortcut) return false; |
| 3297 // The edge is not retaining if it goes from system domain |
| 3298 // (i.e. an object not reachable from window) to the user domain |
| 3299 // (i.e. a reachable object). |
| 3300 return edge->from()->reachable_from_window() |
| 3301 || !edge->to()->reachable_from_window(); |
| 3302 } |
| 3303 |
| 3304 |
| 3305 void HeapSnapshotGenerator::FillPostorderIndexes( |
3301 Vector<HeapEntry*>* entries) { | 3306 Vector<HeapEntry*>* entries) { |
3302 snapshot_->ClearPaint(); | 3307 snapshot_->ClearPaint(); |
3303 int current_entry = 0; | 3308 int current_entry = 0; |
3304 List<HeapEntry*> nodes_to_visit; | 3309 List<HeapEntry*> nodes_to_visit; |
3305 nodes_to_visit.Add(snapshot_->root()); | 3310 HeapEntry* root = snapshot_->root(); |
| 3311 nodes_to_visit.Add(root); |
3306 snapshot_->root()->paint(); | 3312 snapshot_->root()->paint(); |
3307 while (!nodes_to_visit.is_empty()) { | 3313 while (!nodes_to_visit.is_empty()) { |
3308 HeapEntry* entry = nodes_to_visit.last(); | 3314 HeapEntry* entry = nodes_to_visit.last(); |
3309 Vector<HeapGraphEdge> children = entry->children(); | 3315 Vector<HeapGraphEdge> children = entry->children(); |
3310 bool has_new_edges = false; | 3316 bool has_new_edges = false; |
3311 for (int i = 0; i < children.length(); ++i) { | 3317 for (int i = 0; i < children.length(); ++i) { |
3312 if (children[i].type() == HeapGraphEdge::kShortcut) continue; | 3318 if (entry != root && !IsRetainingEdge(&children[i])) continue; |
3313 HeapEntry* child = children[i].to(); | 3319 HeapEntry* child = children[i].to(); |
3314 if (!child->painted()) { | 3320 if (!child->painted()) { |
3315 nodes_to_visit.Add(child); | 3321 nodes_to_visit.Add(child); |
3316 child->paint(); | 3322 child->paint(); |
3317 has_new_edges = true; | 3323 has_new_edges = true; |
3318 } | 3324 } |
3319 } | 3325 } |
3320 if (!has_new_edges) { | 3326 if (!has_new_edges) { |
3321 entry->set_ordered_index(current_entry); | 3327 entry->set_ordered_index(current_entry); |
3322 (*entries)[current_entry++] = entry; | 3328 (*entries)[current_entry++] = entry; |
(...skipping 14 matching lines...) Expand all Loading... |
3337 } | 3343 } |
3338 | 3344 |
3339 | 3345 |
3340 // The algorithm is based on the article: | 3346 // The algorithm is based on the article: |
3341 // K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm" | 3347 // K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm" |
3342 // Softw. Pract. Exper. 4 (2001), pp. 1-10. | 3348 // Softw. Pract. Exper. 4 (2001), pp. 1-10. |
3343 bool HeapSnapshotGenerator::BuildDominatorTree( | 3349 bool HeapSnapshotGenerator::BuildDominatorTree( |
3344 const Vector<HeapEntry*>& entries, | 3350 const Vector<HeapEntry*>& entries, |
3345 Vector<int>* dominators) { | 3351 Vector<int>* dominators) { |
3346 if (entries.length() == 0) return true; | 3352 if (entries.length() == 0) return true; |
| 3353 HeapEntry* root = snapshot_->root(); |
3347 const int entries_length = entries.length(), root_index = entries_length - 1; | 3354 const int entries_length = entries.length(), root_index = entries_length - 1; |
3348 static const int kNoDominator = -1; | 3355 static const int kNoDominator = -1; |
3349 for (int i = 0; i < root_index; ++i) (*dominators)[i] = kNoDominator; | 3356 for (int i = 0; i < root_index; ++i) (*dominators)[i] = kNoDominator; |
3350 (*dominators)[root_index] = root_index; | 3357 (*dominators)[root_index] = root_index; |
3351 | 3358 |
3352 // The affected array is used to mark entries which dominators | 3359 // The affected array is used to mark entries which dominators |
3353 // have to be racalculated because of changes in their retainers. | 3360 // have to be racalculated because of changes in their retainers. |
3354 ScopedVector<bool> affected(entries_length); | 3361 ScopedVector<bool> affected(entries_length); |
3355 for (int i = 0; i < affected.length(); ++i) affected[i] = false; | 3362 for (int i = 0; i < affected.length(); ++i) affected[i] = false; |
3356 // Mark the root direct children as affected. | 3363 // Mark the root direct children as affected. |
3357 Vector<HeapGraphEdge> children = entries[root_index]->children(); | 3364 Vector<HeapGraphEdge> children = entries[root_index]->children(); |
3358 for (int i = 0; i < children.length(); ++i) { | 3365 for (int i = 0; i < children.length(); ++i) { |
3359 affected[children[i].to()->ordered_index()] = true; | 3366 affected[children[i].to()->ordered_index()] = true; |
3360 } | 3367 } |
3361 | 3368 |
3362 bool changed = true; | 3369 bool changed = true; |
3363 while (changed) { | 3370 while (changed) { |
3364 changed = false; | 3371 changed = false; |
3365 if (!ProgressReport(true)) return false; | 3372 if (!ProgressReport(true)) return false; |
3366 for (int i = root_index - 1; i >= 0; --i) { | 3373 for (int i = root_index - 1; i >= 0; --i) { |
3367 if (!affected[i]) continue; | 3374 if (!affected[i]) continue; |
3368 affected[i] = false; | 3375 affected[i] = false; |
3369 // If dominator of the entry has already been set to root, | 3376 // If dominator of the entry has already been set to root, |
3370 // then it can't propagate any further. | 3377 // then it can't propagate any further. |
3371 if ((*dominators)[i] == root_index) continue; | 3378 if ((*dominators)[i] == root_index) continue; |
3372 int new_idom_index = kNoDominator; | 3379 int new_idom_index = kNoDominator; |
3373 Vector<HeapGraphEdge*> rets = entries[i]->retainers(); | 3380 Vector<HeapGraphEdge*> rets = entries[i]->retainers(); |
3374 for (int j = 0; j < rets.length(); ++j) { | 3381 for (int j = 0; j < rets.length(); ++j) { |
3375 if (rets[j]->type() == HeapGraphEdge::kShortcut) continue; | 3382 if (rets[j]->from() != root && !IsRetainingEdge(rets[j])) continue; |
3376 int ret_index = rets[j]->From()->ordered_index(); | 3383 int ret_index = rets[j]->from()->ordered_index(); |
3377 if (dominators->at(ret_index) != kNoDominator) { | 3384 if (dominators->at(ret_index) != kNoDominator) { |
3378 new_idom_index = new_idom_index == kNoDominator | 3385 new_idom_index = new_idom_index == kNoDominator |
3379 ? ret_index | 3386 ? ret_index |
3380 : Intersect(ret_index, new_idom_index, *dominators); | 3387 : Intersect(ret_index, new_idom_index, *dominators); |
3381 // If idom has already reached the root, it doesn't make sense | 3388 // If idom has already reached the root, it doesn't make sense |
3382 // to check other retainers. | 3389 // to check other retainers. |
3383 if (new_idom_index == root_index) break; | 3390 if (new_idom_index == root_index) break; |
3384 } | 3391 } |
3385 } | 3392 } |
3386 if (new_idom_index != kNoDominator | 3393 if (new_idom_index != kNoDominator |
3387 && dominators->at(i) != new_idom_index) { | 3394 && dominators->at(i) != new_idom_index) { |
3388 (*dominators)[i] = new_idom_index; | 3395 (*dominators)[i] = new_idom_index; |
3389 changed = true; | 3396 changed = true; |
3390 Vector<HeapGraphEdge> children = entries[i]->children(); | 3397 Vector<HeapGraphEdge> children = entries[i]->children(); |
3391 for (int j = 0; j < children.length(); ++j) { | 3398 for (int j = 0; j < children.length(); ++j) { |
3392 affected[children[j].to()->ordered_index()] = true; | 3399 affected[children[j].to()->ordered_index()] = true; |
3393 } | 3400 } |
3394 } | 3401 } |
3395 } | 3402 } |
3396 } | 3403 } |
3397 return true; | 3404 return true; |
3398 } | 3405 } |
3399 | 3406 |
3400 | 3407 |
3401 bool HeapSnapshotGenerator::SetEntriesDominators() { | 3408 bool HeapSnapshotGenerator::SetEntriesDominators() { |
3402 // This array is used for maintaining reverse postorder of nodes. | 3409 MarkWindowReachableObjects(); |
| 3410 // This array is used for maintaining postorder of nodes. |
3403 ScopedVector<HeapEntry*> ordered_entries(snapshot_->entries()->length()); | 3411 ScopedVector<HeapEntry*> ordered_entries(snapshot_->entries()->length()); |
3404 FillReversePostorderIndexes(&ordered_entries); | 3412 FillPostorderIndexes(&ordered_entries); |
3405 ScopedVector<int> dominators(ordered_entries.length()); | 3413 ScopedVector<int> dominators(ordered_entries.length()); |
3406 if (!BuildDominatorTree(ordered_entries, &dominators)) return false; | 3414 if (!BuildDominatorTree(ordered_entries, &dominators)) return false; |
3407 for (int i = 0; i < ordered_entries.length(); ++i) { | 3415 for (int i = 0; i < ordered_entries.length(); ++i) { |
3408 ASSERT(dominators[i] >= 0); | 3416 ASSERT(dominators[i] >= 0); |
3409 ordered_entries[i]->set_dominator(ordered_entries[dominators[i]]); | 3417 ordered_entries[i]->set_dominator(ordered_entries[dominators[i]]); |
3410 } | 3418 } |
3411 return true; | 3419 return true; |
3412 } | 3420 } |
3413 | 3421 |
3414 | 3422 |
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3569 SnapshotSizeConstants<kPointerSize>::kMaxSerializableSnapshotRawSize / MB, | 3577 SnapshotSizeConstants<kPointerSize>::kMaxSerializableSnapshotRawSize / MB, |
3570 (snapshot_->raw_entries_size() + MB - 1) / MB); | 3578 (snapshot_->raw_entries_size() + MB - 1) / MB); |
3571 HeapEntry* message = result->AddEntry( | 3579 HeapEntry* message = result->AddEntry( |
3572 HeapEntry::kString, text, 0, 4, 0, 0); | 3580 HeapEntry::kString, text, 0, 4, 0, 0); |
3573 root->SetUnidirElementReference(0, 1, message); | 3581 root->SetUnidirElementReference(0, 1, message); |
3574 result->SetDominatorsToSelf(); | 3582 result->SetDominatorsToSelf(); |
3575 return result; | 3583 return result; |
3576 } | 3584 } |
3577 | 3585 |
3578 | 3586 |
| 3587 void HeapSnapshotJSONSerializer::CalculateNodeIndexes( |
| 3588 const List<HeapEntry*>& nodes) { |
| 3589 // type,name,id,self_size,retained_size,dominator,children_index. |
| 3590 const int node_fields_count = 7; |
| 3591 // Root must be the first. |
| 3592 ASSERT(nodes.first() == snapshot_->root()); |
| 3593 // Rewrite node indexes, so they refer to actual array positions. Do this |
| 3594 // only once. |
| 3595 if (nodes[0]->entry_index() == -1) { |
| 3596 int index = 0; |
| 3597 for (int i = 0; i < nodes.length(); ++i, index += node_fields_count) { |
| 3598 nodes[i]->set_entry_index(index); |
| 3599 } |
| 3600 } |
| 3601 } |
| 3602 |
| 3603 |
3579 void HeapSnapshotJSONSerializer::SerializeImpl() { | 3604 void HeapSnapshotJSONSerializer::SerializeImpl() { |
| 3605 List<HeapEntry*>& nodes = *(snapshot_->entries()); |
| 3606 CalculateNodeIndexes(nodes); |
3580 writer_->AddCharacter('{'); | 3607 writer_->AddCharacter('{'); |
3581 writer_->AddString("\"snapshot\":{"); | 3608 writer_->AddString("\"snapshot\":{"); |
3582 SerializeSnapshot(); | 3609 SerializeSnapshot(); |
3583 if (writer_->aborted()) return; | 3610 if (writer_->aborted()) return; |
3584 writer_->AddString("},\n"); | 3611 writer_->AddString("},\n"); |
3585 writer_->AddString("\"nodes\":["); | 3612 writer_->AddString("\"nodes\":["); |
3586 SerializeNodes(); | 3613 SerializeNodes(nodes); |
| 3614 if (writer_->aborted()) return; |
| 3615 writer_->AddString("],\n"); |
| 3616 writer_->AddString("\"edges\":["); |
| 3617 SerializeEdges(nodes); |
3587 if (writer_->aborted()) return; | 3618 if (writer_->aborted()) return; |
3588 writer_->AddString("],\n"); | 3619 writer_->AddString("],\n"); |
3589 writer_->AddString("\"strings\":["); | 3620 writer_->AddString("\"strings\":["); |
3590 SerializeStrings(); | 3621 SerializeStrings(); |
3591 if (writer_->aborted()) return; | 3622 if (writer_->aborted()) return; |
3592 writer_->AddCharacter(']'); | 3623 writer_->AddCharacter(']'); |
3593 writer_->AddCharacter('}'); | 3624 writer_->AddCharacter('}'); |
3594 writer_->Finalize(); | 3625 writer_->Finalize(); |
3595 } | 3626 } |
3596 | 3627 |
(...skipping 26 matching lines...) Expand all Loading... |
3623 int result = buffer_pos; | 3654 int result = buffer_pos; |
3624 do { | 3655 do { |
3625 int last_digit = value % 10; | 3656 int last_digit = value % 10; |
3626 buffer[--buffer_pos] = '0' + last_digit; | 3657 buffer[--buffer_pos] = '0' + last_digit; |
3627 value /= 10; | 3658 value /= 10; |
3628 } while (value); | 3659 } while (value); |
3629 return result; | 3660 return result; |
3630 } | 3661 } |
3631 | 3662 |
3632 | 3663 |
3633 void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge) { | 3664 void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge, |
| 3665 bool first_edge) { |
3634 // The buffer needs space for 3 ints, 3 commas and \0 | 3666 // The buffer needs space for 3 ints, 3 commas and \0 |
3635 static const int kBufferSize = | 3667 static const int kBufferSize = |
3636 MaxDecimalDigitsIn<sizeof(int)>::kSigned * 3 + 3 + 1; // NOLINT | 3668 MaxDecimalDigitsIn<sizeof(int)>::kSigned * 3 + 3 + 1; // NOLINT |
3637 EmbeddedVector<char, kBufferSize> buffer; | 3669 EmbeddedVector<char, kBufferSize> buffer; |
3638 int edge_name_or_index = edge->type() == HeapGraphEdge::kElement | 3670 int edge_name_or_index = edge->type() == HeapGraphEdge::kElement |
3639 || edge->type() == HeapGraphEdge::kHidden | 3671 || edge->type() == HeapGraphEdge::kHidden |
3640 || edge->type() == HeapGraphEdge::kWeak | 3672 || edge->type() == HeapGraphEdge::kWeak |
3641 ? edge->index() : GetStringId(edge->name()); | 3673 ? edge->index() : GetStringId(edge->name()); |
3642 int buffer_pos = 0; | 3674 int buffer_pos = 0; |
3643 buffer[buffer_pos++] = ','; | 3675 if (!first_edge) { |
| 3676 buffer[buffer_pos++] = ','; |
| 3677 } |
3644 buffer_pos = itoa(edge->type(), buffer, buffer_pos); | 3678 buffer_pos = itoa(edge->type(), buffer, buffer_pos); |
3645 buffer[buffer_pos++] = ','; | 3679 buffer[buffer_pos++] = ','; |
3646 buffer_pos = itoa(edge_name_or_index, buffer, buffer_pos); | 3680 buffer_pos = itoa(edge_name_or_index, buffer, buffer_pos); |
3647 buffer[buffer_pos++] = ','; | 3681 buffer[buffer_pos++] = ','; |
3648 buffer_pos = itoa(edge->to()->entry_index(), buffer, buffer_pos); | 3682 buffer_pos = itoa(edge->to()->entry_index(), buffer, buffer_pos); |
3649 buffer[buffer_pos++] = '\0'; | 3683 buffer[buffer_pos++] = '\0'; |
3650 writer_->AddString(buffer.start()); | 3684 writer_->AddString(buffer.start()); |
3651 } | 3685 } |
3652 | 3686 |
3653 | 3687 |
3654 void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) { | 3688 void HeapSnapshotJSONSerializer::SerializeEdges(const List<HeapEntry*>& nodes) { |
| 3689 bool first_edge = true; |
| 3690 for (int i = 0; i < nodes.length(); ++i) { |
| 3691 HeapEntry* entry = nodes[i]; |
| 3692 Vector<HeapGraphEdge> children = entry->children(); |
| 3693 for (int j = 0; j < children.length(); ++j) { |
| 3694 SerializeEdge(&children[j], first_edge); |
| 3695 first_edge = false; |
| 3696 if (writer_->aborted()) return; |
| 3697 } |
| 3698 } |
| 3699 } |
| 3700 |
| 3701 |
| 3702 void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry, |
| 3703 int edges_index) { |
3655 // The buffer needs space for 6 ints, 1 uint32_t, 7 commas, \n and \0 | 3704 // The buffer needs space for 6 ints, 1 uint32_t, 7 commas, \n and \0 |
3656 static const int kBufferSize = | 3705 static const int kBufferSize = |
3657 6 * MaxDecimalDigitsIn<sizeof(int)>::kSigned // NOLINT | 3706 6 * MaxDecimalDigitsIn<sizeof(int)>::kSigned // NOLINT |
3658 + MaxDecimalDigitsIn<sizeof(uint32_t)>::kUnsigned // NOLINT | 3707 + MaxDecimalDigitsIn<sizeof(uint32_t)>::kUnsigned // NOLINT |
3659 + 7 + 1 + 1; | 3708 + 7 + 1 + 1; |
3660 EmbeddedVector<char, kBufferSize> buffer; | 3709 EmbeddedVector<char, kBufferSize> buffer; |
3661 Vector<HeapGraphEdge> children = entry->children(); | |
3662 int buffer_pos = 0; | 3710 int buffer_pos = 0; |
3663 buffer[buffer_pos++] = '\n'; | 3711 buffer[buffer_pos++] = '\n'; |
3664 buffer[buffer_pos++] = ','; | 3712 if (entry->entry_index() != 0) { |
| 3713 buffer[buffer_pos++] = ','; |
| 3714 } |
3665 buffer_pos = itoa(entry->type(), buffer, buffer_pos); | 3715 buffer_pos = itoa(entry->type(), buffer, buffer_pos); |
3666 buffer[buffer_pos++] = ','; | 3716 buffer[buffer_pos++] = ','; |
3667 buffer_pos = itoa(GetStringId(entry->name()), buffer, buffer_pos); | 3717 buffer_pos = itoa(GetStringId(entry->name()), buffer, buffer_pos); |
3668 buffer[buffer_pos++] = ','; | 3718 buffer[buffer_pos++] = ','; |
3669 buffer_pos = itoa(entry->id(), buffer, buffer_pos); | 3719 buffer_pos = itoa(entry->id(), buffer, buffer_pos); |
3670 buffer[buffer_pos++] = ','; | 3720 buffer[buffer_pos++] = ','; |
3671 buffer_pos = itoa(entry->self_size(), buffer, buffer_pos); | 3721 buffer_pos = itoa(entry->self_size(), buffer, buffer_pos); |
3672 buffer[buffer_pos++] = ','; | 3722 buffer[buffer_pos++] = ','; |
3673 buffer_pos = itoa(entry->retained_size(), buffer, buffer_pos); | 3723 buffer_pos = itoa(entry->retained_size(), buffer, buffer_pos); |
3674 buffer[buffer_pos++] = ','; | 3724 buffer[buffer_pos++] = ','; |
3675 buffer_pos = itoa(entry->dominator()->entry_index(), buffer, buffer_pos); | 3725 buffer_pos = itoa(entry->dominator()->entry_index(), buffer, buffer_pos); |
3676 buffer[buffer_pos++] = ','; | 3726 buffer[buffer_pos++] = ','; |
3677 buffer_pos = itoa(children.length(), buffer, buffer_pos); | 3727 buffer_pos = itoa(edges_index, buffer, buffer_pos); |
3678 buffer[buffer_pos++] = '\0'; | 3728 buffer[buffer_pos++] = '\0'; |
3679 writer_->AddString(buffer.start()); | 3729 writer_->AddString(buffer.start()); |
3680 for (int i = 0; i < children.length(); ++i) { | 3730 } |
3681 SerializeEdge(&children[i]); | 3731 |
| 3732 |
| 3733 void HeapSnapshotJSONSerializer::SerializeNodes(const List<HeapEntry*>& nodes) { |
| 3734 const int edge_fields_count = 3; // type,name|index,to_node. |
| 3735 int edges_index = 0; |
| 3736 for (int i = 0; i < nodes.length(); ++i) { |
| 3737 HeapEntry* entry = nodes[i]; |
| 3738 SerializeNode(entry, edges_index); |
| 3739 edges_index += entry->children().length() * edge_fields_count; |
3682 if (writer_->aborted()) return; | 3740 if (writer_->aborted()) return; |
3683 } | 3741 } |
3684 } | 3742 } |
3685 | |
3686 | |
3687 void HeapSnapshotJSONSerializer::SerializeNodes() { | |
3688 // The first (zero) item of nodes array is an object describing node | |
3689 // serialization layout. We use a set of macros to improve | |
3690 // readability. | |
3691 #define JSON_A(s) "["s"]" | |
3692 #define JSON_O(s) "{"s"}" | |
3693 #define JSON_S(s) "\""s"\"" | |
3694 writer_->AddString(JSON_O( | |
3695 JSON_S("fields") ":" JSON_A( | |
3696 JSON_S("type") | |
3697 "," JSON_S("name") | |
3698 "," JSON_S("id") | |
3699 "," JSON_S("self_size") | |
3700 "," JSON_S("retained_size") | |
3701 "," JSON_S("dominator") | |
3702 "," JSON_S("children_count") | |
3703 "," JSON_S("children")) | |
3704 "," JSON_S("types") ":" JSON_A( | |
3705 JSON_A( | |
3706 JSON_S("hidden") | |
3707 "," JSON_S("array") | |
3708 "," JSON_S("string") | |
3709 "," JSON_S("object") | |
3710 "," JSON_S("code") | |
3711 "," JSON_S("closure") | |
3712 "," JSON_S("regexp") | |
3713 "," JSON_S("number") | |
3714 "," JSON_S("native") | |
3715 "," JSON_S("synthetic")) | |
3716 "," JSON_S("string") | |
3717 "," JSON_S("number") | |
3718 "," JSON_S("number") | |
3719 "," JSON_S("number") | |
3720 "," JSON_S("number") | |
3721 "," JSON_S("number") | |
3722 "," JSON_O( | |
3723 JSON_S("fields") ":" JSON_A( | |
3724 JSON_S("type") | |
3725 "," JSON_S("name_or_index") | |
3726 "," JSON_S("to_node")) | |
3727 "," JSON_S("types") ":" JSON_A( | |
3728 JSON_A( | |
3729 JSON_S("context") | |
3730 "," JSON_S("element") | |
3731 "," JSON_S("property") | |
3732 "," JSON_S("internal") | |
3733 "," JSON_S("hidden") | |
3734 "," JSON_S("shortcut") | |
3735 "," JSON_S("weak")) | |
3736 "," JSON_S("string_or_number") | |
3737 "," JSON_S("node")))))); | |
3738 #undef JSON_S | |
3739 #undef JSON_O | |
3740 #undef JSON_A | |
3741 | |
3742 const int node_fields_count = 7; | |
3743 // type,name,id,self_size,retained_size,dominator,children_count. | |
3744 const int edge_fields_count = 3; // type,name|index,to_node. | |
3745 | |
3746 List<HeapEntry*>& nodes = *(snapshot_->entries()); | |
3747 // Root must be the first. | |
3748 ASSERT(nodes.first() == snapshot_->root()); | |
3749 // Rewrite node indexes, so they refer to actual array positions. Do this | |
3750 // only once. | |
3751 if (nodes[0]->entry_index() == -1) { | |
3752 // Nodes start from array index 1. | |
3753 int index = 1; | |
3754 for (int i = 0; i < nodes.length(); ++i) { | |
3755 HeapEntry* node = nodes[i]; | |
3756 node->set_entry_index(index); | |
3757 index += node_fields_count + | |
3758 node->children().length() * edge_fields_count; | |
3759 } | |
3760 } | |
3761 | |
3762 for (int i = 0; i < nodes.length(); ++i) { | |
3763 SerializeNode(nodes[i]); | |
3764 if (writer_->aborted()) return; | |
3765 } | |
3766 } | |
3767 | 3743 |
3768 | 3744 |
3769 void HeapSnapshotJSONSerializer::SerializeSnapshot() { | 3745 void HeapSnapshotJSONSerializer::SerializeSnapshot() { |
3770 writer_->AddString("\"title\":\""); | 3746 writer_->AddString("\"title\":\""); |
3771 writer_->AddString(snapshot_->title()); | 3747 writer_->AddString(snapshot_->title()); |
3772 writer_->AddString("\""); | 3748 writer_->AddString("\""); |
3773 writer_->AddString(",\"uid\":"); | 3749 writer_->AddString(",\"uid\":"); |
3774 writer_->AddNumber(snapshot_->uid()); | 3750 writer_->AddNumber(snapshot_->uid()); |
| 3751 writer_->AddString(",\"meta\":"); |
| 3752 // The object describing node serialization layout. |
| 3753 // We use a set of macros to improve readability. |
| 3754 #define JSON_A(s) "["s"]" |
| 3755 #define JSON_O(s) "{"s"}" |
| 3756 #define JSON_S(s) "\""s"\"" |
| 3757 writer_->AddString(JSON_O( |
| 3758 JSON_S("node_fields") ":" JSON_A( |
| 3759 JSON_S("type") "," |
| 3760 JSON_S("name") "," |
| 3761 JSON_S("id") "," |
| 3762 JSON_S("self_size") "," |
| 3763 JSON_S("retained_size") "," |
| 3764 JSON_S("dominator") "," |
| 3765 JSON_S("edges_index")) "," |
| 3766 JSON_S("node_types") ":" JSON_A( |
| 3767 JSON_A( |
| 3768 JSON_S("hidden") "," |
| 3769 JSON_S("array") "," |
| 3770 JSON_S("string") "," |
| 3771 JSON_S("object") "," |
| 3772 JSON_S("code") "," |
| 3773 JSON_S("closure") "," |
| 3774 JSON_S("regexp") "," |
| 3775 JSON_S("number") "," |
| 3776 JSON_S("native") "," |
| 3777 JSON_S("synthetic")) "," |
| 3778 JSON_S("string") "," |
| 3779 JSON_S("number") "," |
| 3780 JSON_S("number") "," |
| 3781 JSON_S("number") "," |
| 3782 JSON_S("number") "," |
| 3783 JSON_S("number")) "," |
| 3784 JSON_S("edge_fields") ":" JSON_A( |
| 3785 JSON_S("type") "," |
| 3786 JSON_S("name_or_index") "," |
| 3787 JSON_S("to_node")) "," |
| 3788 JSON_S("edge_types") ":" JSON_A( |
| 3789 JSON_A( |
| 3790 JSON_S("context") "," |
| 3791 JSON_S("element") "," |
| 3792 JSON_S("property") "," |
| 3793 JSON_S("internal") "," |
| 3794 JSON_S("hidden") "," |
| 3795 JSON_S("shortcut") "," |
| 3796 JSON_S("weak")) "," |
| 3797 JSON_S("string_or_number") "," |
| 3798 JSON_S("node")))); |
| 3799 #undef JSON_S |
| 3800 #undef JSON_O |
| 3801 #undef JSON_A |
| 3802 writer_->AddString(",\"node_count\":"); |
| 3803 writer_->AddNumber(snapshot_->entries()->length()); |
| 3804 writer_->AddString(",\"edge_count\":"); |
| 3805 writer_->AddNumber(snapshot_->number_of_edges()); |
3775 } | 3806 } |
3776 | 3807 |
3777 | 3808 |
3778 static void WriteUChar(OutputStreamWriter* w, unibrow::uchar u) { | 3809 static void WriteUChar(OutputStreamWriter* w, unibrow::uchar u) { |
3779 static const char hex_chars[] = "0123456789ABCDEF"; | 3810 static const char hex_chars[] = "0123456789ABCDEF"; |
3780 w->AddString("\\u"); | 3811 w->AddString("\\u"); |
3781 w->AddCharacter(hex_chars[(u >> 12) & 0xf]); | 3812 w->AddCharacter(hex_chars[(u >> 12) & 0xf]); |
3782 w->AddCharacter(hex_chars[(u >> 8) & 0xf]); | 3813 w->AddCharacter(hex_chars[(u >> 8) & 0xf]); |
3783 w->AddCharacter(hex_chars[(u >> 4) & 0xf]); | 3814 w->AddCharacter(hex_chars[(u >> 4) & 0xf]); |
3784 w->AddCharacter(hex_chars[u & 0xf]); | 3815 w->AddCharacter(hex_chars[u & 0xf]); |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3862 | 3893 |
3863 | 3894 |
3864 void HeapSnapshotJSONSerializer::SortHashMap( | 3895 void HeapSnapshotJSONSerializer::SortHashMap( |
3865 HashMap* map, List<HashMap::Entry*>* sorted_entries) { | 3896 HashMap* map, List<HashMap::Entry*>* sorted_entries) { |
3866 for (HashMap::Entry* p = map->Start(); p != NULL; p = map->Next(p)) | 3897 for (HashMap::Entry* p = map->Start(); p != NULL; p = map->Next(p)) |
3867 sorted_entries->Add(p); | 3898 sorted_entries->Add(p); |
3868 sorted_entries->Sort(SortUsingEntryValue); | 3899 sorted_entries->Sort(SortUsingEntryValue); |
3869 } | 3900 } |
3870 | 3901 |
3871 } } // namespace v8::internal | 3902 } } // namespace v8::internal |
OLD | NEW |