| 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 12 matching lines...) Expand all Loading... |
| 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | 27 |
| 28 #include "v8.h" | 28 #include "v8.h" |
| 29 | 29 |
| 30 #include "profile-generator-inl.h" | 30 #include "profile-generator-inl.h" |
| 31 | 31 |
| 32 #include "global-handles.h" | 32 #include "global-handles.h" |
| 33 #include "heap-profiler.h" | |
| 34 #include "scopeinfo.h" | 33 #include "scopeinfo.h" |
| 35 #include "unicode.h" | 34 #include "unicode.h" |
| 36 #include "zone-inl.h" | 35 #include "zone-inl.h" |
| 37 #include "debug.h" | 36 #include "debug.h" |
| 38 | 37 |
| 39 namespace v8 { | 38 namespace v8 { |
| 40 namespace internal { | 39 namespace internal { |
| 41 | 40 |
| 42 | 41 |
| 43 TokenEnumerator::TokenEnumerator() | 42 TokenEnumerator::TokenEnumerator() |
| (...skipping 892 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 936 // If no frames were symbolized, put the VM state entry in. | 935 // If no frames were symbolized, put the VM state entry in. |
| 937 if (no_symbolized_entries) { | 936 if (no_symbolized_entries) { |
| 938 *entry++ = EntryForVMState(sample.state); | 937 *entry++ = EntryForVMState(sample.state); |
| 939 } | 938 } |
| 940 } | 939 } |
| 941 | 940 |
| 942 profiles_->AddPathToCurrentProfiles(entries); | 941 profiles_->AddPathToCurrentProfiles(entries); |
| 943 } | 942 } |
| 944 | 943 |
| 945 | 944 |
| 946 HeapGraphEdge::HeapGraphEdge(Type type, const char* name, int from, int to) | |
| 947 : type_(type), | |
| 948 from_index_(from), | |
| 949 to_index_(to), | |
| 950 name_(name) { | |
| 951 ASSERT(type == kContextVariable | |
| 952 || type == kProperty | |
| 953 || type == kInternal | |
| 954 || type == kShortcut); | |
| 955 } | |
| 956 | |
| 957 | |
| 958 HeapGraphEdge::HeapGraphEdge(Type type, int index, int from, int to) | |
| 959 : type_(type), | |
| 960 from_index_(from), | |
| 961 to_index_(to), | |
| 962 index_(index) { | |
| 963 ASSERT(type == kElement || type == kHidden || type == kWeak); | |
| 964 } | |
| 965 | |
| 966 | |
| 967 void HeapGraphEdge::ReplaceToIndexWithEntry(HeapSnapshot* snapshot) { | |
| 968 to_entry_ = &snapshot->entries()[to_index_]; | |
| 969 } | |
| 970 | |
| 971 | |
| 972 const int HeapEntry::kNoEntry = -1; | |
| 973 | |
| 974 HeapEntry::HeapEntry(HeapSnapshot* snapshot, | |
| 975 Type type, | |
| 976 const char* name, | |
| 977 SnapshotObjectId id, | |
| 978 int self_size) | |
| 979 : type_(type), | |
| 980 children_count_(0), | |
| 981 children_index_(-1), | |
| 982 self_size_(self_size), | |
| 983 id_(id), | |
| 984 snapshot_(snapshot), | |
| 985 name_(name) { } | |
| 986 | |
| 987 | |
| 988 void HeapEntry::SetNamedReference(HeapGraphEdge::Type type, | |
| 989 const char* name, | |
| 990 HeapEntry* entry) { | |
| 991 HeapGraphEdge edge(type, name, this->index(), entry->index()); | |
| 992 snapshot_->edges().Add(edge); | |
| 993 ++children_count_; | |
| 994 } | |
| 995 | |
| 996 | |
| 997 void HeapEntry::SetIndexedReference(HeapGraphEdge::Type type, | |
| 998 int index, | |
| 999 HeapEntry* entry) { | |
| 1000 HeapGraphEdge edge(type, index, this->index(), entry->index()); | |
| 1001 snapshot_->edges().Add(edge); | |
| 1002 ++children_count_; | |
| 1003 } | |
| 1004 | |
| 1005 | |
| 1006 Handle<HeapObject> HeapEntry::GetHeapObject() { | |
| 1007 return snapshot_->collection()->FindHeapObjectById(id()); | |
| 1008 } | |
| 1009 | |
| 1010 | |
| 1011 void HeapEntry::Print( | |
| 1012 const char* prefix, const char* edge_name, int max_depth, int indent) { | |
| 1013 STATIC_CHECK(sizeof(unsigned) == sizeof(id())); | |
| 1014 OS::Print("%6d @%6u %*c %s%s: ", | |
| 1015 self_size(), id(), indent, ' ', prefix, edge_name); | |
| 1016 if (type() != kString) { | |
| 1017 OS::Print("%s %.40s\n", TypeAsString(), name_); | |
| 1018 } else { | |
| 1019 OS::Print("\""); | |
| 1020 const char* c = name_; | |
| 1021 while (*c && (c - name_) <= 40) { | |
| 1022 if (*c != '\n') | |
| 1023 OS::Print("%c", *c); | |
| 1024 else | |
| 1025 OS::Print("\\n"); | |
| 1026 ++c; | |
| 1027 } | |
| 1028 OS::Print("\"\n"); | |
| 1029 } | |
| 1030 if (--max_depth == 0) return; | |
| 1031 Vector<HeapGraphEdge*> ch = children(); | |
| 1032 for (int i = 0; i < ch.length(); ++i) { | |
| 1033 HeapGraphEdge& edge = *ch[i]; | |
| 1034 const char* edge_prefix = ""; | |
| 1035 EmbeddedVector<char, 64> index; | |
| 1036 const char* edge_name = index.start(); | |
| 1037 switch (edge.type()) { | |
| 1038 case HeapGraphEdge::kContextVariable: | |
| 1039 edge_prefix = "#"; | |
| 1040 edge_name = edge.name(); | |
| 1041 break; | |
| 1042 case HeapGraphEdge::kElement: | |
| 1043 OS::SNPrintF(index, "%d", edge.index()); | |
| 1044 break; | |
| 1045 case HeapGraphEdge::kInternal: | |
| 1046 edge_prefix = "$"; | |
| 1047 edge_name = edge.name(); | |
| 1048 break; | |
| 1049 case HeapGraphEdge::kProperty: | |
| 1050 edge_name = edge.name(); | |
| 1051 break; | |
| 1052 case HeapGraphEdge::kHidden: | |
| 1053 edge_prefix = "$"; | |
| 1054 OS::SNPrintF(index, "%d", edge.index()); | |
| 1055 break; | |
| 1056 case HeapGraphEdge::kShortcut: | |
| 1057 edge_prefix = "^"; | |
| 1058 edge_name = edge.name(); | |
| 1059 break; | |
| 1060 case HeapGraphEdge::kWeak: | |
| 1061 edge_prefix = "w"; | |
| 1062 OS::SNPrintF(index, "%d", edge.index()); | |
| 1063 break; | |
| 1064 default: | |
| 1065 OS::SNPrintF(index, "!!! unknown edge type: %d ", edge.type()); | |
| 1066 } | |
| 1067 edge.to()->Print(edge_prefix, edge_name, max_depth, indent + 2); | |
| 1068 } | |
| 1069 } | |
| 1070 | |
| 1071 | |
| 1072 const char* HeapEntry::TypeAsString() { | |
| 1073 switch (type()) { | |
| 1074 case kHidden: return "/hidden/"; | |
| 1075 case kObject: return "/object/"; | |
| 1076 case kClosure: return "/closure/"; | |
| 1077 case kString: return "/string/"; | |
| 1078 case kCode: return "/code/"; | |
| 1079 case kArray: return "/array/"; | |
| 1080 case kRegExp: return "/regexp/"; | |
| 1081 case kHeapNumber: return "/number/"; | |
| 1082 case kNative: return "/native/"; | |
| 1083 case kSynthetic: return "/synthetic/"; | |
| 1084 default: return "???"; | |
| 1085 } | |
| 1086 } | |
| 1087 | |
| 1088 | |
| 1089 // It is very important to keep objects that form a heap snapshot | |
| 1090 // as small as possible. | |
| 1091 namespace { // Avoid littering the global namespace. | |
| 1092 | |
| 1093 template <size_t ptr_size> struct SnapshotSizeConstants; | |
| 1094 | |
| 1095 template <> struct SnapshotSizeConstants<4> { | |
| 1096 static const int kExpectedHeapGraphEdgeSize = 12; | |
| 1097 static const int kExpectedHeapEntrySize = 24; | |
| 1098 static const int kExpectedHeapSnapshotsCollectionSize = 100; | |
| 1099 static const int kExpectedHeapSnapshotSize = 136; | |
| 1100 static const size_t kMaxSerializableSnapshotRawSize = 256 * MB; | |
| 1101 }; | |
| 1102 | |
| 1103 template <> struct SnapshotSizeConstants<8> { | |
| 1104 static const int kExpectedHeapGraphEdgeSize = 24; | |
| 1105 static const int kExpectedHeapEntrySize = 32; | |
| 1106 static const int kExpectedHeapSnapshotsCollectionSize = 152; | |
| 1107 static const int kExpectedHeapSnapshotSize = 168; | |
| 1108 static const uint64_t kMaxSerializableSnapshotRawSize = | |
| 1109 static_cast<uint64_t>(6000) * MB; | |
| 1110 }; | |
| 1111 | |
| 1112 } // namespace | |
| 1113 | |
| 1114 HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, | |
| 1115 HeapSnapshot::Type type, | |
| 1116 const char* title, | |
| 1117 unsigned uid) | |
| 1118 : collection_(collection), | |
| 1119 type_(type), | |
| 1120 title_(title), | |
| 1121 uid_(uid), | |
| 1122 root_index_(HeapEntry::kNoEntry), | |
| 1123 gc_roots_index_(HeapEntry::kNoEntry), | |
| 1124 natives_root_index_(HeapEntry::kNoEntry), | |
| 1125 max_snapshot_js_object_id_(0) { | |
| 1126 STATIC_CHECK( | |
| 1127 sizeof(HeapGraphEdge) == | |
| 1128 SnapshotSizeConstants<kPointerSize>::kExpectedHeapGraphEdgeSize); | |
| 1129 STATIC_CHECK( | |
| 1130 sizeof(HeapEntry) == | |
| 1131 SnapshotSizeConstants<kPointerSize>::kExpectedHeapEntrySize); | |
| 1132 for (int i = 0; i < VisitorSynchronization::kNumberOfSyncTags; ++i) { | |
| 1133 gc_subroot_indexes_[i] = HeapEntry::kNoEntry; | |
| 1134 } | |
| 1135 } | |
| 1136 | |
| 1137 | |
| 1138 void HeapSnapshot::Delete() { | |
| 1139 collection_->RemoveSnapshot(this); | |
| 1140 delete this; | |
| 1141 } | |
| 1142 | |
| 1143 | |
| 1144 void HeapSnapshot::RememberLastJSObjectId() { | |
| 1145 max_snapshot_js_object_id_ = collection_->last_assigned_id(); | |
| 1146 } | |
| 1147 | |
| 1148 | |
| 1149 HeapEntry* HeapSnapshot::AddRootEntry() { | |
| 1150 ASSERT(root_index_ == HeapEntry::kNoEntry); | |
| 1151 ASSERT(entries_.is_empty()); // Root entry must be the first one. | |
| 1152 HeapEntry* entry = AddEntry(HeapEntry::kObject, | |
| 1153 "", | |
| 1154 HeapObjectsMap::kInternalRootObjectId, | |
| 1155 0); | |
| 1156 root_index_ = entry->index(); | |
| 1157 ASSERT(root_index_ == 0); | |
| 1158 return entry; | |
| 1159 } | |
| 1160 | |
| 1161 | |
| 1162 HeapEntry* HeapSnapshot::AddGcRootsEntry() { | |
| 1163 ASSERT(gc_roots_index_ == HeapEntry::kNoEntry); | |
| 1164 HeapEntry* entry = AddEntry(HeapEntry::kObject, | |
| 1165 "(GC roots)", | |
| 1166 HeapObjectsMap::kGcRootsObjectId, | |
| 1167 0); | |
| 1168 gc_roots_index_ = entry->index(); | |
| 1169 return entry; | |
| 1170 } | |
| 1171 | |
| 1172 | |
| 1173 HeapEntry* HeapSnapshot::AddGcSubrootEntry(int tag) { | |
| 1174 ASSERT(gc_subroot_indexes_[tag] == HeapEntry::kNoEntry); | |
| 1175 ASSERT(0 <= tag && tag < VisitorSynchronization::kNumberOfSyncTags); | |
| 1176 HeapEntry* entry = AddEntry( | |
| 1177 HeapEntry::kObject, | |
| 1178 VisitorSynchronization::kTagNames[tag], | |
| 1179 HeapObjectsMap::GetNthGcSubrootId(tag), | |
| 1180 0); | |
| 1181 gc_subroot_indexes_[tag] = entry->index(); | |
| 1182 return entry; | |
| 1183 } | |
| 1184 | |
| 1185 | |
| 1186 HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type, | |
| 1187 const char* name, | |
| 1188 SnapshotObjectId id, | |
| 1189 int size) { | |
| 1190 HeapEntry entry(this, type, name, id, size); | |
| 1191 entries_.Add(entry); | |
| 1192 return &entries_.last(); | |
| 1193 } | |
| 1194 | |
| 1195 | |
| 1196 void HeapSnapshot::FillChildren() { | |
| 1197 ASSERT(children().is_empty()); | |
| 1198 children().Allocate(edges().length()); | |
| 1199 int children_index = 0; | |
| 1200 for (int i = 0; i < entries().length(); ++i) { | |
| 1201 HeapEntry* entry = &entries()[i]; | |
| 1202 children_index = entry->set_children_index(children_index); | |
| 1203 } | |
| 1204 ASSERT(edges().length() == children_index); | |
| 1205 for (int i = 0; i < edges().length(); ++i) { | |
| 1206 HeapGraphEdge* edge = &edges()[i]; | |
| 1207 edge->ReplaceToIndexWithEntry(this); | |
| 1208 edge->from()->add_child(edge); | |
| 1209 } | |
| 1210 } | |
| 1211 | |
| 1212 | |
| 1213 class FindEntryById { | |
| 1214 public: | |
| 1215 explicit FindEntryById(SnapshotObjectId id) : id_(id) { } | |
| 1216 int operator()(HeapEntry* const* entry) { | |
| 1217 if ((*entry)->id() == id_) return 0; | |
| 1218 return (*entry)->id() < id_ ? -1 : 1; | |
| 1219 } | |
| 1220 private: | |
| 1221 SnapshotObjectId id_; | |
| 1222 }; | |
| 1223 | |
| 1224 | |
| 1225 HeapEntry* HeapSnapshot::GetEntryById(SnapshotObjectId id) { | |
| 1226 List<HeapEntry*>* entries_by_id = GetSortedEntriesList(); | |
| 1227 // Perform a binary search by id. | |
| 1228 int index = SortedListBSearch(*entries_by_id, FindEntryById(id)); | |
| 1229 if (index == -1) | |
| 1230 return NULL; | |
| 1231 return entries_by_id->at(index); | |
| 1232 } | |
| 1233 | |
| 1234 | |
| 1235 template<class T> | |
| 1236 static int SortByIds(const T* entry1_ptr, | |
| 1237 const T* entry2_ptr) { | |
| 1238 if ((*entry1_ptr)->id() == (*entry2_ptr)->id()) return 0; | |
| 1239 return (*entry1_ptr)->id() < (*entry2_ptr)->id() ? -1 : 1; | |
| 1240 } | |
| 1241 | |
| 1242 | |
| 1243 List<HeapEntry*>* HeapSnapshot::GetSortedEntriesList() { | |
| 1244 if (sorted_entries_.is_empty()) { | |
| 1245 sorted_entries_.Allocate(entries_.length()); | |
| 1246 for (int i = 0; i < entries_.length(); ++i) { | |
| 1247 sorted_entries_[i] = &entries_[i]; | |
| 1248 } | |
| 1249 sorted_entries_.Sort(SortByIds); | |
| 1250 } | |
| 1251 return &sorted_entries_; | |
| 1252 } | |
| 1253 | |
| 1254 | |
| 1255 void HeapSnapshot::Print(int max_depth) { | |
| 1256 root()->Print("", "", max_depth, 0); | |
| 1257 } | |
| 1258 | |
| 1259 | |
| 1260 template<typename T, class P> | |
| 1261 static size_t GetMemoryUsedByList(const List<T, P>& list) { | |
| 1262 return list.length() * sizeof(T) + sizeof(list); | |
| 1263 } | |
| 1264 | |
| 1265 | |
| 1266 size_t HeapSnapshot::RawSnapshotSize() const { | |
| 1267 STATIC_CHECK(SnapshotSizeConstants<kPointerSize>::kExpectedHeapSnapshotSize == | |
| 1268 sizeof(HeapSnapshot)); // NOLINT | |
| 1269 return | |
| 1270 sizeof(*this) + | |
| 1271 GetMemoryUsedByList(entries_) + | |
| 1272 GetMemoryUsedByList(edges_) + | |
| 1273 GetMemoryUsedByList(children_) + | |
| 1274 GetMemoryUsedByList(sorted_entries_); | |
| 1275 } | |
| 1276 | |
| 1277 | |
| 1278 // We split IDs on evens for embedder objects (see | |
| 1279 // HeapObjectsMap::GenerateId) and odds for native objects. | |
| 1280 const SnapshotObjectId HeapObjectsMap::kInternalRootObjectId = 1; | |
| 1281 const SnapshotObjectId HeapObjectsMap::kGcRootsObjectId = | |
| 1282 HeapObjectsMap::kInternalRootObjectId + HeapObjectsMap::kObjectIdStep; | |
| 1283 const SnapshotObjectId HeapObjectsMap::kGcRootsFirstSubrootId = | |
| 1284 HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep; | |
| 1285 const SnapshotObjectId HeapObjectsMap::kFirstAvailableObjectId = | |
| 1286 HeapObjectsMap::kGcRootsFirstSubrootId + | |
| 1287 VisitorSynchronization::kNumberOfSyncTags * HeapObjectsMap::kObjectIdStep; | |
| 1288 | |
| 1289 HeapObjectsMap::HeapObjectsMap(Heap* heap) | |
| 1290 : next_id_(kFirstAvailableObjectId), | |
| 1291 entries_map_(AddressesMatch), | |
| 1292 heap_(heap) { | |
| 1293 // This dummy element solves a problem with entries_map_. | |
| 1294 // When we do lookup in HashMap we see no difference between two cases: | |
| 1295 // it has an entry with NULL as the value or it has created | |
| 1296 // a new entry on the fly with NULL as the default value. | |
| 1297 // With such dummy element we have a guaranty that all entries_map_ entries | |
| 1298 // will have the value field grater than 0. | |
| 1299 // This fact is using in MoveObject method. | |
| 1300 entries_.Add(EntryInfo(0, NULL, 0)); | |
| 1301 } | |
| 1302 | |
| 1303 | |
| 1304 void HeapObjectsMap::SnapshotGenerationFinished() { | |
| 1305 RemoveDeadEntries(); | |
| 1306 } | |
| 1307 | |
| 1308 | |
| 1309 void HeapObjectsMap::MoveObject(Address from, Address to) { | |
| 1310 ASSERT(to != NULL); | |
| 1311 ASSERT(from != NULL); | |
| 1312 if (from == to) return; | |
| 1313 void* from_value = entries_map_.Remove(from, AddressHash(from)); | |
| 1314 if (from_value == NULL) return; | |
| 1315 int from_entry_info_index = | |
| 1316 static_cast<int>(reinterpret_cast<intptr_t>(from_value)); | |
| 1317 entries_.at(from_entry_info_index).addr = to; | |
| 1318 HashMap::Entry* to_entry = entries_map_.Lookup(to, AddressHash(to), true); | |
| 1319 if (to_entry->value != NULL) { | |
| 1320 int to_entry_info_index = | |
| 1321 static_cast<int>(reinterpret_cast<intptr_t>(to_entry->value)); | |
| 1322 // Without this operation we will have two EntryInfo's with the same | |
| 1323 // value in addr field. It is bad because later at RemoveDeadEntries | |
| 1324 // one of this entry will be removed with the corresponding entries_map_ | |
| 1325 // entry. | |
| 1326 entries_.at(to_entry_info_index).addr = NULL; | |
| 1327 } | |
| 1328 to_entry->value = reinterpret_cast<void*>(from_entry_info_index); | |
| 1329 } | |
| 1330 | |
| 1331 | |
| 1332 SnapshotObjectId HeapObjectsMap::FindEntry(Address addr) { | |
| 1333 HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), false); | |
| 1334 if (entry == NULL) return 0; | |
| 1335 int entry_index = static_cast<int>(reinterpret_cast<intptr_t>(entry->value)); | |
| 1336 EntryInfo& entry_info = entries_.at(entry_index); | |
| 1337 ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy()); | |
| 1338 return entry_info.id; | |
| 1339 } | |
| 1340 | |
| 1341 | |
| 1342 SnapshotObjectId HeapObjectsMap::FindOrAddEntry(Address addr, | |
| 1343 unsigned int size) { | |
| 1344 ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy()); | |
| 1345 HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), true); | |
| 1346 if (entry->value != NULL) { | |
| 1347 int entry_index = | |
| 1348 static_cast<int>(reinterpret_cast<intptr_t>(entry->value)); | |
| 1349 EntryInfo& entry_info = entries_.at(entry_index); | |
| 1350 entry_info.accessed = true; | |
| 1351 entry_info.size = size; | |
| 1352 return entry_info.id; | |
| 1353 } | |
| 1354 entry->value = reinterpret_cast<void*>(entries_.length()); | |
| 1355 SnapshotObjectId id = next_id_; | |
| 1356 next_id_ += kObjectIdStep; | |
| 1357 entries_.Add(EntryInfo(id, addr, size)); | |
| 1358 ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy()); | |
| 1359 return id; | |
| 1360 } | |
| 1361 | |
| 1362 | |
| 1363 void HeapObjectsMap::StopHeapObjectsTracking() { | |
| 1364 time_intervals_.Clear(); | |
| 1365 } | |
| 1366 | |
| 1367 void HeapObjectsMap::UpdateHeapObjectsMap() { | |
| 1368 HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask, | |
| 1369 "HeapSnapshotsCollection::UpdateHeapObjectsMap"); | |
| 1370 HeapIterator iterator(heap_); | |
| 1371 for (HeapObject* obj = iterator.next(); | |
| 1372 obj != NULL; | |
| 1373 obj = iterator.next()) { | |
| 1374 FindOrAddEntry(obj->address(), obj->Size()); | |
| 1375 } | |
| 1376 RemoveDeadEntries(); | |
| 1377 } | |
| 1378 | |
| 1379 | |
| 1380 SnapshotObjectId HeapObjectsMap::PushHeapObjectsStats(OutputStream* stream) { | |
| 1381 UpdateHeapObjectsMap(); | |
| 1382 time_intervals_.Add(TimeInterval(next_id_)); | |
| 1383 int prefered_chunk_size = stream->GetChunkSize(); | |
| 1384 List<v8::HeapStatsUpdate> stats_buffer; | |
| 1385 ASSERT(!entries_.is_empty()); | |
| 1386 EntryInfo* entry_info = &entries_.first(); | |
| 1387 EntryInfo* end_entry_info = &entries_.last() + 1; | |
| 1388 for (int time_interval_index = 0; | |
| 1389 time_interval_index < time_intervals_.length(); | |
| 1390 ++time_interval_index) { | |
| 1391 TimeInterval& time_interval = time_intervals_[time_interval_index]; | |
| 1392 SnapshotObjectId time_interval_id = time_interval.id; | |
| 1393 uint32_t entries_size = 0; | |
| 1394 EntryInfo* start_entry_info = entry_info; | |
| 1395 while (entry_info < end_entry_info && entry_info->id < time_interval_id) { | |
| 1396 entries_size += entry_info->size; | |
| 1397 ++entry_info; | |
| 1398 } | |
| 1399 uint32_t entries_count = | |
| 1400 static_cast<uint32_t>(entry_info - start_entry_info); | |
| 1401 if (time_interval.count != entries_count || | |
| 1402 time_interval.size != entries_size) { | |
| 1403 stats_buffer.Add(v8::HeapStatsUpdate( | |
| 1404 time_interval_index, | |
| 1405 time_interval.count = entries_count, | |
| 1406 time_interval.size = entries_size)); | |
| 1407 if (stats_buffer.length() >= prefered_chunk_size) { | |
| 1408 OutputStream::WriteResult result = stream->WriteHeapStatsChunk( | |
| 1409 &stats_buffer.first(), stats_buffer.length()); | |
| 1410 if (result == OutputStream::kAbort) return last_assigned_id(); | |
| 1411 stats_buffer.Clear(); | |
| 1412 } | |
| 1413 } | |
| 1414 } | |
| 1415 ASSERT(entry_info == end_entry_info); | |
| 1416 if (!stats_buffer.is_empty()) { | |
| 1417 OutputStream::WriteResult result = stream->WriteHeapStatsChunk( | |
| 1418 &stats_buffer.first(), stats_buffer.length()); | |
| 1419 if (result == OutputStream::kAbort) return last_assigned_id(); | |
| 1420 } | |
| 1421 stream->EndOfStream(); | |
| 1422 return last_assigned_id(); | |
| 1423 } | |
| 1424 | |
| 1425 | |
| 1426 void HeapObjectsMap::RemoveDeadEntries() { | |
| 1427 ASSERT(entries_.length() > 0 && | |
| 1428 entries_.at(0).id == 0 && | |
| 1429 entries_.at(0).addr == NULL); | |
| 1430 int first_free_entry = 1; | |
| 1431 for (int i = 1; i < entries_.length(); ++i) { | |
| 1432 EntryInfo& entry_info = entries_.at(i); | |
| 1433 if (entry_info.accessed) { | |
| 1434 if (first_free_entry != i) { | |
| 1435 entries_.at(first_free_entry) = entry_info; | |
| 1436 } | |
| 1437 entries_.at(first_free_entry).accessed = false; | |
| 1438 HashMap::Entry* entry = entries_map_.Lookup( | |
| 1439 entry_info.addr, AddressHash(entry_info.addr), false); | |
| 1440 ASSERT(entry); | |
| 1441 entry->value = reinterpret_cast<void*>(first_free_entry); | |
| 1442 ++first_free_entry; | |
| 1443 } else { | |
| 1444 if (entry_info.addr) { | |
| 1445 entries_map_.Remove(entry_info.addr, AddressHash(entry_info.addr)); | |
| 1446 } | |
| 1447 } | |
| 1448 } | |
| 1449 entries_.Rewind(first_free_entry); | |
| 1450 ASSERT(static_cast<uint32_t>(entries_.length()) - 1 == | |
| 1451 entries_map_.occupancy()); | |
| 1452 } | |
| 1453 | |
| 1454 | |
| 1455 SnapshotObjectId HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) { | |
| 1456 SnapshotObjectId id = static_cast<SnapshotObjectId>(info->GetHash()); | |
| 1457 const char* label = info->GetLabel(); | |
| 1458 id ^= StringHasher::HashSequentialString(label, | |
| 1459 static_cast<int>(strlen(label)), | |
| 1460 HEAP->HashSeed()); | |
| 1461 intptr_t element_count = info->GetElementCount(); | |
| 1462 if (element_count != -1) | |
| 1463 id ^= ComputeIntegerHash(static_cast<uint32_t>(element_count), | |
| 1464 v8::internal::kZeroHashSeed); | |
| 1465 return id << 1; | |
| 1466 } | |
| 1467 | |
| 1468 | |
| 1469 size_t HeapObjectsMap::GetUsedMemorySize() const { | |
| 1470 return | |
| 1471 sizeof(*this) + | |
| 1472 sizeof(HashMap::Entry) * entries_map_.capacity() + | |
| 1473 GetMemoryUsedByList(entries_) + | |
| 1474 GetMemoryUsedByList(time_intervals_); | |
| 1475 } | |
| 1476 | |
| 1477 | |
| 1478 HeapSnapshotsCollection::HeapSnapshotsCollection(Heap* heap) | |
| 1479 : is_tracking_objects_(false), | |
| 1480 snapshots_uids_(HeapSnapshotsMatch), | |
| 1481 token_enumerator_(new TokenEnumerator()), | |
| 1482 ids_(heap) { | |
| 1483 } | |
| 1484 | |
| 1485 | |
| 1486 static void DeleteHeapSnapshot(HeapSnapshot** snapshot_ptr) { | |
| 1487 delete *snapshot_ptr; | |
| 1488 } | |
| 1489 | |
| 1490 | |
| 1491 HeapSnapshotsCollection::~HeapSnapshotsCollection() { | |
| 1492 delete token_enumerator_; | |
| 1493 snapshots_.Iterate(DeleteHeapSnapshot); | |
| 1494 } | |
| 1495 | |
| 1496 | |
| 1497 HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(HeapSnapshot::Type type, | |
| 1498 const char* name, | |
| 1499 unsigned uid) { | |
| 1500 is_tracking_objects_ = true; // Start watching for heap objects moves. | |
| 1501 return new HeapSnapshot(this, type, name, uid); | |
| 1502 } | |
| 1503 | |
| 1504 | |
| 1505 void HeapSnapshotsCollection::SnapshotGenerationFinished( | |
| 1506 HeapSnapshot* snapshot) { | |
| 1507 ids_.SnapshotGenerationFinished(); | |
| 1508 if (snapshot != NULL) { | |
| 1509 snapshots_.Add(snapshot); | |
| 1510 HashMap::Entry* entry = | |
| 1511 snapshots_uids_.Lookup(reinterpret_cast<void*>(snapshot->uid()), | |
| 1512 static_cast<uint32_t>(snapshot->uid()), | |
| 1513 true); | |
| 1514 ASSERT(entry->value == NULL); | |
| 1515 entry->value = snapshot; | |
| 1516 } | |
| 1517 } | |
| 1518 | |
| 1519 | |
| 1520 HeapSnapshot* HeapSnapshotsCollection::GetSnapshot(unsigned uid) { | |
| 1521 HashMap::Entry* entry = snapshots_uids_.Lookup(reinterpret_cast<void*>(uid), | |
| 1522 static_cast<uint32_t>(uid), | |
| 1523 false); | |
| 1524 return entry != NULL ? reinterpret_cast<HeapSnapshot*>(entry->value) : NULL; | |
| 1525 } | |
| 1526 | |
| 1527 | |
| 1528 void HeapSnapshotsCollection::RemoveSnapshot(HeapSnapshot* snapshot) { | |
| 1529 snapshots_.RemoveElement(snapshot); | |
| 1530 unsigned uid = snapshot->uid(); | |
| 1531 snapshots_uids_.Remove(reinterpret_cast<void*>(uid), | |
| 1532 static_cast<uint32_t>(uid)); | |
| 1533 } | |
| 1534 | |
| 1535 | |
| 1536 Handle<HeapObject> HeapSnapshotsCollection::FindHeapObjectById( | |
| 1537 SnapshotObjectId id) { | |
| 1538 // First perform a full GC in order to avoid dead objects. | |
| 1539 HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask, | |
| 1540 "HeapSnapshotsCollection::FindHeapObjectById"); | |
| 1541 AssertNoAllocation no_allocation; | |
| 1542 HeapObject* object = NULL; | |
| 1543 HeapIterator iterator(heap(), HeapIterator::kFilterUnreachable); | |
| 1544 // Make sure that object with the given id is still reachable. | |
| 1545 for (HeapObject* obj = iterator.next(); | |
| 1546 obj != NULL; | |
| 1547 obj = iterator.next()) { | |
| 1548 if (ids_.FindEntry(obj->address()) == id) { | |
| 1549 ASSERT(object == NULL); | |
| 1550 object = obj; | |
| 1551 // Can't break -- kFilterUnreachable requires full heap traversal. | |
| 1552 } | |
| 1553 } | |
| 1554 return object != NULL ? Handle<HeapObject>(object) : Handle<HeapObject>(); | |
| 1555 } | |
| 1556 | |
| 1557 | |
| 1558 size_t HeapSnapshotsCollection::GetUsedMemorySize() const { | |
| 1559 STATIC_CHECK(SnapshotSizeConstants<kPointerSize>:: | |
| 1560 kExpectedHeapSnapshotsCollectionSize == | |
| 1561 sizeof(HeapSnapshotsCollection)); // NOLINT | |
| 1562 size_t size = sizeof(*this); | |
| 1563 size += names_.GetUsedMemorySize(); | |
| 1564 size += ids_.GetUsedMemorySize(); | |
| 1565 size += sizeof(HashMap::Entry) * snapshots_uids_.capacity(); | |
| 1566 size += GetMemoryUsedByList(snapshots_); | |
| 1567 for (int i = 0; i < snapshots_.length(); ++i) { | |
| 1568 size += snapshots_[i]->RawSnapshotSize(); | |
| 1569 } | |
| 1570 return size; | |
| 1571 } | |
| 1572 | |
| 1573 | |
| 1574 HeapEntriesMap::HeapEntriesMap() | |
| 1575 : entries_(HeapThingsMatch) { | |
| 1576 } | |
| 1577 | |
| 1578 | |
| 1579 int HeapEntriesMap::Map(HeapThing thing) { | |
| 1580 HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), false); | |
| 1581 if (cache_entry == NULL) return HeapEntry::kNoEntry; | |
| 1582 return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value)); | |
| 1583 } | |
| 1584 | |
| 1585 | |
| 1586 void HeapEntriesMap::Pair(HeapThing thing, int entry) { | |
| 1587 HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), true); | |
| 1588 ASSERT(cache_entry->value == NULL); | |
| 1589 cache_entry->value = reinterpret_cast<void*>(static_cast<intptr_t>(entry)); | |
| 1590 } | |
| 1591 | |
| 1592 | |
| 1593 HeapObjectsSet::HeapObjectsSet() | |
| 1594 : entries_(HeapEntriesMap::HeapThingsMatch) { | |
| 1595 } | |
| 1596 | |
| 1597 | |
| 1598 void HeapObjectsSet::Clear() { | |
| 1599 entries_.Clear(); | |
| 1600 } | |
| 1601 | |
| 1602 | |
| 1603 bool HeapObjectsSet::Contains(Object* obj) { | |
| 1604 if (!obj->IsHeapObject()) return false; | |
| 1605 HeapObject* object = HeapObject::cast(obj); | |
| 1606 return entries_.Lookup(object, HeapEntriesMap::Hash(object), false) != NULL; | |
| 1607 } | |
| 1608 | |
| 1609 | |
| 1610 void HeapObjectsSet::Insert(Object* obj) { | |
| 1611 if (!obj->IsHeapObject()) return; | |
| 1612 HeapObject* object = HeapObject::cast(obj); | |
| 1613 entries_.Lookup(object, HeapEntriesMap::Hash(object), true); | |
| 1614 } | |
| 1615 | |
| 1616 | |
| 1617 const char* HeapObjectsSet::GetTag(Object* obj) { | |
| 1618 HeapObject* object = HeapObject::cast(obj); | |
| 1619 HashMap::Entry* cache_entry = | |
| 1620 entries_.Lookup(object, HeapEntriesMap::Hash(object), false); | |
| 1621 return cache_entry != NULL | |
| 1622 ? reinterpret_cast<const char*>(cache_entry->value) | |
| 1623 : NULL; | |
| 1624 } | |
| 1625 | |
| 1626 | |
| 1627 void HeapObjectsSet::SetTag(Object* obj, const char* tag) { | |
| 1628 if (!obj->IsHeapObject()) return; | |
| 1629 HeapObject* object = HeapObject::cast(obj); | |
| 1630 HashMap::Entry* cache_entry = | |
| 1631 entries_.Lookup(object, HeapEntriesMap::Hash(object), true); | |
| 1632 cache_entry->value = const_cast<char*>(tag); | |
| 1633 } | |
| 1634 | |
| 1635 | |
| 1636 HeapObject* const V8HeapExplorer::kInternalRootObject = | |
| 1637 reinterpret_cast<HeapObject*>( | |
| 1638 static_cast<intptr_t>(HeapObjectsMap::kInternalRootObjectId)); | |
| 1639 HeapObject* const V8HeapExplorer::kGcRootsObject = | |
| 1640 reinterpret_cast<HeapObject*>( | |
| 1641 static_cast<intptr_t>(HeapObjectsMap::kGcRootsObjectId)); | |
| 1642 HeapObject* const V8HeapExplorer::kFirstGcSubrootObject = | |
| 1643 reinterpret_cast<HeapObject*>( | |
| 1644 static_cast<intptr_t>(HeapObjectsMap::kGcRootsFirstSubrootId)); | |
| 1645 HeapObject* const V8HeapExplorer::kLastGcSubrootObject = | |
| 1646 reinterpret_cast<HeapObject*>( | |
| 1647 static_cast<intptr_t>(HeapObjectsMap::kFirstAvailableObjectId)); | |
| 1648 | |
| 1649 | |
| 1650 V8HeapExplorer::V8HeapExplorer( | |
| 1651 HeapSnapshot* snapshot, | |
| 1652 SnapshottingProgressReportingInterface* progress, | |
| 1653 v8::HeapProfiler::ObjectNameResolver* resolver) | |
| 1654 : heap_(Isolate::Current()->heap()), | |
| 1655 snapshot_(snapshot), | |
| 1656 collection_(snapshot_->collection()), | |
| 1657 progress_(progress), | |
| 1658 filler_(NULL), | |
| 1659 global_object_name_resolver_(resolver) { | |
| 1660 } | |
| 1661 | |
| 1662 | |
| 1663 V8HeapExplorer::~V8HeapExplorer() { | |
| 1664 } | |
| 1665 | |
| 1666 | |
| 1667 HeapEntry* V8HeapExplorer::AllocateEntry(HeapThing ptr) { | |
| 1668 return AddEntry(reinterpret_cast<HeapObject*>(ptr)); | |
| 1669 } | |
| 1670 | |
| 1671 | |
| 1672 HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object) { | |
| 1673 if (object == kInternalRootObject) { | |
| 1674 snapshot_->AddRootEntry(); | |
| 1675 return snapshot_->root(); | |
| 1676 } else if (object == kGcRootsObject) { | |
| 1677 HeapEntry* entry = snapshot_->AddGcRootsEntry(); | |
| 1678 return entry; | |
| 1679 } else if (object >= kFirstGcSubrootObject && object < kLastGcSubrootObject) { | |
| 1680 HeapEntry* entry = snapshot_->AddGcSubrootEntry(GetGcSubrootOrder(object)); | |
| 1681 return entry; | |
| 1682 } else if (object->IsJSFunction()) { | |
| 1683 JSFunction* func = JSFunction::cast(object); | |
| 1684 SharedFunctionInfo* shared = func->shared(); | |
| 1685 const char* name = shared->bound() ? "native_bind" : | |
| 1686 collection_->names()->GetName(String::cast(shared->name())); | |
| 1687 return AddEntry(object, HeapEntry::kClosure, name); | |
| 1688 } else if (object->IsJSRegExp()) { | |
| 1689 JSRegExp* re = JSRegExp::cast(object); | |
| 1690 return AddEntry(object, | |
| 1691 HeapEntry::kRegExp, | |
| 1692 collection_->names()->GetName(re->Pattern())); | |
| 1693 } else if (object->IsJSObject()) { | |
| 1694 const char* name = collection_->names()->GetName( | |
| 1695 GetConstructorName(JSObject::cast(object))); | |
| 1696 if (object->IsJSGlobalObject()) { | |
| 1697 const char* tag = objects_tags_.GetTag(object); | |
| 1698 if (tag != NULL) { | |
| 1699 name = collection_->names()->GetFormatted("%s / %s", name, tag); | |
| 1700 } | |
| 1701 } | |
| 1702 return AddEntry(object, HeapEntry::kObject, name); | |
| 1703 } else if (object->IsString()) { | |
| 1704 return AddEntry(object, | |
| 1705 HeapEntry::kString, | |
| 1706 collection_->names()->GetName(String::cast(object))); | |
| 1707 } else if (object->IsCode()) { | |
| 1708 return AddEntry(object, HeapEntry::kCode, ""); | |
| 1709 } else if (object->IsSharedFunctionInfo()) { | |
| 1710 String* name = String::cast(SharedFunctionInfo::cast(object)->name()); | |
| 1711 return AddEntry(object, | |
| 1712 HeapEntry::kCode, | |
| 1713 collection_->names()->GetName(name)); | |
| 1714 } else if (object->IsScript()) { | |
| 1715 Object* name = Script::cast(object)->name(); | |
| 1716 return AddEntry(object, | |
| 1717 HeapEntry::kCode, | |
| 1718 name->IsString() | |
| 1719 ? collection_->names()->GetName(String::cast(name)) | |
| 1720 : ""); | |
| 1721 } else if (object->IsNativeContext()) { | |
| 1722 return AddEntry(object, HeapEntry::kHidden, "system / NativeContext"); | |
| 1723 } else if (object->IsContext()) { | |
| 1724 return AddEntry(object, HeapEntry::kHidden, "system / Context"); | |
| 1725 } else if (object->IsFixedArray() || | |
| 1726 object->IsFixedDoubleArray() || | |
| 1727 object->IsByteArray() || | |
| 1728 object->IsExternalArray()) { | |
| 1729 return AddEntry(object, HeapEntry::kArray, ""); | |
| 1730 } else if (object->IsHeapNumber()) { | |
| 1731 return AddEntry(object, HeapEntry::kHeapNumber, "number"); | |
| 1732 } | |
| 1733 return AddEntry(object, HeapEntry::kHidden, GetSystemEntryName(object)); | |
| 1734 } | |
| 1735 | |
| 1736 | |
| 1737 HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object, | |
| 1738 HeapEntry::Type type, | |
| 1739 const char* name) { | |
| 1740 int object_size = object->Size(); | |
| 1741 SnapshotObjectId object_id = | |
| 1742 collection_->GetObjectId(object->address(), object_size); | |
| 1743 return snapshot_->AddEntry(type, name, object_id, object_size); | |
| 1744 } | |
| 1745 | |
| 1746 | |
| 1747 class GcSubrootsEnumerator : public ObjectVisitor { | |
| 1748 public: | |
| 1749 GcSubrootsEnumerator( | |
| 1750 SnapshotFillerInterface* filler, V8HeapExplorer* explorer) | |
| 1751 : filler_(filler), | |
| 1752 explorer_(explorer), | |
| 1753 previous_object_count_(0), | |
| 1754 object_count_(0) { | |
| 1755 } | |
| 1756 void VisitPointers(Object** start, Object** end) { | |
| 1757 object_count_ += end - start; | |
| 1758 } | |
| 1759 void Synchronize(VisitorSynchronization::SyncTag tag) { | |
| 1760 // Skip empty subroots. | |
| 1761 if (previous_object_count_ != object_count_) { | |
| 1762 previous_object_count_ = object_count_; | |
| 1763 filler_->AddEntry(V8HeapExplorer::GetNthGcSubrootObject(tag), explorer_); | |
| 1764 } | |
| 1765 } | |
| 1766 private: | |
| 1767 SnapshotFillerInterface* filler_; | |
| 1768 V8HeapExplorer* explorer_; | |
| 1769 intptr_t previous_object_count_; | |
| 1770 intptr_t object_count_; | |
| 1771 }; | |
| 1772 | |
| 1773 | |
| 1774 void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) { | |
| 1775 filler->AddEntry(kInternalRootObject, this); | |
| 1776 filler->AddEntry(kGcRootsObject, this); | |
| 1777 GcSubrootsEnumerator enumerator(filler, this); | |
| 1778 heap_->IterateRoots(&enumerator, VISIT_ALL); | |
| 1779 } | |
| 1780 | |
| 1781 | |
| 1782 const char* V8HeapExplorer::GetSystemEntryName(HeapObject* object) { | |
| 1783 switch (object->map()->instance_type()) { | |
| 1784 case MAP_TYPE: | |
| 1785 switch (Map::cast(object)->instance_type()) { | |
| 1786 #define MAKE_STRING_MAP_CASE(instance_type, size, name, Name) \ | |
| 1787 case instance_type: return "system / Map (" #Name ")"; | |
| 1788 STRING_TYPE_LIST(MAKE_STRING_MAP_CASE) | |
| 1789 #undef MAKE_STRING_MAP_CASE | |
| 1790 default: return "system / Map"; | |
| 1791 } | |
| 1792 case JS_GLOBAL_PROPERTY_CELL_TYPE: return "system / JSGlobalPropertyCell"; | |
| 1793 case FOREIGN_TYPE: return "system / Foreign"; | |
| 1794 case ODDBALL_TYPE: return "system / Oddball"; | |
| 1795 #define MAKE_STRUCT_CASE(NAME, Name, name) \ | |
| 1796 case NAME##_TYPE: return "system / "#Name; | |
| 1797 STRUCT_LIST(MAKE_STRUCT_CASE) | |
| 1798 #undef MAKE_STRUCT_CASE | |
| 1799 default: return "system"; | |
| 1800 } | |
| 1801 } | |
| 1802 | |
| 1803 | |
| 1804 int V8HeapExplorer::EstimateObjectsCount(HeapIterator* iterator) { | |
| 1805 int objects_count = 0; | |
| 1806 for (HeapObject* obj = iterator->next(); | |
| 1807 obj != NULL; | |
| 1808 obj = iterator->next()) { | |
| 1809 objects_count++; | |
| 1810 } | |
| 1811 return objects_count; | |
| 1812 } | |
| 1813 | |
| 1814 | |
| 1815 class IndexedReferencesExtractor : public ObjectVisitor { | |
| 1816 public: | |
| 1817 IndexedReferencesExtractor(V8HeapExplorer* generator, | |
| 1818 HeapObject* parent_obj, | |
| 1819 int parent) | |
| 1820 : generator_(generator), | |
| 1821 parent_obj_(parent_obj), | |
| 1822 parent_(parent), | |
| 1823 next_index_(1) { | |
| 1824 } | |
| 1825 void VisitPointers(Object** start, Object** end) { | |
| 1826 for (Object** p = start; p < end; p++) { | |
| 1827 if (CheckVisitedAndUnmark(p)) continue; | |
| 1828 generator_->SetHiddenReference(parent_obj_, parent_, next_index_++, *p); | |
| 1829 } | |
| 1830 } | |
| 1831 static void MarkVisitedField(HeapObject* obj, int offset) { | |
| 1832 if (offset < 0) return; | |
| 1833 Address field = obj->address() + offset; | |
| 1834 ASSERT(!Memory::Object_at(field)->IsFailure()); | |
| 1835 ASSERT(Memory::Object_at(field)->IsHeapObject()); | |
| 1836 *field |= kFailureTag; | |
| 1837 } | |
| 1838 | |
| 1839 private: | |
| 1840 bool CheckVisitedAndUnmark(Object** field) { | |
| 1841 if ((*field)->IsFailure()) { | |
| 1842 intptr_t untagged = reinterpret_cast<intptr_t>(*field) & ~kFailureTagMask; | |
| 1843 *field = reinterpret_cast<Object*>(untagged | kHeapObjectTag); | |
| 1844 ASSERT((*field)->IsHeapObject()); | |
| 1845 return true; | |
| 1846 } | |
| 1847 return false; | |
| 1848 } | |
| 1849 V8HeapExplorer* generator_; | |
| 1850 HeapObject* parent_obj_; | |
| 1851 int parent_; | |
| 1852 int next_index_; | |
| 1853 }; | |
| 1854 | |
| 1855 | |
| 1856 void V8HeapExplorer::ExtractReferences(HeapObject* obj) { | |
| 1857 HeapEntry* heap_entry = GetEntry(obj); | |
| 1858 if (heap_entry == NULL) return; // No interest in this object. | |
| 1859 int entry = heap_entry->index(); | |
| 1860 | |
| 1861 bool extract_indexed_refs = true; | |
| 1862 if (obj->IsJSGlobalProxy()) { | |
| 1863 ExtractJSGlobalProxyReferences(JSGlobalProxy::cast(obj)); | |
| 1864 } else if (obj->IsJSObject()) { | |
| 1865 ExtractJSObjectReferences(entry, JSObject::cast(obj)); | |
| 1866 } else if (obj->IsString()) { | |
| 1867 ExtractStringReferences(entry, String::cast(obj)); | |
| 1868 } else if (obj->IsContext()) { | |
| 1869 ExtractContextReferences(entry, Context::cast(obj)); | |
| 1870 } else if (obj->IsMap()) { | |
| 1871 ExtractMapReferences(entry, Map::cast(obj)); | |
| 1872 } else if (obj->IsSharedFunctionInfo()) { | |
| 1873 ExtractSharedFunctionInfoReferences(entry, SharedFunctionInfo::cast(obj)); | |
| 1874 } else if (obj->IsScript()) { | |
| 1875 ExtractScriptReferences(entry, Script::cast(obj)); | |
| 1876 } else if (obj->IsCodeCache()) { | |
| 1877 ExtractCodeCacheReferences(entry, CodeCache::cast(obj)); | |
| 1878 } else if (obj->IsCode()) { | |
| 1879 ExtractCodeReferences(entry, Code::cast(obj)); | |
| 1880 } else if (obj->IsJSGlobalPropertyCell()) { | |
| 1881 ExtractJSGlobalPropertyCellReferences( | |
| 1882 entry, JSGlobalPropertyCell::cast(obj)); | |
| 1883 extract_indexed_refs = false; | |
| 1884 } | |
| 1885 if (extract_indexed_refs) { | |
| 1886 SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset); | |
| 1887 IndexedReferencesExtractor refs_extractor(this, obj, entry); | |
| 1888 obj->Iterate(&refs_extractor); | |
| 1889 } | |
| 1890 } | |
| 1891 | |
| 1892 | |
| 1893 void V8HeapExplorer::ExtractJSGlobalProxyReferences(JSGlobalProxy* proxy) { | |
| 1894 // We need to reference JS global objects from snapshot's root. | |
| 1895 // We use JSGlobalProxy because this is what embedder (e.g. browser) | |
| 1896 // uses for the global object. | |
| 1897 Object* object = proxy->map()->prototype(); | |
| 1898 bool is_debug_object = false; | |
| 1899 #ifdef ENABLE_DEBUGGER_SUPPORT | |
| 1900 is_debug_object = object->IsGlobalObject() && | |
| 1901 Isolate::Current()->debug()->IsDebugGlobal(GlobalObject::cast(object)); | |
| 1902 #endif | |
| 1903 if (!is_debug_object) { | |
| 1904 SetUserGlobalReference(object); | |
| 1905 } | |
| 1906 } | |
| 1907 | |
| 1908 | |
| 1909 void V8HeapExplorer::ExtractJSObjectReferences( | |
| 1910 int entry, JSObject* js_obj) { | |
| 1911 HeapObject* obj = js_obj; | |
| 1912 ExtractClosureReferences(js_obj, entry); | |
| 1913 ExtractPropertyReferences(js_obj, entry); | |
| 1914 ExtractElementReferences(js_obj, entry); | |
| 1915 ExtractInternalReferences(js_obj, entry); | |
| 1916 SetPropertyReference( | |
| 1917 obj, entry, heap_->Proto_symbol(), js_obj->GetPrototype()); | |
| 1918 if (obj->IsJSFunction()) { | |
| 1919 JSFunction* js_fun = JSFunction::cast(js_obj); | |
| 1920 Object* proto_or_map = js_fun->prototype_or_initial_map(); | |
| 1921 if (!proto_or_map->IsTheHole()) { | |
| 1922 if (!proto_or_map->IsMap()) { | |
| 1923 SetPropertyReference( | |
| 1924 obj, entry, | |
| 1925 heap_->prototype_symbol(), proto_or_map, | |
| 1926 NULL, | |
| 1927 JSFunction::kPrototypeOrInitialMapOffset); | |
| 1928 } else { | |
| 1929 SetPropertyReference( | |
| 1930 obj, entry, | |
| 1931 heap_->prototype_symbol(), js_fun->prototype()); | |
| 1932 } | |
| 1933 } | |
| 1934 SharedFunctionInfo* shared_info = js_fun->shared(); | |
| 1935 // JSFunction has either bindings or literals and never both. | |
| 1936 bool bound = shared_info->bound(); | |
| 1937 TagObject(js_fun->literals_or_bindings(), | |
| 1938 bound ? "(function bindings)" : "(function literals)"); | |
| 1939 SetInternalReference(js_fun, entry, | |
| 1940 bound ? "bindings" : "literals", | |
| 1941 js_fun->literals_or_bindings(), | |
| 1942 JSFunction::kLiteralsOffset); | |
| 1943 TagObject(shared_info, "(shared function info)"); | |
| 1944 SetInternalReference(js_fun, entry, | |
| 1945 "shared", shared_info, | |
| 1946 JSFunction::kSharedFunctionInfoOffset); | |
| 1947 TagObject(js_fun->unchecked_context(), "(context)"); | |
| 1948 SetInternalReference(js_fun, entry, | |
| 1949 "context", js_fun->unchecked_context(), | |
| 1950 JSFunction::kContextOffset); | |
| 1951 for (int i = JSFunction::kNonWeakFieldsEndOffset; | |
| 1952 i < JSFunction::kSize; | |
| 1953 i += kPointerSize) { | |
| 1954 SetWeakReference(js_fun, entry, i, *HeapObject::RawField(js_fun, i), i); | |
| 1955 } | |
| 1956 } else if (obj->IsGlobalObject()) { | |
| 1957 GlobalObject* global_obj = GlobalObject::cast(obj); | |
| 1958 SetInternalReference(global_obj, entry, | |
| 1959 "builtins", global_obj->builtins(), | |
| 1960 GlobalObject::kBuiltinsOffset); | |
| 1961 SetInternalReference(global_obj, entry, | |
| 1962 "native_context", global_obj->native_context(), | |
| 1963 GlobalObject::kNativeContextOffset); | |
| 1964 SetInternalReference(global_obj, entry, | |
| 1965 "global_receiver", global_obj->global_receiver(), | |
| 1966 GlobalObject::kGlobalReceiverOffset); | |
| 1967 } | |
| 1968 TagObject(js_obj->properties(), "(object properties)"); | |
| 1969 SetInternalReference(obj, entry, | |
| 1970 "properties", js_obj->properties(), | |
| 1971 JSObject::kPropertiesOffset); | |
| 1972 TagObject(js_obj->elements(), "(object elements)"); | |
| 1973 SetInternalReference(obj, entry, | |
| 1974 "elements", js_obj->elements(), | |
| 1975 JSObject::kElementsOffset); | |
| 1976 } | |
| 1977 | |
| 1978 | |
| 1979 void V8HeapExplorer::ExtractStringReferences(int entry, String* string) { | |
| 1980 if (string->IsConsString()) { | |
| 1981 ConsString* cs = ConsString::cast(string); | |
| 1982 SetInternalReference(cs, entry, "first", cs->first(), | |
| 1983 ConsString::kFirstOffset); | |
| 1984 SetInternalReference(cs, entry, "second", cs->second(), | |
| 1985 ConsString::kSecondOffset); | |
| 1986 } else if (string->IsSlicedString()) { | |
| 1987 SlicedString* ss = SlicedString::cast(string); | |
| 1988 SetInternalReference(ss, entry, "parent", ss->parent(), | |
| 1989 SlicedString::kParentOffset); | |
| 1990 } | |
| 1991 } | |
| 1992 | |
| 1993 | |
| 1994 void V8HeapExplorer::ExtractContextReferences(int entry, Context* context) { | |
| 1995 #define EXTRACT_CONTEXT_FIELD(index, type, name) \ | |
| 1996 SetInternalReference(context, entry, #name, context->get(Context::index), \ | |
| 1997 FixedArray::OffsetOfElementAt(Context::index)); | |
| 1998 EXTRACT_CONTEXT_FIELD(CLOSURE_INDEX, JSFunction, closure); | |
| 1999 EXTRACT_CONTEXT_FIELD(PREVIOUS_INDEX, Context, previous); | |
| 2000 EXTRACT_CONTEXT_FIELD(EXTENSION_INDEX, Object, extension); | |
| 2001 EXTRACT_CONTEXT_FIELD(GLOBAL_OBJECT_INDEX, GlobalObject, global); | |
| 2002 if (context->IsNativeContext()) { | |
| 2003 TagObject(context->jsfunction_result_caches(), | |
| 2004 "(context func. result caches)"); | |
| 2005 TagObject(context->normalized_map_cache(), "(context norm. map cache)"); | |
| 2006 TagObject(context->runtime_context(), "(runtime context)"); | |
| 2007 TagObject(context->embedder_data(), "(context data)"); | |
| 2008 NATIVE_CONTEXT_FIELDS(EXTRACT_CONTEXT_FIELD); | |
| 2009 #undef EXTRACT_CONTEXT_FIELD | |
| 2010 for (int i = Context::FIRST_WEAK_SLOT; | |
| 2011 i < Context::NATIVE_CONTEXT_SLOTS; | |
| 2012 ++i) { | |
| 2013 SetWeakReference(context, entry, i, context->get(i), | |
| 2014 FixedArray::OffsetOfElementAt(i)); | |
| 2015 } | |
| 2016 } | |
| 2017 } | |
| 2018 | |
| 2019 | |
| 2020 void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) { | |
| 2021 SetInternalReference(map, entry, | |
| 2022 "prototype", map->prototype(), Map::kPrototypeOffset); | |
| 2023 SetInternalReference(map, entry, | |
| 2024 "constructor", map->constructor(), | |
| 2025 Map::kConstructorOffset); | |
| 2026 if (map->HasTransitionArray()) { | |
| 2027 TransitionArray* transitions = map->transitions(); | |
| 2028 | |
| 2029 Object* back_pointer = transitions->back_pointer_storage(); | |
| 2030 TagObject(transitions->back_pointer_storage(), "(back pointer)"); | |
| 2031 SetInternalReference(transitions, entry, | |
| 2032 "backpointer", back_pointer, | |
| 2033 TransitionArray::kBackPointerStorageOffset); | |
| 2034 IndexedReferencesExtractor transitions_refs(this, transitions, entry); | |
| 2035 transitions->Iterate(&transitions_refs); | |
| 2036 | |
| 2037 TagObject(transitions, "(transition array)"); | |
| 2038 SetInternalReference(map, entry, | |
| 2039 "transitions", transitions, | |
| 2040 Map::kTransitionsOrBackPointerOffset); | |
| 2041 } else { | |
| 2042 Object* back_pointer = map->GetBackPointer(); | |
| 2043 TagObject(back_pointer, "(back pointer)"); | |
| 2044 SetInternalReference(map, entry, | |
| 2045 "backpointer", back_pointer, | |
| 2046 Map::kTransitionsOrBackPointerOffset); | |
| 2047 } | |
| 2048 DescriptorArray* descriptors = map->instance_descriptors(); | |
| 2049 TagObject(descriptors, "(map descriptors)"); | |
| 2050 SetInternalReference(map, entry, | |
| 2051 "descriptors", descriptors, | |
| 2052 Map::kDescriptorsOffset); | |
| 2053 | |
| 2054 SetInternalReference(map, entry, | |
| 2055 "code_cache", map->code_cache(), | |
| 2056 Map::kCodeCacheOffset); | |
| 2057 } | |
| 2058 | |
| 2059 | |
| 2060 void V8HeapExplorer::ExtractSharedFunctionInfoReferences( | |
| 2061 int entry, SharedFunctionInfo* shared) { | |
| 2062 HeapObject* obj = shared; | |
| 2063 SetInternalReference(obj, entry, | |
| 2064 "name", shared->name(), | |
| 2065 SharedFunctionInfo::kNameOffset); | |
| 2066 TagObject(shared->code(), "(code)"); | |
| 2067 SetInternalReference(obj, entry, | |
| 2068 "code", shared->code(), | |
| 2069 SharedFunctionInfo::kCodeOffset); | |
| 2070 TagObject(shared->scope_info(), "(function scope info)"); | |
| 2071 SetInternalReference(obj, entry, | |
| 2072 "scope_info", shared->scope_info(), | |
| 2073 SharedFunctionInfo::kScopeInfoOffset); | |
| 2074 SetInternalReference(obj, entry, | |
| 2075 "instance_class_name", shared->instance_class_name(), | |
| 2076 SharedFunctionInfo::kInstanceClassNameOffset); | |
| 2077 SetInternalReference(obj, entry, | |
| 2078 "script", shared->script(), | |
| 2079 SharedFunctionInfo::kScriptOffset); | |
| 2080 TagObject(shared->construct_stub(), "(code)"); | |
| 2081 SetInternalReference(obj, entry, | |
| 2082 "construct_stub", shared->construct_stub(), | |
| 2083 SharedFunctionInfo::kConstructStubOffset); | |
| 2084 SetInternalReference(obj, entry, | |
| 2085 "function_data", shared->function_data(), | |
| 2086 SharedFunctionInfo::kFunctionDataOffset); | |
| 2087 SetInternalReference(obj, entry, | |
| 2088 "debug_info", shared->debug_info(), | |
| 2089 SharedFunctionInfo::kDebugInfoOffset); | |
| 2090 SetInternalReference(obj, entry, | |
| 2091 "inferred_name", shared->inferred_name(), | |
| 2092 SharedFunctionInfo::kInferredNameOffset); | |
| 2093 SetInternalReference(obj, entry, | |
| 2094 "this_property_assignments", | |
| 2095 shared->this_property_assignments(), | |
| 2096 SharedFunctionInfo::kThisPropertyAssignmentsOffset); | |
| 2097 SetWeakReference(obj, entry, | |
| 2098 1, shared->initial_map(), | |
| 2099 SharedFunctionInfo::kInitialMapOffset); | |
| 2100 } | |
| 2101 | |
| 2102 | |
| 2103 void V8HeapExplorer::ExtractScriptReferences(int entry, Script* script) { | |
| 2104 HeapObject* obj = script; | |
| 2105 SetInternalReference(obj, entry, | |
| 2106 "source", script->source(), | |
| 2107 Script::kSourceOffset); | |
| 2108 SetInternalReference(obj, entry, | |
| 2109 "name", script->name(), | |
| 2110 Script::kNameOffset); | |
| 2111 SetInternalReference(obj, entry, | |
| 2112 "data", script->data(), | |
| 2113 Script::kDataOffset); | |
| 2114 SetInternalReference(obj, entry, | |
| 2115 "context_data", script->context_data(), | |
| 2116 Script::kContextOffset); | |
| 2117 TagObject(script->line_ends(), "(script line ends)"); | |
| 2118 SetInternalReference(obj, entry, | |
| 2119 "line_ends", script->line_ends(), | |
| 2120 Script::kLineEndsOffset); | |
| 2121 } | |
| 2122 | |
| 2123 | |
| 2124 void V8HeapExplorer::ExtractCodeCacheReferences( | |
| 2125 int entry, CodeCache* code_cache) { | |
| 2126 TagObject(code_cache->default_cache(), "(default code cache)"); | |
| 2127 SetInternalReference(code_cache, entry, | |
| 2128 "default_cache", code_cache->default_cache(), | |
| 2129 CodeCache::kDefaultCacheOffset); | |
| 2130 TagObject(code_cache->normal_type_cache(), "(code type cache)"); | |
| 2131 SetInternalReference(code_cache, entry, | |
| 2132 "type_cache", code_cache->normal_type_cache(), | |
| 2133 CodeCache::kNormalTypeCacheOffset); | |
| 2134 } | |
| 2135 | |
| 2136 | |
| 2137 void V8HeapExplorer::ExtractCodeReferences(int entry, Code* code) { | |
| 2138 TagObject(code->relocation_info(), "(code relocation info)"); | |
| 2139 SetInternalReference(code, entry, | |
| 2140 "relocation_info", code->relocation_info(), | |
| 2141 Code::kRelocationInfoOffset); | |
| 2142 SetInternalReference(code, entry, | |
| 2143 "handler_table", code->handler_table(), | |
| 2144 Code::kHandlerTableOffset); | |
| 2145 TagObject(code->deoptimization_data(), "(code deopt data)"); | |
| 2146 SetInternalReference(code, entry, | |
| 2147 "deoptimization_data", code->deoptimization_data(), | |
| 2148 Code::kDeoptimizationDataOffset); | |
| 2149 if (code->kind() == Code::FUNCTION) { | |
| 2150 SetInternalReference(code, entry, | |
| 2151 "type_feedback_info", code->type_feedback_info(), | |
| 2152 Code::kTypeFeedbackInfoOffset); | |
| 2153 } | |
| 2154 SetInternalReference(code, entry, | |
| 2155 "gc_metadata", code->gc_metadata(), | |
| 2156 Code::kGCMetadataOffset); | |
| 2157 } | |
| 2158 | |
| 2159 | |
| 2160 void V8HeapExplorer::ExtractJSGlobalPropertyCellReferences( | |
| 2161 int entry, JSGlobalPropertyCell* cell) { | |
| 2162 SetInternalReference(cell, entry, "value", cell->value()); | |
| 2163 } | |
| 2164 | |
| 2165 | |
| 2166 void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj, int entry) { | |
| 2167 if (!js_obj->IsJSFunction()) return; | |
| 2168 | |
| 2169 JSFunction* func = JSFunction::cast(js_obj); | |
| 2170 if (func->shared()->bound()) { | |
| 2171 FixedArray* bindings = func->function_bindings(); | |
| 2172 SetNativeBindReference(js_obj, entry, "bound_this", | |
| 2173 bindings->get(JSFunction::kBoundThisIndex)); | |
| 2174 SetNativeBindReference(js_obj, entry, "bound_function", | |
| 2175 bindings->get(JSFunction::kBoundFunctionIndex)); | |
| 2176 for (int i = JSFunction::kBoundArgumentsStartIndex; | |
| 2177 i < bindings->length(); i++) { | |
| 2178 const char* reference_name = collection_->names()->GetFormatted( | |
| 2179 "bound_argument_%d", | |
| 2180 i - JSFunction::kBoundArgumentsStartIndex); | |
| 2181 SetNativeBindReference(js_obj, entry, reference_name, | |
| 2182 bindings->get(i)); | |
| 2183 } | |
| 2184 } else { | |
| 2185 Context* context = func->context()->declaration_context(); | |
| 2186 ScopeInfo* scope_info = context->closure()->shared()->scope_info(); | |
| 2187 // Add context allocated locals. | |
| 2188 int context_locals = scope_info->ContextLocalCount(); | |
| 2189 for (int i = 0; i < context_locals; ++i) { | |
| 2190 String* local_name = scope_info->ContextLocalName(i); | |
| 2191 int idx = Context::MIN_CONTEXT_SLOTS + i; | |
| 2192 SetClosureReference(js_obj, entry, local_name, context->get(idx)); | |
| 2193 } | |
| 2194 | |
| 2195 // Add function variable. | |
| 2196 if (scope_info->HasFunctionName()) { | |
| 2197 String* name = scope_info->FunctionName(); | |
| 2198 VariableMode mode; | |
| 2199 int idx = scope_info->FunctionContextSlotIndex(name, &mode); | |
| 2200 if (idx >= 0) { | |
| 2201 SetClosureReference(js_obj, entry, name, context->get(idx)); | |
| 2202 } | |
| 2203 } | |
| 2204 } | |
| 2205 } | |
| 2206 | |
| 2207 | |
| 2208 void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, int entry) { | |
| 2209 if (js_obj->HasFastProperties()) { | |
| 2210 DescriptorArray* descs = js_obj->map()->instance_descriptors(); | |
| 2211 int real_size = js_obj->map()->NumberOfOwnDescriptors(); | |
| 2212 for (int i = 0; i < descs->number_of_descriptors(); i++) { | |
| 2213 if (descs->GetDetails(i).descriptor_index() > real_size) continue; | |
| 2214 switch (descs->GetType(i)) { | |
| 2215 case FIELD: { | |
| 2216 int index = descs->GetFieldIndex(i); | |
| 2217 | |
| 2218 String* k = descs->GetKey(i); | |
| 2219 if (index < js_obj->map()->inobject_properties()) { | |
| 2220 Object* value = js_obj->InObjectPropertyAt(index); | |
| 2221 if (k != heap_->hidden_symbol()) { | |
| 2222 SetPropertyReference( | |
| 2223 js_obj, entry, | |
| 2224 k, value, | |
| 2225 NULL, | |
| 2226 js_obj->GetInObjectPropertyOffset(index)); | |
| 2227 } else { | |
| 2228 TagObject(value, "(hidden properties)"); | |
| 2229 SetInternalReference( | |
| 2230 js_obj, entry, | |
| 2231 "hidden_properties", value, | |
| 2232 js_obj->GetInObjectPropertyOffset(index)); | |
| 2233 } | |
| 2234 } else { | |
| 2235 Object* value = js_obj->FastPropertyAt(index); | |
| 2236 if (k != heap_->hidden_symbol()) { | |
| 2237 SetPropertyReference(js_obj, entry, k, value); | |
| 2238 } else { | |
| 2239 TagObject(value, "(hidden properties)"); | |
| 2240 SetInternalReference(js_obj, entry, "hidden_properties", value); | |
| 2241 } | |
| 2242 } | |
| 2243 break; | |
| 2244 } | |
| 2245 case CONSTANT_FUNCTION: | |
| 2246 SetPropertyReference( | |
| 2247 js_obj, entry, | |
| 2248 descs->GetKey(i), descs->GetConstantFunction(i)); | |
| 2249 break; | |
| 2250 case CALLBACKS: { | |
| 2251 Object* callback_obj = descs->GetValue(i); | |
| 2252 if (callback_obj->IsAccessorPair()) { | |
| 2253 AccessorPair* accessors = AccessorPair::cast(callback_obj); | |
| 2254 if (Object* getter = accessors->getter()) { | |
| 2255 SetPropertyReference(js_obj, entry, descs->GetKey(i), | |
| 2256 getter, "get-%s"); | |
| 2257 } | |
| 2258 if (Object* setter = accessors->setter()) { | |
| 2259 SetPropertyReference(js_obj, entry, descs->GetKey(i), | |
| 2260 setter, "set-%s"); | |
| 2261 } | |
| 2262 } | |
| 2263 break; | |
| 2264 } | |
| 2265 case NORMAL: // only in slow mode | |
| 2266 case HANDLER: // only in lookup results, not in descriptors | |
| 2267 case INTERCEPTOR: // only in lookup results, not in descriptors | |
| 2268 break; | |
| 2269 case TRANSITION: | |
| 2270 case NONEXISTENT: | |
| 2271 UNREACHABLE(); | |
| 2272 break; | |
| 2273 } | |
| 2274 } | |
| 2275 } else { | |
| 2276 StringDictionary* dictionary = js_obj->property_dictionary(); | |
| 2277 int length = dictionary->Capacity(); | |
| 2278 for (int i = 0; i < length; ++i) { | |
| 2279 Object* k = dictionary->KeyAt(i); | |
| 2280 if (dictionary->IsKey(k)) { | |
| 2281 Object* target = dictionary->ValueAt(i); | |
| 2282 // We assume that global objects can only have slow properties. | |
| 2283 Object* value = target->IsJSGlobalPropertyCell() | |
| 2284 ? JSGlobalPropertyCell::cast(target)->value() | |
| 2285 : target; | |
| 2286 if (k != heap_->hidden_symbol()) { | |
| 2287 SetPropertyReference(js_obj, entry, String::cast(k), value); | |
| 2288 } else { | |
| 2289 TagObject(value, "(hidden properties)"); | |
| 2290 SetInternalReference(js_obj, entry, "hidden_properties", value); | |
| 2291 } | |
| 2292 } | |
| 2293 } | |
| 2294 } | |
| 2295 } | |
| 2296 | |
| 2297 | |
| 2298 void V8HeapExplorer::ExtractElementReferences(JSObject* js_obj, int entry) { | |
| 2299 if (js_obj->HasFastObjectElements()) { | |
| 2300 FixedArray* elements = FixedArray::cast(js_obj->elements()); | |
| 2301 int length = js_obj->IsJSArray() ? | |
| 2302 Smi::cast(JSArray::cast(js_obj)->length())->value() : | |
| 2303 elements->length(); | |
| 2304 for (int i = 0; i < length; ++i) { | |
| 2305 if (!elements->get(i)->IsTheHole()) { | |
| 2306 SetElementReference(js_obj, entry, i, elements->get(i)); | |
| 2307 } | |
| 2308 } | |
| 2309 } else if (js_obj->HasDictionaryElements()) { | |
| 2310 SeededNumberDictionary* dictionary = js_obj->element_dictionary(); | |
| 2311 int length = dictionary->Capacity(); | |
| 2312 for (int i = 0; i < length; ++i) { | |
| 2313 Object* k = dictionary->KeyAt(i); | |
| 2314 if (dictionary->IsKey(k)) { | |
| 2315 ASSERT(k->IsNumber()); | |
| 2316 uint32_t index = static_cast<uint32_t>(k->Number()); | |
| 2317 SetElementReference(js_obj, entry, index, dictionary->ValueAt(i)); | |
| 2318 } | |
| 2319 } | |
| 2320 } | |
| 2321 } | |
| 2322 | |
| 2323 | |
| 2324 void V8HeapExplorer::ExtractInternalReferences(JSObject* js_obj, int entry) { | |
| 2325 int length = js_obj->GetInternalFieldCount(); | |
| 2326 for (int i = 0; i < length; ++i) { | |
| 2327 Object* o = js_obj->GetInternalField(i); | |
| 2328 SetInternalReference( | |
| 2329 js_obj, entry, i, o, js_obj->GetInternalFieldOffset(i)); | |
| 2330 } | |
| 2331 } | |
| 2332 | |
| 2333 | |
| 2334 String* V8HeapExplorer::GetConstructorName(JSObject* object) { | |
| 2335 Heap* heap = object->GetHeap(); | |
| 2336 if (object->IsJSFunction()) return heap->closure_symbol(); | |
| 2337 String* constructor_name = object->constructor_name(); | |
| 2338 if (constructor_name == heap->Object_symbol()) { | |
| 2339 // Look up an immediate "constructor" property, if it is a function, | |
| 2340 // return its name. This is for instances of binding objects, which | |
| 2341 // have prototype constructor type "Object". | |
| 2342 Object* constructor_prop = NULL; | |
| 2343 LookupResult result(heap->isolate()); | |
| 2344 object->LocalLookupRealNamedProperty(heap->constructor_symbol(), &result); | |
| 2345 if (!result.IsFound()) return object->constructor_name(); | |
| 2346 | |
| 2347 constructor_prop = result.GetLazyValue(); | |
| 2348 if (constructor_prop->IsJSFunction()) { | |
| 2349 Object* maybe_name = | |
| 2350 JSFunction::cast(constructor_prop)->shared()->name(); | |
| 2351 if (maybe_name->IsString()) { | |
| 2352 String* name = String::cast(maybe_name); | |
| 2353 if (name->length() > 0) return name; | |
| 2354 } | |
| 2355 } | |
| 2356 } | |
| 2357 return object->constructor_name(); | |
| 2358 } | |
| 2359 | |
| 2360 | |
| 2361 HeapEntry* V8HeapExplorer::GetEntry(Object* obj) { | |
| 2362 if (!obj->IsHeapObject()) return NULL; | |
| 2363 return filler_->FindOrAddEntry(obj, this); | |
| 2364 } | |
| 2365 | |
| 2366 | |
| 2367 class RootsReferencesExtractor : public ObjectVisitor { | |
| 2368 private: | |
| 2369 struct IndexTag { | |
| 2370 IndexTag(int index, VisitorSynchronization::SyncTag tag) | |
| 2371 : index(index), tag(tag) { } | |
| 2372 int index; | |
| 2373 VisitorSynchronization::SyncTag tag; | |
| 2374 }; | |
| 2375 | |
| 2376 public: | |
| 2377 RootsReferencesExtractor() | |
| 2378 : collecting_all_references_(false), | |
| 2379 previous_reference_count_(0) { | |
| 2380 } | |
| 2381 | |
| 2382 void VisitPointers(Object** start, Object** end) { | |
| 2383 if (collecting_all_references_) { | |
| 2384 for (Object** p = start; p < end; p++) all_references_.Add(*p); | |
| 2385 } else { | |
| 2386 for (Object** p = start; p < end; p++) strong_references_.Add(*p); | |
| 2387 } | |
| 2388 } | |
| 2389 | |
| 2390 void SetCollectingAllReferences() { collecting_all_references_ = true; } | |
| 2391 | |
| 2392 void FillReferences(V8HeapExplorer* explorer) { | |
| 2393 ASSERT(strong_references_.length() <= all_references_.length()); | |
| 2394 for (int i = 0; i < reference_tags_.length(); ++i) { | |
| 2395 explorer->SetGcRootsReference(reference_tags_[i].tag); | |
| 2396 } | |
| 2397 int strong_index = 0, all_index = 0, tags_index = 0; | |
| 2398 while (all_index < all_references_.length()) { | |
| 2399 if (strong_index < strong_references_.length() && | |
| 2400 strong_references_[strong_index] == all_references_[all_index]) { | |
| 2401 explorer->SetGcSubrootReference(reference_tags_[tags_index].tag, | |
| 2402 false, | |
| 2403 all_references_[all_index++]); | |
| 2404 ++strong_index; | |
| 2405 } else { | |
| 2406 explorer->SetGcSubrootReference(reference_tags_[tags_index].tag, | |
| 2407 true, | |
| 2408 all_references_[all_index++]); | |
| 2409 } | |
| 2410 if (reference_tags_[tags_index].index == all_index) ++tags_index; | |
| 2411 } | |
| 2412 } | |
| 2413 | |
| 2414 void Synchronize(VisitorSynchronization::SyncTag tag) { | |
| 2415 if (collecting_all_references_ && | |
| 2416 previous_reference_count_ != all_references_.length()) { | |
| 2417 previous_reference_count_ = all_references_.length(); | |
| 2418 reference_tags_.Add(IndexTag(previous_reference_count_, tag)); | |
| 2419 } | |
| 2420 } | |
| 2421 | |
| 2422 private: | |
| 2423 bool collecting_all_references_; | |
| 2424 List<Object*> strong_references_; | |
| 2425 List<Object*> all_references_; | |
| 2426 int previous_reference_count_; | |
| 2427 List<IndexTag> reference_tags_; | |
| 2428 }; | |
| 2429 | |
| 2430 | |
| 2431 bool V8HeapExplorer::IterateAndExtractReferences( | |
| 2432 SnapshotFillerInterface* filler) { | |
| 2433 HeapIterator iterator(heap_, HeapIterator::kFilterUnreachable); | |
| 2434 | |
| 2435 filler_ = filler; | |
| 2436 bool interrupted = false; | |
| 2437 | |
| 2438 // Heap iteration with filtering must be finished in any case. | |
| 2439 for (HeapObject* obj = iterator.next(); | |
| 2440 obj != NULL; | |
| 2441 obj = iterator.next(), progress_->ProgressStep()) { | |
| 2442 if (!interrupted) { | |
| 2443 ExtractReferences(obj); | |
| 2444 if (!progress_->ProgressReport(false)) interrupted = true; | |
| 2445 } | |
| 2446 } | |
| 2447 if (interrupted) { | |
| 2448 filler_ = NULL; | |
| 2449 return false; | |
| 2450 } | |
| 2451 | |
| 2452 SetRootGcRootsReference(); | |
| 2453 RootsReferencesExtractor extractor; | |
| 2454 heap_->IterateRoots(&extractor, VISIT_ONLY_STRONG); | |
| 2455 extractor.SetCollectingAllReferences(); | |
| 2456 heap_->IterateRoots(&extractor, VISIT_ALL); | |
| 2457 extractor.FillReferences(this); | |
| 2458 filler_ = NULL; | |
| 2459 return progress_->ProgressReport(true); | |
| 2460 } | |
| 2461 | |
| 2462 | |
| 2463 bool V8HeapExplorer::IsEssentialObject(Object* object) { | |
| 2464 return object->IsHeapObject() | |
| 2465 && !object->IsOddball() | |
| 2466 && object != heap_->empty_byte_array() | |
| 2467 && object != heap_->empty_fixed_array() | |
| 2468 && object != heap_->empty_descriptor_array() | |
| 2469 && object != heap_->fixed_array_map() | |
| 2470 && object != heap_->global_property_cell_map() | |
| 2471 && object != heap_->shared_function_info_map() | |
| 2472 && object != heap_->free_space_map() | |
| 2473 && object != heap_->one_pointer_filler_map() | |
| 2474 && object != heap_->two_pointer_filler_map(); | |
| 2475 } | |
| 2476 | |
| 2477 | |
| 2478 void V8HeapExplorer::SetClosureReference(HeapObject* parent_obj, | |
| 2479 int parent_entry, | |
| 2480 String* reference_name, | |
| 2481 Object* child_obj) { | |
| 2482 HeapEntry* child_entry = GetEntry(child_obj); | |
| 2483 if (child_entry != NULL) { | |
| 2484 filler_->SetNamedReference(HeapGraphEdge::kContextVariable, | |
| 2485 parent_entry, | |
| 2486 collection_->names()->GetName(reference_name), | |
| 2487 child_entry); | |
| 2488 } | |
| 2489 } | |
| 2490 | |
| 2491 | |
| 2492 void V8HeapExplorer::SetNativeBindReference(HeapObject* parent_obj, | |
| 2493 int parent_entry, | |
| 2494 const char* reference_name, | |
| 2495 Object* child_obj) { | |
| 2496 HeapEntry* child_entry = GetEntry(child_obj); | |
| 2497 if (child_entry != NULL) { | |
| 2498 filler_->SetNamedReference(HeapGraphEdge::kShortcut, | |
| 2499 parent_entry, | |
| 2500 reference_name, | |
| 2501 child_entry); | |
| 2502 } | |
| 2503 } | |
| 2504 | |
| 2505 | |
| 2506 void V8HeapExplorer::SetElementReference(HeapObject* parent_obj, | |
| 2507 int parent_entry, | |
| 2508 int index, | |
| 2509 Object* child_obj) { | |
| 2510 HeapEntry* child_entry = GetEntry(child_obj); | |
| 2511 if (child_entry != NULL) { | |
| 2512 filler_->SetIndexedReference(HeapGraphEdge::kElement, | |
| 2513 parent_entry, | |
| 2514 index, | |
| 2515 child_entry); | |
| 2516 } | |
| 2517 } | |
| 2518 | |
| 2519 | |
| 2520 void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj, | |
| 2521 int parent_entry, | |
| 2522 const char* reference_name, | |
| 2523 Object* child_obj, | |
| 2524 int field_offset) { | |
| 2525 HeapEntry* child_entry = GetEntry(child_obj); | |
| 2526 if (child_entry == NULL) return; | |
| 2527 if (IsEssentialObject(child_obj)) { | |
| 2528 filler_->SetNamedReference(HeapGraphEdge::kInternal, | |
| 2529 parent_entry, | |
| 2530 reference_name, | |
| 2531 child_entry); | |
| 2532 } | |
| 2533 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset); | |
| 2534 } | |
| 2535 | |
| 2536 | |
| 2537 void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj, | |
| 2538 int parent_entry, | |
| 2539 int index, | |
| 2540 Object* child_obj, | |
| 2541 int field_offset) { | |
| 2542 HeapEntry* child_entry = GetEntry(child_obj); | |
| 2543 if (child_entry == NULL) return; | |
| 2544 if (IsEssentialObject(child_obj)) { | |
| 2545 filler_->SetNamedReference(HeapGraphEdge::kInternal, | |
| 2546 parent_entry, | |
| 2547 collection_->names()->GetName(index), | |
| 2548 child_entry); | |
| 2549 } | |
| 2550 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset); | |
| 2551 } | |
| 2552 | |
| 2553 | |
| 2554 void V8HeapExplorer::SetHiddenReference(HeapObject* parent_obj, | |
| 2555 int parent_entry, | |
| 2556 int index, | |
| 2557 Object* child_obj) { | |
| 2558 HeapEntry* child_entry = GetEntry(child_obj); | |
| 2559 if (child_entry != NULL && IsEssentialObject(child_obj)) { | |
| 2560 filler_->SetIndexedReference(HeapGraphEdge::kHidden, | |
| 2561 parent_entry, | |
| 2562 index, | |
| 2563 child_entry); | |
| 2564 } | |
| 2565 } | |
| 2566 | |
| 2567 | |
| 2568 void V8HeapExplorer::SetWeakReference(HeapObject* parent_obj, | |
| 2569 int parent_entry, | |
| 2570 int index, | |
| 2571 Object* child_obj, | |
| 2572 int field_offset) { | |
| 2573 HeapEntry* child_entry = GetEntry(child_obj); | |
| 2574 if (child_entry != NULL) { | |
| 2575 filler_->SetIndexedReference(HeapGraphEdge::kWeak, | |
| 2576 parent_entry, | |
| 2577 index, | |
| 2578 child_entry); | |
| 2579 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset); | |
| 2580 } | |
| 2581 } | |
| 2582 | |
| 2583 | |
| 2584 void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj, | |
| 2585 int parent_entry, | |
| 2586 String* reference_name, | |
| 2587 Object* child_obj, | |
| 2588 const char* name_format_string, | |
| 2589 int field_offset) { | |
| 2590 HeapEntry* child_entry = GetEntry(child_obj); | |
| 2591 if (child_entry != NULL) { | |
| 2592 HeapGraphEdge::Type type = reference_name->length() > 0 ? | |
| 2593 HeapGraphEdge::kProperty : HeapGraphEdge::kInternal; | |
| 2594 const char* name = name_format_string != NULL ? | |
| 2595 collection_->names()->GetFormatted( | |
| 2596 name_format_string, | |
| 2597 *reference_name->ToCString(DISALLOW_NULLS, | |
| 2598 ROBUST_STRING_TRAVERSAL)) : | |
| 2599 collection_->names()->GetName(reference_name); | |
| 2600 | |
| 2601 filler_->SetNamedReference(type, | |
| 2602 parent_entry, | |
| 2603 name, | |
| 2604 child_entry); | |
| 2605 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset); | |
| 2606 } | |
| 2607 } | |
| 2608 | |
| 2609 | |
| 2610 void V8HeapExplorer::SetRootGcRootsReference() { | |
| 2611 filler_->SetIndexedAutoIndexReference( | |
| 2612 HeapGraphEdge::kElement, | |
| 2613 snapshot_->root()->index(), | |
| 2614 snapshot_->gc_roots()); | |
| 2615 } | |
| 2616 | |
| 2617 | |
| 2618 void V8HeapExplorer::SetUserGlobalReference(Object* child_obj) { | |
| 2619 HeapEntry* child_entry = GetEntry(child_obj); | |
| 2620 ASSERT(child_entry != NULL); | |
| 2621 filler_->SetNamedAutoIndexReference( | |
| 2622 HeapGraphEdge::kShortcut, | |
| 2623 snapshot_->root()->index(), | |
| 2624 child_entry); | |
| 2625 } | |
| 2626 | |
| 2627 | |
| 2628 void V8HeapExplorer::SetGcRootsReference(VisitorSynchronization::SyncTag tag) { | |
| 2629 filler_->SetIndexedAutoIndexReference( | |
| 2630 HeapGraphEdge::kElement, | |
| 2631 snapshot_->gc_roots()->index(), | |
| 2632 snapshot_->gc_subroot(tag)); | |
| 2633 } | |
| 2634 | |
| 2635 | |
| 2636 void V8HeapExplorer::SetGcSubrootReference( | |
| 2637 VisitorSynchronization::SyncTag tag, bool is_weak, Object* child_obj) { | |
| 2638 HeapEntry* child_entry = GetEntry(child_obj); | |
| 2639 if (child_entry != NULL) { | |
| 2640 const char* name = GetStrongGcSubrootName(child_obj); | |
| 2641 if (name != NULL) { | |
| 2642 filler_->SetNamedReference( | |
| 2643 HeapGraphEdge::kInternal, | |
| 2644 snapshot_->gc_subroot(tag)->index(), | |
| 2645 name, | |
| 2646 child_entry); | |
| 2647 } else { | |
| 2648 filler_->SetIndexedAutoIndexReference( | |
| 2649 is_weak ? HeapGraphEdge::kWeak : HeapGraphEdge::kElement, | |
| 2650 snapshot_->gc_subroot(tag)->index(), | |
| 2651 child_entry); | |
| 2652 } | |
| 2653 } | |
| 2654 } | |
| 2655 | |
| 2656 | |
| 2657 const char* V8HeapExplorer::GetStrongGcSubrootName(Object* object) { | |
| 2658 if (strong_gc_subroot_names_.is_empty()) { | |
| 2659 #define NAME_ENTRY(name) strong_gc_subroot_names_.SetTag(heap_->name(), #name); | |
| 2660 #define ROOT_NAME(type, name, camel_name) NAME_ENTRY(name) | |
| 2661 STRONG_ROOT_LIST(ROOT_NAME) | |
| 2662 #undef ROOT_NAME | |
| 2663 #define STRUCT_MAP_NAME(NAME, Name, name) NAME_ENTRY(name##_map) | |
| 2664 STRUCT_LIST(STRUCT_MAP_NAME) | |
| 2665 #undef STRUCT_MAP_NAME | |
| 2666 #define SYMBOL_NAME(name, str) NAME_ENTRY(name) | |
| 2667 SYMBOL_LIST(SYMBOL_NAME) | |
| 2668 #undef SYMBOL_NAME | |
| 2669 #undef NAME_ENTRY | |
| 2670 CHECK(!strong_gc_subroot_names_.is_empty()); | |
| 2671 } | |
| 2672 return strong_gc_subroot_names_.GetTag(object); | |
| 2673 } | |
| 2674 | |
| 2675 | |
| 2676 void V8HeapExplorer::TagObject(Object* obj, const char* tag) { | |
| 2677 if (IsEssentialObject(obj)) { | |
| 2678 HeapEntry* entry = GetEntry(obj); | |
| 2679 if (entry->name()[0] == '\0') { | |
| 2680 entry->set_name(tag); | |
| 2681 } | |
| 2682 } | |
| 2683 } | |
| 2684 | |
| 2685 | |
| 2686 class GlobalObjectsEnumerator : public ObjectVisitor { | |
| 2687 public: | |
| 2688 virtual void VisitPointers(Object** start, Object** end) { | |
| 2689 for (Object** p = start; p < end; p++) { | |
| 2690 if ((*p)->IsNativeContext()) { | |
| 2691 Context* context = Context::cast(*p); | |
| 2692 JSObject* proxy = context->global_proxy(); | |
| 2693 if (proxy->IsJSGlobalProxy()) { | |
| 2694 Object* global = proxy->map()->prototype(); | |
| 2695 if (global->IsJSGlobalObject()) { | |
| 2696 objects_.Add(Handle<JSGlobalObject>(JSGlobalObject::cast(global))); | |
| 2697 } | |
| 2698 } | |
| 2699 } | |
| 2700 } | |
| 2701 } | |
| 2702 int count() { return objects_.length(); } | |
| 2703 Handle<JSGlobalObject>& at(int i) { return objects_[i]; } | |
| 2704 | |
| 2705 private: | |
| 2706 List<Handle<JSGlobalObject> > objects_; | |
| 2707 }; | |
| 2708 | |
| 2709 | |
| 2710 // Modifies heap. Must not be run during heap traversal. | |
| 2711 void V8HeapExplorer::TagGlobalObjects() { | |
| 2712 Isolate* isolate = Isolate::Current(); | |
| 2713 HandleScope scope(isolate); | |
| 2714 GlobalObjectsEnumerator enumerator; | |
| 2715 isolate->global_handles()->IterateAllRoots(&enumerator); | |
| 2716 const char** urls = NewArray<const char*>(enumerator.count()); | |
| 2717 for (int i = 0, l = enumerator.count(); i < l; ++i) { | |
| 2718 if (global_object_name_resolver_) { | |
| 2719 HandleScope scope(isolate); | |
| 2720 Handle<JSGlobalObject> global_obj = enumerator.at(i); | |
| 2721 urls[i] = global_object_name_resolver_->GetName( | |
| 2722 Utils::ToLocal(Handle<JSObject>::cast(global_obj))); | |
| 2723 } else { | |
| 2724 urls[i] = NULL; | |
| 2725 } | |
| 2726 } | |
| 2727 | |
| 2728 AssertNoAllocation no_allocation; | |
| 2729 for (int i = 0, l = enumerator.count(); i < l; ++i) { | |
| 2730 objects_tags_.SetTag(*enumerator.at(i), urls[i]); | |
| 2731 } | |
| 2732 | |
| 2733 DeleteArray(urls); | |
| 2734 } | |
| 2735 | |
| 2736 | |
| 2737 class GlobalHandlesExtractor : public ObjectVisitor { | |
| 2738 public: | |
| 2739 explicit GlobalHandlesExtractor(NativeObjectsExplorer* explorer) | |
| 2740 : explorer_(explorer) {} | |
| 2741 virtual ~GlobalHandlesExtractor() {} | |
| 2742 virtual void VisitPointers(Object** start, Object** end) { | |
| 2743 UNREACHABLE(); | |
| 2744 } | |
| 2745 virtual void VisitEmbedderReference(Object** p, uint16_t class_id) { | |
| 2746 explorer_->VisitSubtreeWrapper(p, class_id); | |
| 2747 } | |
| 2748 private: | |
| 2749 NativeObjectsExplorer* explorer_; | |
| 2750 }; | |
| 2751 | |
| 2752 | |
| 2753 class BasicHeapEntriesAllocator : public HeapEntriesAllocator { | |
| 2754 public: | |
| 2755 BasicHeapEntriesAllocator( | |
| 2756 HeapSnapshot* snapshot, | |
| 2757 HeapEntry::Type entries_type) | |
| 2758 : snapshot_(snapshot), | |
| 2759 collection_(snapshot_->collection()), | |
| 2760 entries_type_(entries_type) { | |
| 2761 } | |
| 2762 virtual HeapEntry* AllocateEntry(HeapThing ptr); | |
| 2763 private: | |
| 2764 HeapSnapshot* snapshot_; | |
| 2765 HeapSnapshotsCollection* collection_; | |
| 2766 HeapEntry::Type entries_type_; | |
| 2767 }; | |
| 2768 | |
| 2769 | |
| 2770 HeapEntry* BasicHeapEntriesAllocator::AllocateEntry(HeapThing ptr) { | |
| 2771 v8::RetainedObjectInfo* info = reinterpret_cast<v8::RetainedObjectInfo*>(ptr); | |
| 2772 intptr_t elements = info->GetElementCount(); | |
| 2773 intptr_t size = info->GetSizeInBytes(); | |
| 2774 const char* name = elements != -1 | |
| 2775 ? collection_->names()->GetFormatted( | |
| 2776 "%s / %" V8_PTR_PREFIX "d entries", info->GetLabel(), elements) | |
| 2777 : collection_->names()->GetCopy(info->GetLabel()); | |
| 2778 return snapshot_->AddEntry( | |
| 2779 entries_type_, | |
| 2780 name, | |
| 2781 HeapObjectsMap::GenerateId(info), | |
| 2782 size != -1 ? static_cast<int>(size) : 0); | |
| 2783 } | |
| 2784 | |
| 2785 | |
| 2786 NativeObjectsExplorer::NativeObjectsExplorer( | |
| 2787 HeapSnapshot* snapshot, SnapshottingProgressReportingInterface* progress) | |
| 2788 : snapshot_(snapshot), | |
| 2789 collection_(snapshot_->collection()), | |
| 2790 progress_(progress), | |
| 2791 embedder_queried_(false), | |
| 2792 objects_by_info_(RetainedInfosMatch), | |
| 2793 native_groups_(StringsMatch), | |
| 2794 filler_(NULL) { | |
| 2795 synthetic_entries_allocator_ = | |
| 2796 new BasicHeapEntriesAllocator(snapshot, HeapEntry::kSynthetic); | |
| 2797 native_entries_allocator_ = | |
| 2798 new BasicHeapEntriesAllocator(snapshot, HeapEntry::kNative); | |
| 2799 } | |
| 2800 | |
| 2801 | |
| 2802 NativeObjectsExplorer::~NativeObjectsExplorer() { | |
| 2803 for (HashMap::Entry* p = objects_by_info_.Start(); | |
| 2804 p != NULL; | |
| 2805 p = objects_by_info_.Next(p)) { | |
| 2806 v8::RetainedObjectInfo* info = | |
| 2807 reinterpret_cast<v8::RetainedObjectInfo*>(p->key); | |
| 2808 info->Dispose(); | |
| 2809 List<HeapObject*>* objects = | |
| 2810 reinterpret_cast<List<HeapObject*>* >(p->value); | |
| 2811 delete objects; | |
| 2812 } | |
| 2813 for (HashMap::Entry* p = native_groups_.Start(); | |
| 2814 p != NULL; | |
| 2815 p = native_groups_.Next(p)) { | |
| 2816 v8::RetainedObjectInfo* info = | |
| 2817 reinterpret_cast<v8::RetainedObjectInfo*>(p->value); | |
| 2818 info->Dispose(); | |
| 2819 } | |
| 2820 delete synthetic_entries_allocator_; | |
| 2821 delete native_entries_allocator_; | |
| 2822 } | |
| 2823 | |
| 2824 | |
| 2825 int NativeObjectsExplorer::EstimateObjectsCount() { | |
| 2826 FillRetainedObjects(); | |
| 2827 return objects_by_info_.occupancy(); | |
| 2828 } | |
| 2829 | |
| 2830 | |
| 2831 void NativeObjectsExplorer::FillRetainedObjects() { | |
| 2832 if (embedder_queried_) return; | |
| 2833 Isolate* isolate = Isolate::Current(); | |
| 2834 const GCType major_gc_type = kGCTypeMarkSweepCompact; | |
| 2835 // Record objects that are joined into ObjectGroups. | |
| 2836 isolate->heap()->CallGCPrologueCallbacks(major_gc_type); | |
| 2837 List<ObjectGroup*>* groups = isolate->global_handles()->object_groups(); | |
| 2838 for (int i = 0; i < groups->length(); ++i) { | |
| 2839 ObjectGroup* group = groups->at(i); | |
| 2840 if (group->info_ == NULL) continue; | |
| 2841 List<HeapObject*>* list = GetListMaybeDisposeInfo(group->info_); | |
| 2842 for (size_t j = 0; j < group->length_; ++j) { | |
| 2843 HeapObject* obj = HeapObject::cast(*group->objects_[j]); | |
| 2844 list->Add(obj); | |
| 2845 in_groups_.Insert(obj); | |
| 2846 } | |
| 2847 group->info_ = NULL; // Acquire info object ownership. | |
| 2848 } | |
| 2849 isolate->global_handles()->RemoveObjectGroups(); | |
| 2850 isolate->heap()->CallGCEpilogueCallbacks(major_gc_type); | |
| 2851 // Record objects that are not in ObjectGroups, but have class ID. | |
| 2852 GlobalHandlesExtractor extractor(this); | |
| 2853 isolate->global_handles()->IterateAllRootsWithClassIds(&extractor); | |
| 2854 embedder_queried_ = true; | |
| 2855 } | |
| 2856 | |
| 2857 void NativeObjectsExplorer::FillImplicitReferences() { | |
| 2858 Isolate* isolate = Isolate::Current(); | |
| 2859 List<ImplicitRefGroup*>* groups = | |
| 2860 isolate->global_handles()->implicit_ref_groups(); | |
| 2861 for (int i = 0; i < groups->length(); ++i) { | |
| 2862 ImplicitRefGroup* group = groups->at(i); | |
| 2863 HeapObject* parent = *group->parent_; | |
| 2864 int parent_entry = | |
| 2865 filler_->FindOrAddEntry(parent, native_entries_allocator_)->index(); | |
| 2866 ASSERT(parent_entry != HeapEntry::kNoEntry); | |
| 2867 Object*** children = group->children_; | |
| 2868 for (size_t j = 0; j < group->length_; ++j) { | |
| 2869 Object* child = *children[j]; | |
| 2870 HeapEntry* child_entry = | |
| 2871 filler_->FindOrAddEntry(child, native_entries_allocator_); | |
| 2872 filler_->SetNamedReference( | |
| 2873 HeapGraphEdge::kInternal, | |
| 2874 parent_entry, | |
| 2875 "native", | |
| 2876 child_entry); | |
| 2877 } | |
| 2878 } | |
| 2879 isolate->global_handles()->RemoveImplicitRefGroups(); | |
| 2880 } | |
| 2881 | |
| 2882 List<HeapObject*>* NativeObjectsExplorer::GetListMaybeDisposeInfo( | |
| 2883 v8::RetainedObjectInfo* info) { | |
| 2884 HashMap::Entry* entry = | |
| 2885 objects_by_info_.Lookup(info, InfoHash(info), true); | |
| 2886 if (entry->value != NULL) { | |
| 2887 info->Dispose(); | |
| 2888 } else { | |
| 2889 entry->value = new List<HeapObject*>(4); | |
| 2890 } | |
| 2891 return reinterpret_cast<List<HeapObject*>* >(entry->value); | |
| 2892 } | |
| 2893 | |
| 2894 | |
| 2895 bool NativeObjectsExplorer::IterateAndExtractReferences( | |
| 2896 SnapshotFillerInterface* filler) { | |
| 2897 filler_ = filler; | |
| 2898 FillRetainedObjects(); | |
| 2899 FillImplicitReferences(); | |
| 2900 if (EstimateObjectsCount() > 0) { | |
| 2901 for (HashMap::Entry* p = objects_by_info_.Start(); | |
| 2902 p != NULL; | |
| 2903 p = objects_by_info_.Next(p)) { | |
| 2904 v8::RetainedObjectInfo* info = | |
| 2905 reinterpret_cast<v8::RetainedObjectInfo*>(p->key); | |
| 2906 SetNativeRootReference(info); | |
| 2907 List<HeapObject*>* objects = | |
| 2908 reinterpret_cast<List<HeapObject*>* >(p->value); | |
| 2909 for (int i = 0; i < objects->length(); ++i) { | |
| 2910 SetWrapperNativeReferences(objects->at(i), info); | |
| 2911 } | |
| 2912 } | |
| 2913 SetRootNativeRootsReference(); | |
| 2914 } | |
| 2915 filler_ = NULL; | |
| 2916 return true; | |
| 2917 } | |
| 2918 | |
| 2919 | |
| 2920 class NativeGroupRetainedObjectInfo : public v8::RetainedObjectInfo { | |
| 2921 public: | |
| 2922 explicit NativeGroupRetainedObjectInfo(const char* label) | |
| 2923 : disposed_(false), | |
| 2924 hash_(reinterpret_cast<intptr_t>(label)), | |
| 2925 label_(label) { | |
| 2926 } | |
| 2927 | |
| 2928 virtual ~NativeGroupRetainedObjectInfo() {} | |
| 2929 virtual void Dispose() { | |
| 2930 CHECK(!disposed_); | |
| 2931 disposed_ = true; | |
| 2932 delete this; | |
| 2933 } | |
| 2934 virtual bool IsEquivalent(RetainedObjectInfo* other) { | |
| 2935 return hash_ == other->GetHash() && !strcmp(label_, other->GetLabel()); | |
| 2936 } | |
| 2937 virtual intptr_t GetHash() { return hash_; } | |
| 2938 virtual const char* GetLabel() { return label_; } | |
| 2939 | |
| 2940 private: | |
| 2941 bool disposed_; | |
| 2942 intptr_t hash_; | |
| 2943 const char* label_; | |
| 2944 }; | |
| 2945 | |
| 2946 | |
| 2947 NativeGroupRetainedObjectInfo* NativeObjectsExplorer::FindOrAddGroupInfo( | |
| 2948 const char* label) { | |
| 2949 const char* label_copy = collection_->names()->GetCopy(label); | |
| 2950 uint32_t hash = StringHasher::HashSequentialString( | |
| 2951 label_copy, | |
| 2952 static_cast<int>(strlen(label_copy)), | |
| 2953 HEAP->HashSeed()); | |
| 2954 HashMap::Entry* entry = native_groups_.Lookup(const_cast<char*>(label_copy), | |
| 2955 hash, true); | |
| 2956 if (entry->value == NULL) { | |
| 2957 entry->value = new NativeGroupRetainedObjectInfo(label); | |
| 2958 } | |
| 2959 return static_cast<NativeGroupRetainedObjectInfo*>(entry->value); | |
| 2960 } | |
| 2961 | |
| 2962 | |
| 2963 void NativeObjectsExplorer::SetNativeRootReference( | |
| 2964 v8::RetainedObjectInfo* info) { | |
| 2965 HeapEntry* child_entry = | |
| 2966 filler_->FindOrAddEntry(info, native_entries_allocator_); | |
| 2967 ASSERT(child_entry != NULL); | |
| 2968 NativeGroupRetainedObjectInfo* group_info = | |
| 2969 FindOrAddGroupInfo(info->GetGroupLabel()); | |
| 2970 HeapEntry* group_entry = | |
| 2971 filler_->FindOrAddEntry(group_info, synthetic_entries_allocator_); | |
| 2972 filler_->SetNamedAutoIndexReference( | |
| 2973 HeapGraphEdge::kInternal, | |
| 2974 group_entry->index(), | |
| 2975 child_entry); | |
| 2976 } | |
| 2977 | |
| 2978 | |
| 2979 void NativeObjectsExplorer::SetWrapperNativeReferences( | |
| 2980 HeapObject* wrapper, v8::RetainedObjectInfo* info) { | |
| 2981 HeapEntry* wrapper_entry = filler_->FindEntry(wrapper); | |
| 2982 ASSERT(wrapper_entry != NULL); | |
| 2983 HeapEntry* info_entry = | |
| 2984 filler_->FindOrAddEntry(info, native_entries_allocator_); | |
| 2985 ASSERT(info_entry != NULL); | |
| 2986 filler_->SetNamedReference(HeapGraphEdge::kInternal, | |
| 2987 wrapper_entry->index(), | |
| 2988 "native", | |
| 2989 info_entry); | |
| 2990 filler_->SetIndexedAutoIndexReference(HeapGraphEdge::kElement, | |
| 2991 info_entry->index(), | |
| 2992 wrapper_entry); | |
| 2993 } | |
| 2994 | |
| 2995 | |
| 2996 void NativeObjectsExplorer::SetRootNativeRootsReference() { | |
| 2997 for (HashMap::Entry* entry = native_groups_.Start(); | |
| 2998 entry; | |
| 2999 entry = native_groups_.Next(entry)) { | |
| 3000 NativeGroupRetainedObjectInfo* group_info = | |
| 3001 static_cast<NativeGroupRetainedObjectInfo*>(entry->value); | |
| 3002 HeapEntry* group_entry = | |
| 3003 filler_->FindOrAddEntry(group_info, native_entries_allocator_); | |
| 3004 ASSERT(group_entry != NULL); | |
| 3005 filler_->SetIndexedAutoIndexReference( | |
| 3006 HeapGraphEdge::kElement, | |
| 3007 snapshot_->root()->index(), | |
| 3008 group_entry); | |
| 3009 } | |
| 3010 } | |
| 3011 | |
| 3012 | |
| 3013 void NativeObjectsExplorer::VisitSubtreeWrapper(Object** p, uint16_t class_id) { | |
| 3014 if (in_groups_.Contains(*p)) return; | |
| 3015 Isolate* isolate = Isolate::Current(); | |
| 3016 v8::RetainedObjectInfo* info = | |
| 3017 isolate->heap_profiler()->ExecuteWrapperClassCallback(class_id, p); | |
| 3018 if (info == NULL) return; | |
| 3019 GetListMaybeDisposeInfo(info)->Add(HeapObject::cast(*p)); | |
| 3020 } | |
| 3021 | |
| 3022 | |
| 3023 class SnapshotFiller : public SnapshotFillerInterface { | |
| 3024 public: | |
| 3025 explicit SnapshotFiller(HeapSnapshot* snapshot, HeapEntriesMap* entries) | |
| 3026 : snapshot_(snapshot), | |
| 3027 collection_(snapshot->collection()), | |
| 3028 entries_(entries) { } | |
| 3029 HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) { | |
| 3030 HeapEntry* entry = allocator->AllocateEntry(ptr); | |
| 3031 entries_->Pair(ptr, entry->index()); | |
| 3032 return entry; | |
| 3033 } | |
| 3034 HeapEntry* FindEntry(HeapThing ptr) { | |
| 3035 int index = entries_->Map(ptr); | |
| 3036 return index != HeapEntry::kNoEntry ? &snapshot_->entries()[index] : NULL; | |
| 3037 } | |
| 3038 HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) { | |
| 3039 HeapEntry* entry = FindEntry(ptr); | |
| 3040 return entry != NULL ? entry : AddEntry(ptr, allocator); | |
| 3041 } | |
| 3042 void SetIndexedReference(HeapGraphEdge::Type type, | |
| 3043 int parent, | |
| 3044 int index, | |
| 3045 HeapEntry* child_entry) { | |
| 3046 HeapEntry* parent_entry = &snapshot_->entries()[parent]; | |
| 3047 parent_entry->SetIndexedReference(type, index, child_entry); | |
| 3048 } | |
| 3049 void SetIndexedAutoIndexReference(HeapGraphEdge::Type type, | |
| 3050 int parent, | |
| 3051 HeapEntry* child_entry) { | |
| 3052 HeapEntry* parent_entry = &snapshot_->entries()[parent]; | |
| 3053 int index = parent_entry->children_count() + 1; | |
| 3054 parent_entry->SetIndexedReference(type, index, child_entry); | |
| 3055 } | |
| 3056 void SetNamedReference(HeapGraphEdge::Type type, | |
| 3057 int parent, | |
| 3058 const char* reference_name, | |
| 3059 HeapEntry* child_entry) { | |
| 3060 HeapEntry* parent_entry = &snapshot_->entries()[parent]; | |
| 3061 parent_entry->SetNamedReference(type, reference_name, child_entry); | |
| 3062 } | |
| 3063 void SetNamedAutoIndexReference(HeapGraphEdge::Type type, | |
| 3064 int parent, | |
| 3065 HeapEntry* child_entry) { | |
| 3066 HeapEntry* parent_entry = &snapshot_->entries()[parent]; | |
| 3067 int index = parent_entry->children_count() + 1; | |
| 3068 parent_entry->SetNamedReference( | |
| 3069 type, | |
| 3070 collection_->names()->GetName(index), | |
| 3071 child_entry); | |
| 3072 } | |
| 3073 | |
| 3074 private: | |
| 3075 HeapSnapshot* snapshot_; | |
| 3076 HeapSnapshotsCollection* collection_; | |
| 3077 HeapEntriesMap* entries_; | |
| 3078 }; | |
| 3079 | |
| 3080 | |
| 3081 HeapSnapshotGenerator::HeapSnapshotGenerator( | |
| 3082 HeapSnapshot* snapshot, | |
| 3083 v8::ActivityControl* control, | |
| 3084 v8::HeapProfiler::ObjectNameResolver* resolver, | |
| 3085 Heap* heap) | |
| 3086 : snapshot_(snapshot), | |
| 3087 control_(control), | |
| 3088 v8_heap_explorer_(snapshot_, this, resolver), | |
| 3089 dom_explorer_(snapshot_, this), | |
| 3090 heap_(heap) { | |
| 3091 } | |
| 3092 | |
| 3093 | |
| 3094 bool HeapSnapshotGenerator::GenerateSnapshot() { | |
| 3095 v8_heap_explorer_.TagGlobalObjects(); | |
| 3096 | |
| 3097 // TODO(1562) Profiler assumes that any object that is in the heap after | |
| 3098 // full GC is reachable from the root when computing dominators. | |
| 3099 // This is not true for weakly reachable objects. | |
| 3100 // As a temporary solution we call GC twice. | |
| 3101 Isolate::Current()->heap()->CollectAllGarbage( | |
| 3102 Heap::kMakeHeapIterableMask, | |
| 3103 "HeapSnapshotGenerator::GenerateSnapshot"); | |
| 3104 Isolate::Current()->heap()->CollectAllGarbage( | |
| 3105 Heap::kMakeHeapIterableMask, | |
| 3106 "HeapSnapshotGenerator::GenerateSnapshot"); | |
| 3107 | |
| 3108 #ifdef VERIFY_HEAP | |
| 3109 Heap* debug_heap = Isolate::Current()->heap(); | |
| 3110 CHECK(!debug_heap->old_data_space()->was_swept_conservatively()); | |
| 3111 CHECK(!debug_heap->old_pointer_space()->was_swept_conservatively()); | |
| 3112 CHECK(!debug_heap->code_space()->was_swept_conservatively()); | |
| 3113 CHECK(!debug_heap->cell_space()->was_swept_conservatively()); | |
| 3114 CHECK(!debug_heap->map_space()->was_swept_conservatively()); | |
| 3115 #endif | |
| 3116 | |
| 3117 // The following code uses heap iterators, so we want the heap to be | |
| 3118 // stable. It should follow TagGlobalObjects as that can allocate. | |
| 3119 AssertNoAllocation no_alloc; | |
| 3120 | |
| 3121 #ifdef VERIFY_HEAP | |
| 3122 debug_heap->Verify(); | |
| 3123 #endif | |
| 3124 | |
| 3125 SetProgressTotal(1); // 1 pass. | |
| 3126 | |
| 3127 #ifdef VERIFY_HEAP | |
| 3128 debug_heap->Verify(); | |
| 3129 #endif | |
| 3130 | |
| 3131 if (!FillReferences()) return false; | |
| 3132 | |
| 3133 snapshot_->FillChildren(); | |
| 3134 snapshot_->RememberLastJSObjectId(); | |
| 3135 | |
| 3136 progress_counter_ = progress_total_; | |
| 3137 if (!ProgressReport(true)) return false; | |
| 3138 return true; | |
| 3139 } | |
| 3140 | |
| 3141 | |
| 3142 void HeapSnapshotGenerator::ProgressStep() { | |
| 3143 ++progress_counter_; | |
| 3144 } | |
| 3145 | |
| 3146 | |
| 3147 bool HeapSnapshotGenerator::ProgressReport(bool force) { | |
| 3148 const int kProgressReportGranularity = 10000; | |
| 3149 if (control_ != NULL | |
| 3150 && (force || progress_counter_ % kProgressReportGranularity == 0)) { | |
| 3151 return | |
| 3152 control_->ReportProgressValue(progress_counter_, progress_total_) == | |
| 3153 v8::ActivityControl::kContinue; | |
| 3154 } | |
| 3155 return true; | |
| 3156 } | |
| 3157 | |
| 3158 | |
| 3159 void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) { | |
| 3160 if (control_ == NULL) return; | |
| 3161 HeapIterator iterator(heap_, HeapIterator::kFilterUnreachable); | |
| 3162 progress_total_ = iterations_count * ( | |
| 3163 v8_heap_explorer_.EstimateObjectsCount(&iterator) + | |
| 3164 dom_explorer_.EstimateObjectsCount()); | |
| 3165 progress_counter_ = 0; | |
| 3166 } | |
| 3167 | |
| 3168 | |
| 3169 bool HeapSnapshotGenerator::FillReferences() { | |
| 3170 SnapshotFiller filler(snapshot_, &entries_); | |
| 3171 v8_heap_explorer_.AddRootEntries(&filler); | |
| 3172 return v8_heap_explorer_.IterateAndExtractReferences(&filler) | |
| 3173 && dom_explorer_.IterateAndExtractReferences(&filler); | |
| 3174 } | |
| 3175 | |
| 3176 | |
| 3177 template<int bytes> struct MaxDecimalDigitsIn; | |
| 3178 template<> struct MaxDecimalDigitsIn<4> { | |
| 3179 static const int kSigned = 11; | |
| 3180 static const int kUnsigned = 10; | |
| 3181 }; | |
| 3182 template<> struct MaxDecimalDigitsIn<8> { | |
| 3183 static const int kSigned = 20; | |
| 3184 static const int kUnsigned = 20; | |
| 3185 }; | |
| 3186 | |
| 3187 | |
| 3188 class OutputStreamWriter { | |
| 3189 public: | |
| 3190 explicit OutputStreamWriter(v8::OutputStream* stream) | |
| 3191 : stream_(stream), | |
| 3192 chunk_size_(stream->GetChunkSize()), | |
| 3193 chunk_(chunk_size_), | |
| 3194 chunk_pos_(0), | |
| 3195 aborted_(false) { | |
| 3196 ASSERT(chunk_size_ > 0); | |
| 3197 } | |
| 3198 bool aborted() { return aborted_; } | |
| 3199 void AddCharacter(char c) { | |
| 3200 ASSERT(c != '\0'); | |
| 3201 ASSERT(chunk_pos_ < chunk_size_); | |
| 3202 chunk_[chunk_pos_++] = c; | |
| 3203 MaybeWriteChunk(); | |
| 3204 } | |
| 3205 void AddString(const char* s) { | |
| 3206 AddSubstring(s, StrLength(s)); | |
| 3207 } | |
| 3208 void AddSubstring(const char* s, int n) { | |
| 3209 if (n <= 0) return; | |
| 3210 ASSERT(static_cast<size_t>(n) <= strlen(s)); | |
| 3211 const char* s_end = s + n; | |
| 3212 while (s < s_end) { | |
| 3213 int s_chunk_size = Min( | |
| 3214 chunk_size_ - chunk_pos_, static_cast<int>(s_end - s)); | |
| 3215 ASSERT(s_chunk_size > 0); | |
| 3216 memcpy(chunk_.start() + chunk_pos_, s, s_chunk_size); | |
| 3217 s += s_chunk_size; | |
| 3218 chunk_pos_ += s_chunk_size; | |
| 3219 MaybeWriteChunk(); | |
| 3220 } | |
| 3221 } | |
| 3222 void AddNumber(unsigned n) { AddNumberImpl<unsigned>(n, "%u"); } | |
| 3223 void Finalize() { | |
| 3224 if (aborted_) return; | |
| 3225 ASSERT(chunk_pos_ < chunk_size_); | |
| 3226 if (chunk_pos_ != 0) { | |
| 3227 WriteChunk(); | |
| 3228 } | |
| 3229 stream_->EndOfStream(); | |
| 3230 } | |
| 3231 | |
| 3232 private: | |
| 3233 template<typename T> | |
| 3234 void AddNumberImpl(T n, const char* format) { | |
| 3235 // Buffer for the longest value plus trailing \0 | |
| 3236 static const int kMaxNumberSize = | |
| 3237 MaxDecimalDigitsIn<sizeof(T)>::kUnsigned + 1; | |
| 3238 if (chunk_size_ - chunk_pos_ >= kMaxNumberSize) { | |
| 3239 int result = OS::SNPrintF( | |
| 3240 chunk_.SubVector(chunk_pos_, chunk_size_), format, n); | |
| 3241 ASSERT(result != -1); | |
| 3242 chunk_pos_ += result; | |
| 3243 MaybeWriteChunk(); | |
| 3244 } else { | |
| 3245 EmbeddedVector<char, kMaxNumberSize> buffer; | |
| 3246 int result = OS::SNPrintF(buffer, format, n); | |
| 3247 USE(result); | |
| 3248 ASSERT(result != -1); | |
| 3249 AddString(buffer.start()); | |
| 3250 } | |
| 3251 } | |
| 3252 void MaybeWriteChunk() { | |
| 3253 ASSERT(chunk_pos_ <= chunk_size_); | |
| 3254 if (chunk_pos_ == chunk_size_) { | |
| 3255 WriteChunk(); | |
| 3256 } | |
| 3257 } | |
| 3258 void WriteChunk() { | |
| 3259 if (aborted_) return; | |
| 3260 if (stream_->WriteAsciiChunk(chunk_.start(), chunk_pos_) == | |
| 3261 v8::OutputStream::kAbort) aborted_ = true; | |
| 3262 chunk_pos_ = 0; | |
| 3263 } | |
| 3264 | |
| 3265 v8::OutputStream* stream_; | |
| 3266 int chunk_size_; | |
| 3267 ScopedVector<char> chunk_; | |
| 3268 int chunk_pos_; | |
| 3269 bool aborted_; | |
| 3270 }; | |
| 3271 | |
| 3272 | |
| 3273 // type, name|index, to_node. | |
| 3274 const int HeapSnapshotJSONSerializer::kEdgeFieldsCount = 3; | |
| 3275 // type, name, id, self_size, children_index. | |
| 3276 const int HeapSnapshotJSONSerializer::kNodeFieldsCount = 5; | |
| 3277 | |
| 3278 void HeapSnapshotJSONSerializer::Serialize(v8::OutputStream* stream) { | |
| 3279 ASSERT(writer_ == NULL); | |
| 3280 writer_ = new OutputStreamWriter(stream); | |
| 3281 | |
| 3282 HeapSnapshot* original_snapshot = NULL; | |
| 3283 if (snapshot_->RawSnapshotSize() >= | |
| 3284 SnapshotSizeConstants<kPointerSize>::kMaxSerializableSnapshotRawSize) { | |
| 3285 // The snapshot is too big. Serialize a fake snapshot. | |
| 3286 original_snapshot = snapshot_; | |
| 3287 snapshot_ = CreateFakeSnapshot(); | |
| 3288 } | |
| 3289 | |
| 3290 SerializeImpl(); | |
| 3291 | |
| 3292 delete writer_; | |
| 3293 writer_ = NULL; | |
| 3294 | |
| 3295 if (original_snapshot != NULL) { | |
| 3296 delete snapshot_; | |
| 3297 snapshot_ = original_snapshot; | |
| 3298 } | |
| 3299 } | |
| 3300 | |
| 3301 | |
| 3302 HeapSnapshot* HeapSnapshotJSONSerializer::CreateFakeSnapshot() { | |
| 3303 HeapSnapshot* result = new HeapSnapshot(snapshot_->collection(), | |
| 3304 HeapSnapshot::kFull, | |
| 3305 snapshot_->title(), | |
| 3306 snapshot_->uid()); | |
| 3307 result->AddRootEntry(); | |
| 3308 const char* text = snapshot_->collection()->names()->GetFormatted( | |
| 3309 "The snapshot is too big. " | |
| 3310 "Maximum snapshot size is %" V8_PTR_PREFIX "u MB. " | |
| 3311 "Actual snapshot size is %" V8_PTR_PREFIX "u MB.", | |
| 3312 SnapshotSizeConstants<kPointerSize>::kMaxSerializableSnapshotRawSize / MB, | |
| 3313 (snapshot_->RawSnapshotSize() + MB - 1) / MB); | |
| 3314 HeapEntry* message = result->AddEntry(HeapEntry::kString, text, 0, 4); | |
| 3315 result->root()->SetIndexedReference(HeapGraphEdge::kElement, 1, message); | |
| 3316 result->FillChildren(); | |
| 3317 return result; | |
| 3318 } | |
| 3319 | |
| 3320 | |
| 3321 void HeapSnapshotJSONSerializer::SerializeImpl() { | |
| 3322 ASSERT(0 == snapshot_->root()->index()); | |
| 3323 writer_->AddCharacter('{'); | |
| 3324 writer_->AddString("\"snapshot\":{"); | |
| 3325 SerializeSnapshot(); | |
| 3326 if (writer_->aborted()) return; | |
| 3327 writer_->AddString("},\n"); | |
| 3328 writer_->AddString("\"nodes\":["); | |
| 3329 SerializeNodes(); | |
| 3330 if (writer_->aborted()) return; | |
| 3331 writer_->AddString("],\n"); | |
| 3332 writer_->AddString("\"edges\":["); | |
| 3333 SerializeEdges(); | |
| 3334 if (writer_->aborted()) return; | |
| 3335 writer_->AddString("],\n"); | |
| 3336 writer_->AddString("\"strings\":["); | |
| 3337 SerializeStrings(); | |
| 3338 if (writer_->aborted()) return; | |
| 3339 writer_->AddCharacter(']'); | |
| 3340 writer_->AddCharacter('}'); | |
| 3341 writer_->Finalize(); | |
| 3342 } | |
| 3343 | |
| 3344 | |
| 3345 int HeapSnapshotJSONSerializer::GetStringId(const char* s) { | |
| 3346 HashMap::Entry* cache_entry = strings_.Lookup( | |
| 3347 const_cast<char*>(s), ObjectHash(s), true); | |
| 3348 if (cache_entry->value == NULL) { | |
| 3349 cache_entry->value = reinterpret_cast<void*>(next_string_id_++); | |
| 3350 } | |
| 3351 return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value)); | |
| 3352 } | |
| 3353 | |
| 3354 | |
| 3355 static int utoa(unsigned value, const Vector<char>& buffer, int buffer_pos) { | |
| 3356 int number_of_digits = 0; | |
| 3357 unsigned t = value; | |
| 3358 do { | |
| 3359 ++number_of_digits; | |
| 3360 } while (t /= 10); | |
| 3361 | |
| 3362 buffer_pos += number_of_digits; | |
| 3363 int result = buffer_pos; | |
| 3364 do { | |
| 3365 int last_digit = value % 10; | |
| 3366 buffer[--buffer_pos] = '0' + last_digit; | |
| 3367 value /= 10; | |
| 3368 } while (value); | |
| 3369 return result; | |
| 3370 } | |
| 3371 | |
| 3372 | |
| 3373 void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge, | |
| 3374 bool first_edge) { | |
| 3375 // The buffer needs space for 3 unsigned ints, 3 commas, \n and \0 | |
| 3376 static const int kBufferSize = | |
| 3377 MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned * 3 + 3 + 2; // NOLINT | |
| 3378 EmbeddedVector<char, kBufferSize> buffer; | |
| 3379 int edge_name_or_index = edge->type() == HeapGraphEdge::kElement | |
| 3380 || edge->type() == HeapGraphEdge::kHidden | |
| 3381 || edge->type() == HeapGraphEdge::kWeak | |
| 3382 ? edge->index() : GetStringId(edge->name()); | |
| 3383 int buffer_pos = 0; | |
| 3384 if (!first_edge) { | |
| 3385 buffer[buffer_pos++] = ','; | |
| 3386 } | |
| 3387 buffer_pos = utoa(edge->type(), buffer, buffer_pos); | |
| 3388 buffer[buffer_pos++] = ','; | |
| 3389 buffer_pos = utoa(edge_name_or_index, buffer, buffer_pos); | |
| 3390 buffer[buffer_pos++] = ','; | |
| 3391 buffer_pos = utoa(entry_index(edge->to()), buffer, buffer_pos); | |
| 3392 buffer[buffer_pos++] = '\n'; | |
| 3393 buffer[buffer_pos++] = '\0'; | |
| 3394 writer_->AddString(buffer.start()); | |
| 3395 } | |
| 3396 | |
| 3397 | |
| 3398 void HeapSnapshotJSONSerializer::SerializeEdges() { | |
| 3399 List<HeapGraphEdge*>& edges = snapshot_->children(); | |
| 3400 for (int i = 0; i < edges.length(); ++i) { | |
| 3401 ASSERT(i == 0 || | |
| 3402 edges[i - 1]->from()->index() <= edges[i]->from()->index()); | |
| 3403 SerializeEdge(edges[i], i == 0); | |
| 3404 if (writer_->aborted()) return; | |
| 3405 } | |
| 3406 } | |
| 3407 | |
| 3408 | |
| 3409 void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) { | |
| 3410 // The buffer needs space for 5 unsigned ints, 5 commas, \n and \0 | |
| 3411 static const int kBufferSize = | |
| 3412 5 * MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned // NOLINT | |
| 3413 + 5 + 1 + 1; | |
| 3414 EmbeddedVector<char, kBufferSize> buffer; | |
| 3415 int buffer_pos = 0; | |
| 3416 if (entry_index(entry) != 0) { | |
| 3417 buffer[buffer_pos++] = ','; | |
| 3418 } | |
| 3419 buffer_pos = utoa(entry->type(), buffer, buffer_pos); | |
| 3420 buffer[buffer_pos++] = ','; | |
| 3421 buffer_pos = utoa(GetStringId(entry->name()), buffer, buffer_pos); | |
| 3422 buffer[buffer_pos++] = ','; | |
| 3423 buffer_pos = utoa(entry->id(), buffer, buffer_pos); | |
| 3424 buffer[buffer_pos++] = ','; | |
| 3425 buffer_pos = utoa(entry->self_size(), buffer, buffer_pos); | |
| 3426 buffer[buffer_pos++] = ','; | |
| 3427 buffer_pos = utoa(entry->children_count(), buffer, buffer_pos); | |
| 3428 buffer[buffer_pos++] = '\n'; | |
| 3429 buffer[buffer_pos++] = '\0'; | |
| 3430 writer_->AddString(buffer.start()); | |
| 3431 } | |
| 3432 | |
| 3433 | |
| 3434 void HeapSnapshotJSONSerializer::SerializeNodes() { | |
| 3435 List<HeapEntry>& entries = snapshot_->entries(); | |
| 3436 for (int i = 0; i < entries.length(); ++i) { | |
| 3437 SerializeNode(&entries[i]); | |
| 3438 if (writer_->aborted()) return; | |
| 3439 } | |
| 3440 } | |
| 3441 | |
| 3442 | |
| 3443 void HeapSnapshotJSONSerializer::SerializeSnapshot() { | |
| 3444 writer_->AddString("\"title\":\""); | |
| 3445 writer_->AddString(snapshot_->title()); | |
| 3446 writer_->AddString("\""); | |
| 3447 writer_->AddString(",\"uid\":"); | |
| 3448 writer_->AddNumber(snapshot_->uid()); | |
| 3449 writer_->AddString(",\"meta\":"); | |
| 3450 // The object describing node serialization layout. | |
| 3451 // We use a set of macros to improve readability. | |
| 3452 #define JSON_A(s) "[" s "]" | |
| 3453 #define JSON_O(s) "{" s "}" | |
| 3454 #define JSON_S(s) "\"" s "\"" | |
| 3455 writer_->AddString(JSON_O( | |
| 3456 JSON_S("node_fields") ":" JSON_A( | |
| 3457 JSON_S("type") "," | |
| 3458 JSON_S("name") "," | |
| 3459 JSON_S("id") "," | |
| 3460 JSON_S("self_size") "," | |
| 3461 JSON_S("edge_count")) "," | |
| 3462 JSON_S("node_types") ":" JSON_A( | |
| 3463 JSON_A( | |
| 3464 JSON_S("hidden") "," | |
| 3465 JSON_S("array") "," | |
| 3466 JSON_S("string") "," | |
| 3467 JSON_S("object") "," | |
| 3468 JSON_S("code") "," | |
| 3469 JSON_S("closure") "," | |
| 3470 JSON_S("regexp") "," | |
| 3471 JSON_S("number") "," | |
| 3472 JSON_S("native") "," | |
| 3473 JSON_S("synthetic")) "," | |
| 3474 JSON_S("string") "," | |
| 3475 JSON_S("number") "," | |
| 3476 JSON_S("number") "," | |
| 3477 JSON_S("number") "," | |
| 3478 JSON_S("number") "," | |
| 3479 JSON_S("number")) "," | |
| 3480 JSON_S("edge_fields") ":" JSON_A( | |
| 3481 JSON_S("type") "," | |
| 3482 JSON_S("name_or_index") "," | |
| 3483 JSON_S("to_node")) "," | |
| 3484 JSON_S("edge_types") ":" JSON_A( | |
| 3485 JSON_A( | |
| 3486 JSON_S("context") "," | |
| 3487 JSON_S("element") "," | |
| 3488 JSON_S("property") "," | |
| 3489 JSON_S("internal") "," | |
| 3490 JSON_S("hidden") "," | |
| 3491 JSON_S("shortcut") "," | |
| 3492 JSON_S("weak")) "," | |
| 3493 JSON_S("string_or_number") "," | |
| 3494 JSON_S("node")))); | |
| 3495 #undef JSON_S | |
| 3496 #undef JSON_O | |
| 3497 #undef JSON_A | |
| 3498 writer_->AddString(",\"node_count\":"); | |
| 3499 writer_->AddNumber(snapshot_->entries().length()); | |
| 3500 writer_->AddString(",\"edge_count\":"); | |
| 3501 writer_->AddNumber(snapshot_->edges().length()); | |
| 3502 } | |
| 3503 | |
| 3504 | |
| 3505 static void WriteUChar(OutputStreamWriter* w, unibrow::uchar u) { | |
| 3506 static const char hex_chars[] = "0123456789ABCDEF"; | |
| 3507 w->AddString("\\u"); | |
| 3508 w->AddCharacter(hex_chars[(u >> 12) & 0xf]); | |
| 3509 w->AddCharacter(hex_chars[(u >> 8) & 0xf]); | |
| 3510 w->AddCharacter(hex_chars[(u >> 4) & 0xf]); | |
| 3511 w->AddCharacter(hex_chars[u & 0xf]); | |
| 3512 } | |
| 3513 | |
| 3514 void HeapSnapshotJSONSerializer::SerializeString(const unsigned char* s) { | |
| 3515 writer_->AddCharacter('\n'); | |
| 3516 writer_->AddCharacter('\"'); | |
| 3517 for ( ; *s != '\0'; ++s) { | |
| 3518 switch (*s) { | |
| 3519 case '\b': | |
| 3520 writer_->AddString("\\b"); | |
| 3521 continue; | |
| 3522 case '\f': | |
| 3523 writer_->AddString("\\f"); | |
| 3524 continue; | |
| 3525 case '\n': | |
| 3526 writer_->AddString("\\n"); | |
| 3527 continue; | |
| 3528 case '\r': | |
| 3529 writer_->AddString("\\r"); | |
| 3530 continue; | |
| 3531 case '\t': | |
| 3532 writer_->AddString("\\t"); | |
| 3533 continue; | |
| 3534 case '\"': | |
| 3535 case '\\': | |
| 3536 writer_->AddCharacter('\\'); | |
| 3537 writer_->AddCharacter(*s); | |
| 3538 continue; | |
| 3539 default: | |
| 3540 if (*s > 31 && *s < 128) { | |
| 3541 writer_->AddCharacter(*s); | |
| 3542 } else if (*s <= 31) { | |
| 3543 // Special character with no dedicated literal. | |
| 3544 WriteUChar(writer_, *s); | |
| 3545 } else { | |
| 3546 // Convert UTF-8 into \u UTF-16 literal. | |
| 3547 unsigned length = 1, cursor = 0; | |
| 3548 for ( ; length <= 4 && *(s + length) != '\0'; ++length) { } | |
| 3549 unibrow::uchar c = unibrow::Utf8::CalculateValue(s, length, &cursor); | |
| 3550 if (c != unibrow::Utf8::kBadChar) { | |
| 3551 WriteUChar(writer_, c); | |
| 3552 ASSERT(cursor != 0); | |
| 3553 s += cursor - 1; | |
| 3554 } else { | |
| 3555 writer_->AddCharacter('?'); | |
| 3556 } | |
| 3557 } | |
| 3558 } | |
| 3559 } | |
| 3560 writer_->AddCharacter('\"'); | |
| 3561 } | |
| 3562 | |
| 3563 | |
| 3564 void HeapSnapshotJSONSerializer::SerializeStrings() { | |
| 3565 List<HashMap::Entry*> sorted_strings; | |
| 3566 SortHashMap(&strings_, &sorted_strings); | |
| 3567 writer_->AddString("\"<dummy>\""); | |
| 3568 for (int i = 0; i < sorted_strings.length(); ++i) { | |
| 3569 writer_->AddCharacter(','); | |
| 3570 SerializeString( | |
| 3571 reinterpret_cast<const unsigned char*>(sorted_strings[i]->key)); | |
| 3572 if (writer_->aborted()) return; | |
| 3573 } | |
| 3574 } | |
| 3575 | |
| 3576 | |
| 3577 template<typename T> | |
| 3578 inline static int SortUsingEntryValue(const T* x, const T* y) { | |
| 3579 uintptr_t x_uint = reinterpret_cast<uintptr_t>((*x)->value); | |
| 3580 uintptr_t y_uint = reinterpret_cast<uintptr_t>((*y)->value); | |
| 3581 if (x_uint > y_uint) { | |
| 3582 return 1; | |
| 3583 } else if (x_uint == y_uint) { | |
| 3584 return 0; | |
| 3585 } else { | |
| 3586 return -1; | |
| 3587 } | |
| 3588 } | |
| 3589 | |
| 3590 | |
| 3591 void HeapSnapshotJSONSerializer::SortHashMap( | |
| 3592 HashMap* map, List<HashMap::Entry*>* sorted_entries) { | |
| 3593 for (HashMap::Entry* p = map->Start(); p != NULL; p = map->Next(p)) | |
| 3594 sorted_entries->Add(p); | |
| 3595 sorted_entries->Sort(SortUsingEntryValue); | |
| 3596 } | |
| 3597 | |
| 3598 } } // namespace v8::internal | 945 } } // namespace v8::internal |
| OLD | NEW |