OLD | NEW |
1 // Copyright 2010 the V8 project authors. All rights reserved. | 1 // Copyright 2010 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 11 matching lines...) Expand all Loading... |
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
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 #ifdef ENABLE_LOGGING_AND_PROFILING | 28 #ifdef ENABLE_LOGGING_AND_PROFILING |
29 | 29 |
30 #include "v8.h" | 30 #include "v8.h" |
31 #include "global-handles.h" | 31 #include "global-handles.h" |
| 32 #include "scopeinfo.h" |
| 33 #include "top.h" |
| 34 #include "zone-inl.h" |
32 | 35 |
33 #include "profile-generator-inl.h" | 36 #include "profile-generator-inl.h" |
34 | 37 |
35 #include "../include/v8-profiler.h" | |
36 | |
37 namespace v8 { | 38 namespace v8 { |
38 namespace internal { | 39 namespace internal { |
39 | 40 |
40 | 41 |
41 TokenEnumerator::TokenEnumerator() | 42 TokenEnumerator::TokenEnumerator() |
42 : token_locations_(4), | 43 : token_locations_(4), |
43 token_removed_(4) { | 44 token_removed_(4) { |
44 } | 45 } |
45 | 46 |
46 | 47 |
(...skipping 757 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
804 } | 805 } |
805 // If no frames were symbolized, put the VM state entry in. | 806 // If no frames were symbolized, put the VM state entry in. |
806 if (no_symbolized_entries) { | 807 if (no_symbolized_entries) { |
807 *entry++ = EntryForVMState(sample.state); | 808 *entry++ = EntryForVMState(sample.state); |
808 } | 809 } |
809 } | 810 } |
810 | 811 |
811 profiles_->AddPathToCurrentProfiles(entries); | 812 profiles_->AddPathToCurrentProfiles(entries); |
812 } | 813 } |
813 | 814 |
| 815 |
| 816 HeapGraphEdge::HeapGraphEdge(Type type, |
| 817 const char* name, |
| 818 HeapEntry* from, |
| 819 HeapEntry* to) |
| 820 : type_(type), name_(name), from_(from), to_(to) { |
| 821 ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY); |
| 822 } |
| 823 |
| 824 |
| 825 HeapGraphEdge::HeapGraphEdge(int index, |
| 826 HeapEntry* from, |
| 827 HeapEntry* to) |
| 828 : type_(ELEMENT), index_(index), from_(from), to_(to) { |
| 829 } |
| 830 |
| 831 |
| 832 static void DeleteHeapGraphEdge(HeapGraphEdge** edge_ptr) { |
| 833 delete *edge_ptr; |
| 834 } |
| 835 |
| 836 |
| 837 static void DeleteHeapGraphPath(HeapGraphPath** path_ptr) { |
| 838 delete *path_ptr; |
| 839 } |
| 840 |
| 841 |
| 842 HeapEntry::~HeapEntry() { |
| 843 children_.Iterate(DeleteHeapGraphEdge); |
| 844 retaining_paths_.Iterate(DeleteHeapGraphPath); |
| 845 } |
| 846 |
| 847 |
| 848 void HeapEntry::SetClosureReference(const char* name, HeapEntry* entry) { |
| 849 HeapGraphEdge* edge = |
| 850 new HeapGraphEdge(HeapGraphEdge::CONTEXT_VARIABLE, name, this, entry); |
| 851 children_.Add(edge); |
| 852 entry->retainers_.Add(edge); |
| 853 } |
| 854 |
| 855 |
| 856 void HeapEntry::SetElementReference(int index, HeapEntry* entry) { |
| 857 HeapGraphEdge* edge = new HeapGraphEdge(index, this, entry); |
| 858 children_.Add(edge); |
| 859 entry->retainers_.Add(edge); |
| 860 } |
| 861 |
| 862 |
| 863 void HeapEntry::SetPropertyReference(const char* name, HeapEntry* entry) { |
| 864 HeapGraphEdge* edge = |
| 865 new HeapGraphEdge(HeapGraphEdge::PROPERTY, name, this, entry); |
| 866 children_.Add(edge); |
| 867 entry->retainers_.Add(edge); |
| 868 } |
| 869 |
| 870 |
| 871 void HeapEntry::SetAutoIndexReference(HeapEntry* entry) { |
| 872 SetElementReference(next_auto_index_++, entry); |
| 873 } |
| 874 |
| 875 |
| 876 int HeapEntry::TotalSize() { |
| 877 return total_size_ != kUnknownSize ? total_size_ : CalculateTotalSize(); |
| 878 } |
| 879 |
| 880 |
| 881 int HeapEntry::NonSharedTotalSize() { |
| 882 return non_shared_total_size_ != kUnknownSize ? |
| 883 non_shared_total_size_ : CalculateNonSharedTotalSize(); |
| 884 } |
| 885 |
| 886 |
| 887 int HeapEntry::CalculateTotalSize() { |
| 888 snapshot_->ClearPaint(); |
| 889 List<HeapEntry*> list(10); |
| 890 list.Add(this); |
| 891 total_size_ = self_size_; |
| 892 this->PaintReachable(); |
| 893 while (!list.is_empty()) { |
| 894 HeapEntry* entry = list.RemoveLast(); |
| 895 const int children_count = entry->children_.length(); |
| 896 for (int i = 0; i < children_count; ++i) { |
| 897 HeapEntry* child = entry->children_[i]->to(); |
| 898 if (!child->painted_reachable()) { |
| 899 list.Add(child); |
| 900 child->PaintReachable(); |
| 901 total_size_ += child->self_size_; |
| 902 } |
| 903 } |
| 904 } |
| 905 return total_size_; |
| 906 } |
| 907 |
| 908 |
| 909 namespace { |
| 910 |
| 911 class NonSharedSizeCalculator { |
| 912 public: |
| 913 NonSharedSizeCalculator() |
| 914 : non_shared_total_size_(0) { |
| 915 } |
| 916 |
| 917 int non_shared_total_size() const { return non_shared_total_size_; } |
| 918 |
| 919 void Apply(HeapEntry* entry) { |
| 920 if (entry->painted_reachable()) { |
| 921 non_shared_total_size_ += entry->self_size(); |
| 922 } |
| 923 } |
| 924 |
| 925 private: |
| 926 int non_shared_total_size_; |
| 927 }; |
| 928 |
| 929 } // namespace |
| 930 |
| 931 int HeapEntry::CalculateNonSharedTotalSize() { |
| 932 // To calculate non-shared total size, first we paint all reachable |
| 933 // nodes in one color, then we paint all nodes reachable from other |
| 934 // nodes with a different color. Then we consider only nodes painted |
| 935 // with the first color for caclulating the total size. |
| 936 snapshot_->ClearPaint(); |
| 937 List<HeapEntry*> list(10); |
| 938 list.Add(this); |
| 939 this->PaintReachable(); |
| 940 while (!list.is_empty()) { |
| 941 HeapEntry* entry = list.RemoveLast(); |
| 942 const int children_count = entry->children_.length(); |
| 943 for (int i = 0; i < children_count; ++i) { |
| 944 HeapEntry* child = entry->children_[i]->to(); |
| 945 if (!child->painted_reachable()) { |
| 946 list.Add(child); |
| 947 child->PaintReachable(); |
| 948 } |
| 949 } |
| 950 } |
| 951 |
| 952 List<HeapEntry*> list2(10); |
| 953 if (this != snapshot_->root()) { |
| 954 list2.Add(snapshot_->root()); |
| 955 snapshot_->root()->PaintReachableFromOthers(); |
| 956 } |
| 957 while (!list2.is_empty()) { |
| 958 HeapEntry* entry = list2.RemoveLast(); |
| 959 const int children_count = entry->children_.length(); |
| 960 for (int i = 0; i < children_count; ++i) { |
| 961 HeapEntry* child = entry->children_[i]->to(); |
| 962 if (child != this && child->not_painted_reachable_from_others()) { |
| 963 list2.Add(child); |
| 964 child->PaintReachableFromOthers(); |
| 965 } |
| 966 } |
| 967 } |
| 968 |
| 969 NonSharedSizeCalculator calculator; |
| 970 snapshot_->IterateEntries(&calculator); |
| 971 return calculator.non_shared_total_size(); |
| 972 } |
| 973 |
| 974 |
| 975 class CachedHeapGraphPath { |
| 976 public: |
| 977 CachedHeapGraphPath() |
| 978 : nodes_(NodesMatch) { } |
| 979 CachedHeapGraphPath(const CachedHeapGraphPath& src) |
| 980 : nodes_(NodesMatch, &HashMap::DefaultAllocator, src.nodes_.capacity()), |
| 981 path_(src.path_.length() + 1) { |
| 982 for (HashMap::Entry* p = src.nodes_.Start(); |
| 983 p != NULL; |
| 984 p = src.nodes_.Next(p)) { |
| 985 nodes_.Lookup(p->key, p->hash, true); |
| 986 } |
| 987 path_.AddAll(src.path_); |
| 988 } |
| 989 void Add(HeapGraphEdge* edge) { |
| 990 nodes_.Lookup(edge->to(), Hash(edge->to()), true); |
| 991 path_.Add(edge); |
| 992 } |
| 993 bool ContainsNode(HeapEntry* node) { |
| 994 return nodes_.Lookup(node, Hash(node), false) != NULL; |
| 995 } |
| 996 const List<HeapGraphEdge*>* path() const { return &path_; } |
| 997 |
| 998 private: |
| 999 static uint32_t Hash(HeapEntry* entry) { |
| 1000 return static_cast<uint32_t>(reinterpret_cast<intptr_t>(entry)); |
| 1001 } |
| 1002 static bool NodesMatch(void* key1, void* key2) { return key1 == key2; } |
| 1003 |
| 1004 HashMap nodes_; |
| 1005 List<HeapGraphEdge*> path_; |
| 1006 }; |
| 1007 |
| 1008 |
| 1009 const List<HeapGraphPath*>* HeapEntry::GetRetainingPaths() { |
| 1010 if (retaining_paths_.length() == 0 && retainers_.length() != 0) { |
| 1011 CachedHeapGraphPath path; |
| 1012 FindRetainingPaths(this, &path); |
| 1013 } |
| 1014 return &retaining_paths_; |
| 1015 } |
| 1016 |
| 1017 |
| 1018 void HeapEntry::FindRetainingPaths(HeapEntry* node, |
| 1019 CachedHeapGraphPath* prev_path) { |
| 1020 for (int i = 0; i < node->retainers_.length(); ++i) { |
| 1021 HeapGraphEdge* ret_edge = node->retainers_[i]; |
| 1022 if (prev_path->ContainsNode(ret_edge->from())) continue; |
| 1023 if (ret_edge->from() != snapshot_->root()) { |
| 1024 CachedHeapGraphPath path(*prev_path); |
| 1025 path.Add(ret_edge); |
| 1026 FindRetainingPaths(ret_edge->from(), &path); |
| 1027 } else { |
| 1028 HeapGraphPath* ret_path = new HeapGraphPath(*prev_path->path()); |
| 1029 ret_path->Set(0, ret_edge); |
| 1030 retaining_paths_.Add(ret_path); |
| 1031 } |
| 1032 } |
| 1033 } |
| 1034 |
| 1035 |
| 1036 static void RemoveEdge(List<HeapGraphEdge*>* list, HeapGraphEdge* edge) { |
| 1037 for (int i = 0; i < list->length(); ) { |
| 1038 if (list->at(i) == edge) { |
| 1039 list->Remove(i); |
| 1040 return; |
| 1041 } else { |
| 1042 ++i; |
| 1043 } |
| 1044 } |
| 1045 UNREACHABLE(); |
| 1046 } |
| 1047 |
| 1048 |
| 1049 void HeapEntry::RemoveChild(HeapGraphEdge* edge) { |
| 1050 RemoveEdge(&children_, edge); |
| 1051 delete edge; |
| 1052 } |
| 1053 |
| 1054 |
| 1055 void HeapEntry::RemoveRetainer(HeapGraphEdge* edge) { |
| 1056 RemoveEdge(&retainers_, edge); |
| 1057 } |
| 1058 |
| 1059 |
| 1060 void HeapEntry::CutEdges() { |
| 1061 for (int i = 0; i < children_.length(); ++i) { |
| 1062 HeapGraphEdge* edge = children_[i]; |
| 1063 edge->to()->RemoveRetainer(edge); |
| 1064 } |
| 1065 children_.Iterate(DeleteHeapGraphEdge); |
| 1066 children_.Clear(); |
| 1067 |
| 1068 for (int i = 0; i < retainers_.length(); ++i) { |
| 1069 HeapGraphEdge* edge = retainers_[i]; |
| 1070 edge->from()->RemoveChild(edge); |
| 1071 } |
| 1072 retainers_.Clear(); |
| 1073 } |
| 1074 |
| 1075 |
| 1076 void HeapEntry::Print(int max_depth, int indent) { |
| 1077 OS::Print("%6d %6d %6d", self_size_, TotalSize(), NonSharedTotalSize()); |
| 1078 if (type_ != STRING) { |
| 1079 OS::Print("%s %.40s\n", TypeAsString(), name_); |
| 1080 } else { |
| 1081 OS::Print("\""); |
| 1082 const char* c = name_; |
| 1083 while (*c && (c - name_) <= 40) { |
| 1084 if (*c != '\n') |
| 1085 OS::Print("%c", *c); |
| 1086 else |
| 1087 OS::Print("\\n"); |
| 1088 ++c; |
| 1089 } |
| 1090 OS::Print("\"\n"); |
| 1091 } |
| 1092 if (--max_depth == 0) return; |
| 1093 const int children_count = children_.length(); |
| 1094 for (int i = 0; i < children_count; ++i) { |
| 1095 HeapGraphEdge* edge = children_[i]; |
| 1096 switch (edge->type()) { |
| 1097 case HeapGraphEdge::CONTEXT_VARIABLE: |
| 1098 OS::Print(" %*c #%s: ", indent, ' ', edge->name()); |
| 1099 break; |
| 1100 case HeapGraphEdge::ELEMENT: |
| 1101 OS::Print(" %*c %d: ", indent, ' ', edge->index()); |
| 1102 break; |
| 1103 case HeapGraphEdge::PROPERTY: |
| 1104 OS::Print(" %*c %s: ", indent, ' ', edge->name()); |
| 1105 break; |
| 1106 default: |
| 1107 OS::Print("!!! unknown edge type: %d ", edge->type()); |
| 1108 } |
| 1109 edge->to()->Print(max_depth, indent + 2); |
| 1110 } |
| 1111 } |
| 1112 |
| 1113 |
| 1114 const char* HeapEntry::TypeAsString() { |
| 1115 switch (type_) { |
| 1116 case INTERNAL: return "/internal/"; |
| 1117 case JS_OBJECT: return "/object/"; |
| 1118 case CLOSURE: return "/closure/"; |
| 1119 case STRING: return "/string/"; |
| 1120 case CODE: return "/code/"; |
| 1121 case ARRAY: return "/array/"; |
| 1122 default: return "???"; |
| 1123 } |
| 1124 } |
| 1125 |
| 1126 |
| 1127 HeapGraphPath::HeapGraphPath(const List<HeapGraphEdge*>& path) |
| 1128 : path_(path.length() + 1) { |
| 1129 Add(NULL); |
| 1130 for (int i = path.length() - 1; i >= 0; --i) { |
| 1131 Add(path[i]); |
| 1132 } |
| 1133 } |
| 1134 |
| 1135 |
| 1136 void HeapGraphPath::Print() { |
| 1137 path_[0]->from()->Print(1, 0); |
| 1138 for (int i = 0; i < path_.length(); ++i) { |
| 1139 OS::Print(" -> "); |
| 1140 HeapGraphEdge* edge = path_[i]; |
| 1141 switch (edge->type()) { |
| 1142 case HeapGraphEdge::CONTEXT_VARIABLE: |
| 1143 OS::Print("[#%s] ", edge->name()); |
| 1144 break; |
| 1145 case HeapGraphEdge::ELEMENT: |
| 1146 OS::Print("[%d] ", edge->index()); |
| 1147 break; |
| 1148 case HeapGraphEdge::PROPERTY: |
| 1149 OS::Print("[%s] ", edge->name()); |
| 1150 break; |
| 1151 default: |
| 1152 OS::Print("!!! unknown edge type: %d ", edge->type()); |
| 1153 } |
| 1154 edge->to()->Print(1, 0); |
| 1155 } |
| 1156 OS::Print("\n"); |
| 1157 } |
| 1158 |
| 1159 |
| 1160 class IndexedReferencesExtractor : public ObjectVisitor { |
| 1161 public: |
| 1162 IndexedReferencesExtractor(HeapSnapshot* snapshot, HeapEntry* parent) |
| 1163 : snapshot_(snapshot), |
| 1164 parent_(parent) { |
| 1165 } |
| 1166 |
| 1167 void VisitPointer(Object** o) { |
| 1168 if (!(*o)->IsHeapObject()) return; |
| 1169 HeapEntry* entry = snapshot_->GetEntry(HeapObject::cast(*o)); |
| 1170 if (entry != NULL) { |
| 1171 parent_->SetAutoIndexReference(entry); |
| 1172 } |
| 1173 } |
| 1174 |
| 1175 void VisitPointers(Object** start, Object** end) { |
| 1176 for (Object** p = start; p < end; p++) VisitPointer(p); |
| 1177 } |
| 1178 |
| 1179 private: |
| 1180 HeapSnapshot* snapshot_; |
| 1181 HeapEntry* parent_; |
| 1182 }; |
| 1183 |
| 1184 |
| 1185 HeapEntriesMap::HeapEntriesMap() |
| 1186 : entries_(HeapObjectsMatch) { |
| 1187 } |
| 1188 |
| 1189 |
| 1190 HeapEntriesMap::~HeapEntriesMap() { |
| 1191 for (HashMap::Entry* p = entries_.Start(); |
| 1192 p != NULL; |
| 1193 p = entries_.Next(p)) { |
| 1194 if (!IsAlias(p->value)) delete reinterpret_cast<HeapEntry*>(p->value); |
| 1195 } |
| 1196 } |
| 1197 |
| 1198 |
| 1199 void HeapEntriesMap::Alias(HeapObject* object, HeapEntry* entry) { |
| 1200 HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), true); |
| 1201 if (cache_entry->value == NULL) |
| 1202 cache_entry->value = reinterpret_cast<void*>( |
| 1203 reinterpret_cast<intptr_t>(entry) | kAliasTag); |
| 1204 } |
| 1205 |
| 1206 |
| 1207 void HeapEntriesMap::Apply(void (HeapEntry::*Func)(void)) { |
| 1208 for (HashMap::Entry* p = entries_.Start(); |
| 1209 p != NULL; |
| 1210 p = entries_.Next(p)) { |
| 1211 if (!IsAlias(p->value)) (reinterpret_cast<HeapEntry*>(p->value)->*Func)(); |
| 1212 } |
| 1213 } |
| 1214 |
| 1215 |
| 1216 HeapEntry* HeapEntriesMap::Map(HeapObject* object) { |
| 1217 HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), false); |
| 1218 return cache_entry != NULL ? |
| 1219 reinterpret_cast<HeapEntry*>( |
| 1220 reinterpret_cast<intptr_t>(cache_entry->value) & (~kAliasTag)) : NULL; |
| 1221 } |
| 1222 |
| 1223 |
| 1224 void HeapEntriesMap::Pair(HeapObject* object, HeapEntry* entry) { |
| 1225 HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), true); |
| 1226 ASSERT(cache_entry->value == NULL); |
| 1227 cache_entry->value = entry; |
| 1228 } |
| 1229 |
| 1230 |
| 1231 HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, |
| 1232 const char* title, |
| 1233 unsigned uid) |
| 1234 : collection_(collection), |
| 1235 title_(title), |
| 1236 uid_(uid), |
| 1237 root_(this) { |
| 1238 } |
| 1239 |
| 1240 |
| 1241 void HeapSnapshot::ClearPaint() { |
| 1242 root_.ClearPaint(); |
| 1243 entries_.Apply(&HeapEntry::ClearPaint); |
| 1244 } |
| 1245 |
| 1246 |
| 1247 HeapEntry* HeapSnapshot::GetEntry(Object* obj) { |
| 1248 if (!obj->IsHeapObject()) return NULL; |
| 1249 HeapObject* object = HeapObject::cast(obj); |
| 1250 |
| 1251 { |
| 1252 HeapEntry* existing = FindEntry(object); |
| 1253 if (existing != NULL) return existing; |
| 1254 } |
| 1255 |
| 1256 // Add new entry. |
| 1257 if (object->IsJSFunction()) { |
| 1258 JSFunction* func = JSFunction::cast(object); |
| 1259 SharedFunctionInfo* shared = func->shared(); |
| 1260 String* name = String::cast(shared->name())->length() > 0 ? |
| 1261 String::cast(shared->name()) : shared->inferred_name(); |
| 1262 return AddEntry(object, HeapEntry::CLOSURE, collection_->GetName(name)); |
| 1263 } else if (object->IsJSObject()) { |
| 1264 return AddEntry(object, |
| 1265 HeapEntry::JS_OBJECT, |
| 1266 collection_->GetName( |
| 1267 JSObject::cast(object)->constructor_name())); |
| 1268 } else if (object->IsJSGlobalPropertyCell()) { |
| 1269 HeapEntry* value = GetEntry(JSGlobalPropertyCell::cast(object)->value()); |
| 1270 // If GPC references an object that we have interest in, add the object. |
| 1271 // We don't store HeapEntries for GPCs. Instead, we make our hash map |
| 1272 // to point to object's HeapEntry by GPCs address. |
| 1273 if (value != NULL) AddEntryAlias(object, value); |
| 1274 return value; |
| 1275 } else if (object->IsString()) { |
| 1276 return AddEntry(object, |
| 1277 HeapEntry::STRING, |
| 1278 collection_->GetName(String::cast(object))); |
| 1279 } else if (object->IsCode() |
| 1280 || object->IsSharedFunctionInfo() |
| 1281 || object->IsScript()) { |
| 1282 return AddEntry(object, HeapEntry::CODE); |
| 1283 } else if (object->IsFixedArray()) { |
| 1284 return AddEntry(object, HeapEntry::ARRAY); |
| 1285 } |
| 1286 // No interest in this object. |
| 1287 return NULL; |
| 1288 } |
| 1289 |
| 1290 |
| 1291 void HeapSnapshot::SetClosureReference(HeapEntry* parent, |
| 1292 String* reference_name, |
| 1293 Object* child) { |
| 1294 HeapEntry* child_entry = GetEntry(child); |
| 1295 if (child_entry != NULL) { |
| 1296 parent->SetClosureReference( |
| 1297 collection_->GetName(reference_name), child_entry); |
| 1298 } |
| 1299 } |
| 1300 |
| 1301 |
| 1302 void HeapSnapshot::SetElementReference(HeapEntry* parent, |
| 1303 int index, |
| 1304 Object* child) { |
| 1305 HeapEntry* child_entry = GetEntry(child); |
| 1306 if (child_entry != NULL) { |
| 1307 parent->SetElementReference(index, child_entry); |
| 1308 } |
| 1309 } |
| 1310 |
| 1311 |
| 1312 void HeapSnapshot::SetPropertyReference(HeapEntry* parent, |
| 1313 String* reference_name, |
| 1314 Object* child) { |
| 1315 HeapEntry* child_entry = GetEntry(child); |
| 1316 if (child_entry != NULL) { |
| 1317 parent->SetPropertyReference( |
| 1318 collection_->GetName(reference_name), child_entry); |
| 1319 } |
| 1320 } |
| 1321 |
| 1322 |
| 1323 HeapEntry* HeapSnapshot::AddEntry(HeapObject* object, |
| 1324 HeapEntry::Type type, |
| 1325 const char* name) { |
| 1326 HeapEntry* entry = new HeapEntry(this, |
| 1327 type, |
| 1328 name, |
| 1329 GetObjectSize(object), |
| 1330 GetObjectSecurityToken(object)); |
| 1331 entries_.Pair(object, entry); |
| 1332 |
| 1333 // Detect, if this is a JS global object of the current context, and |
| 1334 // add it to snapshot's roots. There can be several JS global objects |
| 1335 // in a context. |
| 1336 if (object->IsJSGlobalProxy()) { |
| 1337 int global_security_token = GetGlobalSecurityToken(); |
| 1338 int object_security_token = |
| 1339 collection_->token_enumerator()->GetTokenId( |
| 1340 Context::cast( |
| 1341 JSGlobalProxy::cast(object)->context())->security_token()); |
| 1342 if (object_security_token == TokenEnumerator::kNoSecurityToken |
| 1343 || object_security_token == global_security_token) { |
| 1344 HeapEntry* global_object_entry = |
| 1345 GetEntry(HeapObject::cast(object->map()->prototype())); |
| 1346 ASSERT(global_object_entry != NULL); |
| 1347 root_.SetAutoIndexReference(global_object_entry); |
| 1348 } |
| 1349 } |
| 1350 |
| 1351 return entry; |
| 1352 } |
| 1353 |
| 1354 |
| 1355 namespace { |
| 1356 |
| 1357 class EdgesCutter { |
| 1358 public: |
| 1359 explicit EdgesCutter(int global_security_token) |
| 1360 : global_security_token_(global_security_token) { |
| 1361 } |
| 1362 |
| 1363 void Apply(HeapEntry* entry) { |
| 1364 if (entry->security_token_id() != TokenEnumerator::kNoSecurityToken |
| 1365 && entry->security_token_id() != global_security_token_) { |
| 1366 entry->CutEdges(); |
| 1367 } |
| 1368 } |
| 1369 |
| 1370 private: |
| 1371 const int global_security_token_; |
| 1372 }; |
| 1373 |
| 1374 } // namespace |
| 1375 |
| 1376 void HeapSnapshot::CutObjectsFromForeignSecurityContexts() { |
| 1377 EdgesCutter cutter(GetGlobalSecurityToken()); |
| 1378 entries_.Apply(&cutter); |
| 1379 } |
| 1380 |
| 1381 |
| 1382 int HeapSnapshot::GetGlobalSecurityToken() { |
| 1383 return collection_->token_enumerator()->GetTokenId( |
| 1384 Top::context()->global()->global_context()->security_token()); |
| 1385 } |
| 1386 |
| 1387 |
| 1388 int HeapSnapshot::GetObjectSize(HeapObject* obj) { |
| 1389 return obj->IsJSObject() ? |
| 1390 CalculateNetworkSize(JSObject::cast(obj)) : obj->Size(); |
| 1391 } |
| 1392 |
| 1393 |
| 1394 int HeapSnapshot::GetObjectSecurityToken(HeapObject* obj) { |
| 1395 if (obj->IsGlobalContext()) { |
| 1396 return collection_->token_enumerator()->GetTokenId( |
| 1397 Context::cast(obj)->security_token()); |
| 1398 } else { |
| 1399 return TokenEnumerator::kNoSecurityToken; |
| 1400 } |
| 1401 } |
| 1402 |
| 1403 |
| 1404 int HeapSnapshot::CalculateNetworkSize(JSObject* obj) { |
| 1405 int size = obj->Size(); |
| 1406 // If 'properties' and 'elements' are non-empty (thus, non-shared), |
| 1407 // take their size into account. |
| 1408 if (FixedArray::cast(obj->properties())->length() != 0) { |
| 1409 size += obj->properties()->Size(); |
| 1410 } |
| 1411 if (FixedArray::cast(obj->elements())->length() != 0) { |
| 1412 size += obj->elements()->Size(); |
| 1413 } |
| 1414 // For functions, also account non-empty context and literals sizes. |
| 1415 if (obj->IsJSFunction()) { |
| 1416 JSFunction* f = JSFunction::cast(obj); |
| 1417 if (f->unchecked_context()->IsContext()) { |
| 1418 size += f->context()->Size(); |
| 1419 } |
| 1420 if (f->literals()->length() != 0) { |
| 1421 size += f->literals()->Size(); |
| 1422 } |
| 1423 } |
| 1424 return size; |
| 1425 } |
| 1426 |
| 1427 |
| 1428 void HeapSnapshot::Print(int max_depth) { |
| 1429 root_.Print(max_depth, 0); |
| 1430 } |
| 1431 |
| 1432 |
| 1433 HeapSnapshotsCollection::HeapSnapshotsCollection() |
| 1434 : snapshots_uids_(HeapSnapshotsMatch), |
| 1435 token_enumerator_(new TokenEnumerator()) { |
| 1436 } |
| 1437 |
| 1438 |
| 1439 static void DeleteHeapSnapshot(HeapSnapshot** snapshot_ptr) { |
| 1440 delete *snapshot_ptr; |
| 1441 } |
| 1442 |
| 1443 |
| 1444 HeapSnapshotsCollection::~HeapSnapshotsCollection() { |
| 1445 delete token_enumerator_; |
| 1446 snapshots_.Iterate(DeleteHeapSnapshot); |
| 1447 } |
| 1448 |
| 1449 |
| 1450 HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(const char* name, |
| 1451 unsigned uid) { |
| 1452 HeapSnapshot* snapshot = new HeapSnapshot(this, name, uid); |
| 1453 snapshots_.Add(snapshot); |
| 1454 HashMap::Entry* entry = |
| 1455 snapshots_uids_.Lookup(reinterpret_cast<void*>(snapshot->uid()), |
| 1456 static_cast<uint32_t>(snapshot->uid()), |
| 1457 true); |
| 1458 ASSERT(entry->value == NULL); |
| 1459 entry->value = snapshot; |
| 1460 return snapshot; |
| 1461 } |
| 1462 |
| 1463 |
| 1464 HeapSnapshot* HeapSnapshotsCollection::GetSnapshot(unsigned uid) { |
| 1465 HashMap::Entry* entry = snapshots_uids_.Lookup(reinterpret_cast<void*>(uid), |
| 1466 static_cast<uint32_t>(uid), |
| 1467 false); |
| 1468 return entry != NULL ? reinterpret_cast<HeapSnapshot*>(entry->value) : NULL; |
| 1469 } |
| 1470 |
| 1471 |
| 1472 HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot) |
| 1473 : snapshot_(snapshot) { |
| 1474 } |
| 1475 |
| 1476 |
| 1477 void HeapSnapshotGenerator::GenerateSnapshot() { |
| 1478 AssertNoAllocation no_alloc; |
| 1479 |
| 1480 // Iterate heap contents. |
| 1481 HeapIterator iterator; |
| 1482 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { |
| 1483 ExtractReferences(obj); |
| 1484 } |
| 1485 |
| 1486 snapshot_->CutObjectsFromForeignSecurityContexts(); |
| 1487 } |
| 1488 |
| 1489 |
| 1490 void HeapSnapshotGenerator::ExtractReferences(HeapObject* obj) { |
| 1491 HeapEntry* entry = snapshot_->GetEntry(obj); |
| 1492 if (entry == NULL) return; |
| 1493 if (entry->visited()) return; |
| 1494 |
| 1495 if (obj->IsJSObject()) { |
| 1496 JSObject* js_obj = JSObject::cast(obj); |
| 1497 ExtractClosureReferences(js_obj, entry); |
| 1498 ExtractPropertyReferences(js_obj, entry); |
| 1499 ExtractElementReferences(js_obj, entry); |
| 1500 snapshot_->SetPropertyReference( |
| 1501 entry, Heap::prototype_symbol(), js_obj->map()->prototype()); |
| 1502 } else if (obj->IsJSGlobalPropertyCell()) { |
| 1503 JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(obj); |
| 1504 snapshot_->SetElementReference(entry, 0, cell->value()); |
| 1505 } else if (obj->IsString()) { |
| 1506 if (obj->IsConsString()) { |
| 1507 ConsString* cs = ConsString::cast(obj); |
| 1508 snapshot_->SetElementReference(entry, 0, cs->first()); |
| 1509 snapshot_->SetElementReference(entry, 1, cs->second()); |
| 1510 } |
| 1511 } else if (obj->IsCode() || obj->IsSharedFunctionInfo() || obj->IsScript()) { |
| 1512 IndexedReferencesExtractor refs_extractor(snapshot_, entry); |
| 1513 obj->Iterate(&refs_extractor); |
| 1514 } else if (obj->IsFixedArray()) { |
| 1515 IndexedReferencesExtractor refs_extractor(snapshot_, entry); |
| 1516 obj->Iterate(&refs_extractor); |
| 1517 } |
| 1518 entry->MarkAsVisited(); |
| 1519 } |
| 1520 |
| 1521 |
| 1522 void HeapSnapshotGenerator::ExtractClosureReferences(JSObject* js_obj, |
| 1523 HeapEntry* entry) { |
| 1524 if (js_obj->IsJSFunction()) { |
| 1525 HandleScope hs; |
| 1526 JSFunction* func = JSFunction::cast(js_obj); |
| 1527 Context* context = func->context(); |
| 1528 ZoneScope zscope(DELETE_ON_EXIT); |
| 1529 ScopeInfo<ZoneListAllocationPolicy> scope_info( |
| 1530 context->closure()->shared()->code()); |
| 1531 int locals_number = scope_info.NumberOfLocals(); |
| 1532 for (int i = 0; i < locals_number; ++i) { |
| 1533 String* local_name = *scope_info.LocalName(i); |
| 1534 int idx = ScopeInfo<>::ContextSlotIndex( |
| 1535 context->closure()->shared()->code(), local_name, NULL); |
| 1536 if (idx >= 0 && idx < context->length()) { |
| 1537 snapshot_->SetClosureReference(entry, local_name, context->get(idx)); |
| 1538 } |
| 1539 } |
| 1540 } |
| 1541 } |
| 1542 |
| 1543 |
| 1544 void HeapSnapshotGenerator::ExtractPropertyReferences(JSObject* js_obj, |
| 1545 HeapEntry* entry) { |
| 1546 if (js_obj->HasFastProperties()) { |
| 1547 DescriptorArray* descs = js_obj->map()->instance_descriptors(); |
| 1548 for (int i = 0; i < descs->number_of_descriptors(); i++) { |
| 1549 switch (descs->GetType(i)) { |
| 1550 case FIELD: { |
| 1551 int index = descs->GetFieldIndex(i); |
| 1552 snapshot_->SetPropertyReference( |
| 1553 entry, descs->GetKey(i), js_obj->FastPropertyAt(index)); |
| 1554 break; |
| 1555 } |
| 1556 case CONSTANT_FUNCTION: |
| 1557 snapshot_->SetPropertyReference( |
| 1558 entry, descs->GetKey(i), descs->GetConstantFunction(i)); |
| 1559 break; |
| 1560 default: ; |
| 1561 } |
| 1562 } |
| 1563 } else { |
| 1564 StringDictionary* dictionary = js_obj->property_dictionary(); |
| 1565 int length = dictionary->Capacity(); |
| 1566 for (int i = 0; i < length; ++i) { |
| 1567 Object* k = dictionary->KeyAt(i); |
| 1568 if (dictionary->IsKey(k)) { |
| 1569 snapshot_->SetPropertyReference( |
| 1570 entry, String::cast(k), dictionary->ValueAt(i)); |
| 1571 } |
| 1572 } |
| 1573 } |
| 1574 } |
| 1575 |
| 1576 |
| 1577 void HeapSnapshotGenerator::ExtractElementReferences(JSObject* js_obj, |
| 1578 HeapEntry* entry) { |
| 1579 if (js_obj->HasFastElements()) { |
| 1580 FixedArray* elements = FixedArray::cast(js_obj->elements()); |
| 1581 int length = js_obj->IsJSArray() ? |
| 1582 Smi::cast(JSArray::cast(js_obj)->length())->value() : |
| 1583 elements->length(); |
| 1584 for (int i = 0; i < length; ++i) { |
| 1585 if (!elements->get(i)->IsTheHole()) { |
| 1586 snapshot_->SetElementReference(entry, i, elements->get(i)); |
| 1587 } |
| 1588 } |
| 1589 } else if (js_obj->HasDictionaryElements()) { |
| 1590 NumberDictionary* dictionary = js_obj->element_dictionary(); |
| 1591 int length = dictionary->Capacity(); |
| 1592 for (int i = 0; i < length; ++i) { |
| 1593 Object* k = dictionary->KeyAt(i); |
| 1594 if (dictionary->IsKey(k)) { |
| 1595 ASSERT(k->IsNumber()); |
| 1596 uint32_t index = static_cast<uint32_t>(k->Number()); |
| 1597 snapshot_->SetElementReference(entry, index, dictionary->ValueAt(i)); |
| 1598 } |
| 1599 } |
| 1600 } |
| 1601 } |
| 1602 |
814 } } // namespace v8::internal | 1603 } } // namespace v8::internal |
815 | 1604 |
816 #endif // ENABLE_LOGGING_AND_PROFILING | 1605 #endif // ENABLE_LOGGING_AND_PROFILING |
OLD | NEW |