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 966 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
977 | 977 |
978 | 978 |
979 int HeapEntry::RetainedSize(bool exact) { | 979 int HeapEntry::RetainedSize(bool exact) { |
980 if (exact && (retained_size_ & kExactRetainedSizeTag) == 0) { | 980 if (exact && (retained_size_ & kExactRetainedSizeTag) == 0) { |
981 CalculateExactRetainedSize(); | 981 CalculateExactRetainedSize(); |
982 } | 982 } |
983 return retained_size_ & (~kExactRetainedSizeTag); | 983 return retained_size_ & (~kExactRetainedSizeTag); |
984 } | 984 } |
985 | 985 |
986 | 986 |
987 List<HeapGraphPath*>* HeapEntry::GetRetainingPaths() { | |
988 return snapshot_->GetRetainingPaths(this); | |
989 } | |
990 | |
991 | |
992 template<class Visitor> | 987 template<class Visitor> |
993 void HeapEntry::ApplyAndPaintAllReachable(Visitor* visitor) { | 988 void HeapEntry::ApplyAndPaintAllReachable(Visitor* visitor) { |
994 List<HeapEntry*> list(10); | 989 List<HeapEntry*> list(10); |
995 list.Add(this); | 990 list.Add(this); |
996 this->paint_reachable(); | 991 this->paint_reachable(); |
997 visitor->Apply(this); | 992 visitor->Apply(this); |
998 while (!list.is_empty()) { | 993 while (!list.is_empty()) { |
999 HeapEntry* entry = list.RemoveLast(); | 994 HeapEntry* entry = list.RemoveLast(); |
1000 Vector<HeapGraphEdge> children = entry->children(); | 995 Vector<HeapGraphEdge> children = entry->children(); |
1001 for (int i = 0; i < children.length(); ++i) { | 996 for (int i = 0; i < children.length(); ++i) { |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1140 } | 1135 } |
1141 | 1136 |
1142 RetainedSizeCalculator ret_size_calc; | 1137 RetainedSizeCalculator ret_size_calc; |
1143 snapshot()->IterateEntries(&ret_size_calc); | 1138 snapshot()->IterateEntries(&ret_size_calc); |
1144 retained_size_ = ret_size_calc.reained_size(); | 1139 retained_size_ = ret_size_calc.reained_size(); |
1145 ASSERT((retained_size_ & kExactRetainedSizeTag) == 0); | 1140 ASSERT((retained_size_ & kExactRetainedSizeTag) == 0); |
1146 retained_size_ |= kExactRetainedSizeTag; | 1141 retained_size_ |= kExactRetainedSizeTag; |
1147 } | 1142 } |
1148 | 1143 |
1149 | 1144 |
1150 class CachedHeapGraphPath { | |
1151 public: | |
1152 CachedHeapGraphPath() | |
1153 : nodes_(NodesMatch) { } | |
1154 CachedHeapGraphPath(const CachedHeapGraphPath& src) | |
1155 : nodes_(NodesMatch, &HashMap::DefaultAllocator, src.nodes_.capacity()), | |
1156 path_(src.path_.length() + 1) { | |
1157 for (HashMap::Entry* p = src.nodes_.Start(); | |
1158 p != NULL; | |
1159 p = src.nodes_.Next(p)) { | |
1160 nodes_.Lookup(p->key, p->hash, true); | |
1161 } | |
1162 path_.AddAll(src.path_); | |
1163 } | |
1164 void Add(HeapGraphEdge* edge) { | |
1165 nodes_.Lookup(edge->to(), Hash(edge->to()), true); | |
1166 path_.Add(edge); | |
1167 } | |
1168 bool ContainsNode(HeapEntry* node) { | |
1169 return nodes_.Lookup(node, Hash(node), false) != NULL; | |
1170 } | |
1171 const List<HeapGraphEdge*>* path() const { return &path_; } | |
1172 | |
1173 private: | |
1174 static uint32_t Hash(HeapEntry* entry) { | |
1175 return static_cast<uint32_t>(reinterpret_cast<intptr_t>(entry)); | |
1176 } | |
1177 static bool NodesMatch(void* key1, void* key2) { return key1 == key2; } | |
1178 | |
1179 HashMap nodes_; | |
1180 List<HeapGraphEdge*> path_; | |
1181 }; | |
1182 | |
1183 | |
1184 List<HeapGraphPath*>* HeapEntry::CalculateRetainingPaths() { | |
1185 List<HeapGraphPath*>* retaining_paths = new List<HeapGraphPath*>(4); | |
1186 CachedHeapGraphPath path; | |
1187 FindRetainingPaths(&path, retaining_paths); | |
1188 return retaining_paths; | |
1189 } | |
1190 | |
1191 | |
1192 void HeapEntry::FindRetainingPaths(CachedHeapGraphPath* prev_path, | |
1193 List<HeapGraphPath*>* retaining_paths) { | |
1194 Vector<HeapGraphEdge*> rets = retainers(); | |
1195 for (int i = 0; i < rets.length(); ++i) { | |
1196 HeapGraphEdge* ret_edge = rets[i]; | |
1197 if (prev_path->ContainsNode(ret_edge->From())) continue; | |
1198 if (ret_edge->From() != snapshot()->root()) { | |
1199 CachedHeapGraphPath path(*prev_path); | |
1200 path.Add(ret_edge); | |
1201 ret_edge->From()->FindRetainingPaths(&path, retaining_paths); | |
1202 } else { | |
1203 HeapGraphPath* ret_path = new HeapGraphPath(*prev_path->path()); | |
1204 ret_path->Set(0, ret_edge); | |
1205 retaining_paths->Add(ret_path); | |
1206 } | |
1207 } | |
1208 } | |
1209 | |
1210 | |
1211 HeapGraphPath::HeapGraphPath(const List<HeapGraphEdge*>& path) | |
1212 : path_(path.length() + 1) { | |
1213 Add(NULL); | |
1214 for (int i = path.length() - 1; i >= 0; --i) { | |
1215 Add(path[i]); | |
1216 } | |
1217 } | |
1218 | |
1219 | |
1220 void HeapGraphPath::Print() { | |
1221 path_[0]->From()->Print(1, 0); | |
1222 for (int i = 0; i < path_.length(); ++i) { | |
1223 OS::Print(" -> "); | |
1224 HeapGraphEdge* edge = path_[i]; | |
1225 switch (edge->type()) { | |
1226 case HeapGraphEdge::kContextVariable: | |
1227 OS::Print("[#%s] ", edge->name()); | |
1228 break; | |
1229 case HeapGraphEdge::kElement: | |
1230 case HeapGraphEdge::kHidden: | |
1231 OS::Print("[%d] ", edge->index()); | |
1232 break; | |
1233 case HeapGraphEdge::kInternal: | |
1234 OS::Print("[$%s] ", edge->name()); | |
1235 break; | |
1236 case HeapGraphEdge::kProperty: | |
1237 OS::Print("[%s] ", edge->name()); | |
1238 break; | |
1239 case HeapGraphEdge::kShortcut: | |
1240 OS::Print("[^%s] ", edge->name()); | |
1241 break; | |
1242 default: | |
1243 OS::Print("!!! unknown edge type: %d ", edge->type()); | |
1244 } | |
1245 edge->to()->Print(1, 0); | |
1246 } | |
1247 OS::Print("\n"); | |
1248 } | |
1249 | |
1250 | |
1251 // It is very important to keep objects that form a heap snapshot | 1145 // It is very important to keep objects that form a heap snapshot |
1252 // as small as possible. | 1146 // as small as possible. |
1253 namespace { // Avoid littering the global namespace. | 1147 namespace { // Avoid littering the global namespace. |
1254 | 1148 |
1255 template <size_t ptr_size> struct SnapshotSizeConstants; | 1149 template <size_t ptr_size> struct SnapshotSizeConstants; |
1256 | 1150 |
1257 template <> struct SnapshotSizeConstants<4> { | 1151 template <> struct SnapshotSizeConstants<4> { |
1258 static const int kExpectedHeapGraphEdgeSize = 12; | 1152 static const int kExpectedHeapGraphEdgeSize = 12; |
1259 static const int kExpectedHeapEntrySize = 36; | 1153 static const int kExpectedHeapEntrySize = 36; |
1260 }; | 1154 }; |
(...skipping 10 matching lines...) Expand all Loading... |
1271 const char* title, | 1165 const char* title, |
1272 unsigned uid) | 1166 unsigned uid) |
1273 : collection_(collection), | 1167 : collection_(collection), |
1274 type_(type), | 1168 type_(type), |
1275 title_(title), | 1169 title_(title), |
1276 uid_(uid), | 1170 uid_(uid), |
1277 root_entry_(NULL), | 1171 root_entry_(NULL), |
1278 gc_roots_entry_(NULL), | 1172 gc_roots_entry_(NULL), |
1279 natives_root_entry_(NULL), | 1173 natives_root_entry_(NULL), |
1280 raw_entries_(NULL), | 1174 raw_entries_(NULL), |
1281 entries_sorted_(false), | 1175 entries_sorted_(false) { |
1282 retaining_paths_(HeapEntry::Match) { | |
1283 STATIC_ASSERT( | 1176 STATIC_ASSERT( |
1284 sizeof(HeapGraphEdge) == | 1177 sizeof(HeapGraphEdge) == |
1285 SnapshotSizeConstants<sizeof(void*)>::kExpectedHeapGraphEdgeSize); // NOL
INT | 1178 SnapshotSizeConstants<sizeof(void*)>::kExpectedHeapGraphEdgeSize); // NOL
INT |
1286 STATIC_ASSERT( | 1179 STATIC_ASSERT( |
1287 sizeof(HeapEntry) == | 1180 sizeof(HeapEntry) == |
1288 SnapshotSizeConstants<sizeof(void*)>::kExpectedHeapEntrySize); // NOLINT | 1181 SnapshotSizeConstants<sizeof(void*)>::kExpectedHeapEntrySize); // NOLINT |
1289 } | 1182 } |
1290 | 1183 |
1291 | |
1292 static void DeleteHeapGraphPath(HeapGraphPath** path_ptr) { | |
1293 delete *path_ptr; | |
1294 } | |
1295 | |
1296 HeapSnapshot::~HeapSnapshot() { | 1184 HeapSnapshot::~HeapSnapshot() { |
1297 DeleteArray(raw_entries_); | 1185 DeleteArray(raw_entries_); |
1298 for (HashMap::Entry* p = retaining_paths_.Start(); | |
1299 p != NULL; | |
1300 p = retaining_paths_.Next(p)) { | |
1301 List<HeapGraphPath*>* list = | |
1302 reinterpret_cast<List<HeapGraphPath*>*>(p->value); | |
1303 list->Iterate(DeleteHeapGraphPath); | |
1304 delete list; | |
1305 } | |
1306 } | 1186 } |
1307 | 1187 |
1308 | 1188 |
1309 void HeapSnapshot::Delete() { | 1189 void HeapSnapshot::Delete() { |
1310 collection_->RemoveSnapshot(this); | 1190 collection_->RemoveSnapshot(this); |
1311 delete this; | 1191 delete this; |
1312 } | 1192 } |
1313 | 1193 |
1314 | 1194 |
1315 void HeapSnapshot::AllocateEntries(int entries_count, | 1195 void HeapSnapshot::AllocateEntries(int entries_count, |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1397 reinterpret_cast<char*>(last_entry) + last_entry->EntrySize())); | 1277 reinterpret_cast<char*>(last_entry) + last_entry->EntrySize())); |
1398 } else { | 1278 } else { |
1399 entries_.Add(reinterpret_cast<HeapEntry*>(raw_entries_)); | 1279 entries_.Add(reinterpret_cast<HeapEntry*>(raw_entries_)); |
1400 } | 1280 } |
1401 ASSERT(reinterpret_cast<char*>(entries_.last()) < | 1281 ASSERT(reinterpret_cast<char*>(entries_.last()) < |
1402 (raw_entries_ + raw_entries_size_)); | 1282 (raw_entries_ + raw_entries_size_)); |
1403 return entries_.last(); | 1283 return entries_.last(); |
1404 } | 1284 } |
1405 | 1285 |
1406 | 1286 |
1407 HeapSnapshotsDiff* HeapSnapshot::CompareWith(HeapSnapshot* snapshot) { | |
1408 return collection_->CompareSnapshots(this, snapshot); | |
1409 } | |
1410 | |
1411 | |
1412 HeapEntry* HeapSnapshot::GetEntryById(uint64_t id) { | 1287 HeapEntry* HeapSnapshot::GetEntryById(uint64_t id) { |
1413 // GetSortedEntriesList is used in diff algorithm and sorts | |
1414 // entries by their id. | |
1415 List<HeapEntry*>* entries_by_id = GetSortedEntriesList(); | 1288 List<HeapEntry*>* entries_by_id = GetSortedEntriesList(); |
1416 | 1289 |
1417 // Perform a binary search by id. | 1290 // Perform a binary search by id. |
1418 int low = 0; | 1291 int low = 0; |
1419 int high = entries_by_id->length() - 1; | 1292 int high = entries_by_id->length() - 1; |
1420 while (low <= high) { | 1293 while (low <= high) { |
1421 int mid = | 1294 int mid = |
1422 (static_cast<unsigned int>(low) + static_cast<unsigned int>(high)) >> 1; | 1295 (static_cast<unsigned int>(low) + static_cast<unsigned int>(high)) >> 1; |
1423 uint64_t mid_id = entries_by_id->at(mid)->id(); | 1296 uint64_t mid_id = entries_by_id->at(mid)->id(); |
1424 if (mid_id > id) | 1297 if (mid_id > id) |
1425 high = mid - 1; | 1298 high = mid - 1; |
1426 else if (mid_id < id) | 1299 else if (mid_id < id) |
1427 low = mid + 1; | 1300 low = mid + 1; |
1428 else | 1301 else |
1429 return entries_by_id->at(mid); | 1302 return entries_by_id->at(mid); |
1430 } | 1303 } |
1431 return NULL; | 1304 return NULL; |
1432 } | 1305 } |
1433 | 1306 |
1434 | 1307 |
1435 List<HeapGraphPath*>* HeapSnapshot::GetRetainingPaths(HeapEntry* entry) { | |
1436 HashMap::Entry* p = | |
1437 retaining_paths_.Lookup(entry, HeapEntry::Hash(entry), true); | |
1438 if (p->value == NULL) { | |
1439 p->value = entry->CalculateRetainingPaths(); | |
1440 } | |
1441 return reinterpret_cast<List<HeapGraphPath*>*>(p->value); | |
1442 } | |
1443 | |
1444 | |
1445 template<class T> | 1308 template<class T> |
1446 static int SortByIds(const T* entry1_ptr, | 1309 static int SortByIds(const T* entry1_ptr, |
1447 const T* entry2_ptr) { | 1310 const T* entry2_ptr) { |
1448 if ((*entry1_ptr)->id() == (*entry2_ptr)->id()) return 0; | 1311 if ((*entry1_ptr)->id() == (*entry2_ptr)->id()) return 0; |
1449 return (*entry1_ptr)->id() < (*entry2_ptr)->id() ? -1 : 1; | 1312 return (*entry1_ptr)->id() < (*entry2_ptr)->id() ? -1 : 1; |
1450 } | 1313 } |
1451 | 1314 |
1452 List<HeapEntry*>* HeapSnapshot::GetSortedEntriesList() { | 1315 List<HeapEntry*>* HeapSnapshot::GetSortedEntriesList() { |
1453 if (!entries_sorted_) { | 1316 if (!entries_sorted_) { |
1454 entries_.Sort(SortByIds); | 1317 entries_.Sort(SortByIds); |
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1624 | 1487 |
1625 | 1488 |
1626 void HeapSnapshotsCollection::RemoveSnapshot(HeapSnapshot* snapshot) { | 1489 void HeapSnapshotsCollection::RemoveSnapshot(HeapSnapshot* snapshot) { |
1627 snapshots_.RemoveElement(snapshot); | 1490 snapshots_.RemoveElement(snapshot); |
1628 unsigned uid = snapshot->uid(); | 1491 unsigned uid = snapshot->uid(); |
1629 snapshots_uids_.Remove(reinterpret_cast<void*>(uid), | 1492 snapshots_uids_.Remove(reinterpret_cast<void*>(uid), |
1630 static_cast<uint32_t>(uid)); | 1493 static_cast<uint32_t>(uid)); |
1631 } | 1494 } |
1632 | 1495 |
1633 | 1496 |
1634 HeapSnapshotsDiff* HeapSnapshotsCollection::CompareSnapshots( | |
1635 HeapSnapshot* snapshot1, | |
1636 HeapSnapshot* snapshot2) { | |
1637 return comparator_.Compare(snapshot1, snapshot2); | |
1638 } | |
1639 | |
1640 | |
1641 HeapEntry *const HeapEntriesMap::kHeapEntryPlaceholder = | 1497 HeapEntry *const HeapEntriesMap::kHeapEntryPlaceholder = |
1642 reinterpret_cast<HeapEntry*>(1); | 1498 reinterpret_cast<HeapEntry*>(1); |
1643 | 1499 |
1644 HeapEntriesMap::HeapEntriesMap() | 1500 HeapEntriesMap::HeapEntriesMap() |
1645 : entries_(HeapThingsMatch), | 1501 : entries_(HeapThingsMatch), |
1646 entries_count_(0), | 1502 entries_count_(0), |
1647 total_children_count_(0), | 1503 total_children_count_(0), |
1648 total_retainers_count_(0) { | 1504 total_retainers_count_(0) { |
1649 } | 1505 } |
1650 | 1506 |
(...skipping 1146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2797 dominator != entry; | 2653 dominator != entry; |
2798 entry = dominator, dominator = entry->dominator()) { | 2654 entry = dominator, dominator = entry->dominator()) { |
2799 dominator->add_retained_size(entry_size); | 2655 dominator->add_retained_size(entry_size); |
2800 } | 2656 } |
2801 if (!ProgressReport()) return false; | 2657 if (!ProgressReport()) return false; |
2802 } | 2658 } |
2803 return true; | 2659 return true; |
2804 } | 2660 } |
2805 | 2661 |
2806 | 2662 |
2807 void HeapSnapshotsDiff::CreateRoots(int additions_count, int deletions_count) { | |
2808 raw_additions_root_ = | |
2809 NewArray<char>(HeapEntry::EntriesSize(1, additions_count, 0)); | |
2810 additions_root()->Init( | |
2811 snapshot2_, HeapEntry::kHidden, "", 0, 0, additions_count, 0); | |
2812 raw_deletions_root_ = | |
2813 NewArray<char>(HeapEntry::EntriesSize(1, deletions_count, 0)); | |
2814 deletions_root()->Init( | |
2815 snapshot1_, HeapEntry::kHidden, "", 0, 0, deletions_count, 0); | |
2816 } | |
2817 | |
2818 | |
2819 static void DeleteHeapSnapshotsDiff(HeapSnapshotsDiff** diff_ptr) { | |
2820 delete *diff_ptr; | |
2821 } | |
2822 | |
2823 HeapSnapshotsComparator::~HeapSnapshotsComparator() { | |
2824 diffs_.Iterate(DeleteHeapSnapshotsDiff); | |
2825 } | |
2826 | |
2827 | |
2828 HeapSnapshotsDiff* HeapSnapshotsComparator::Compare(HeapSnapshot* snapshot1, | |
2829 HeapSnapshot* snapshot2) { | |
2830 snapshot1->ClearPaint(); | |
2831 snapshot1->root()->PaintAllReachable(); | |
2832 snapshot2->ClearPaint(); | |
2833 snapshot2->root()->PaintAllReachable(); | |
2834 | |
2835 List<HeapEntry*>* entries1 = snapshot1->GetSortedEntriesList(); | |
2836 List<HeapEntry*>* entries2 = snapshot2->GetSortedEntriesList(); | |
2837 int i = 0, j = 0; | |
2838 List<HeapEntry*> added_entries, deleted_entries; | |
2839 while (i < entries1->length() && j < entries2->length()) { | |
2840 uint64_t id1 = entries1->at(i)->id(); | |
2841 uint64_t id2 = entries2->at(j)->id(); | |
2842 if (id1 == id2) { | |
2843 HeapEntry* entry1 = entries1->at(i++); | |
2844 HeapEntry* entry2 = entries2->at(j++); | |
2845 if (entry1->painted_reachable() != entry2->painted_reachable()) { | |
2846 if (entry1->painted_reachable()) | |
2847 deleted_entries.Add(entry1); | |
2848 else | |
2849 added_entries.Add(entry2); | |
2850 } | |
2851 } else if (id1 < id2) { | |
2852 HeapEntry* entry = entries1->at(i++); | |
2853 deleted_entries.Add(entry); | |
2854 } else { | |
2855 HeapEntry* entry = entries2->at(j++); | |
2856 added_entries.Add(entry); | |
2857 } | |
2858 } | |
2859 while (i < entries1->length()) { | |
2860 HeapEntry* entry = entries1->at(i++); | |
2861 deleted_entries.Add(entry); | |
2862 } | |
2863 while (j < entries2->length()) { | |
2864 HeapEntry* entry = entries2->at(j++); | |
2865 added_entries.Add(entry); | |
2866 } | |
2867 | |
2868 HeapSnapshotsDiff* diff = new HeapSnapshotsDiff(snapshot1, snapshot2); | |
2869 diffs_.Add(diff); | |
2870 diff->CreateRoots(added_entries.length(), deleted_entries.length()); | |
2871 | |
2872 for (int i = 0; i < deleted_entries.length(); ++i) { | |
2873 HeapEntry* entry = deleted_entries[i]; | |
2874 diff->AddDeletedEntry(i, i + 1, entry); | |
2875 } | |
2876 for (int i = 0; i < added_entries.length(); ++i) { | |
2877 HeapEntry* entry = added_entries[i]; | |
2878 diff->AddAddedEntry(i, i + 1, entry); | |
2879 } | |
2880 return diff; | |
2881 } | |
2882 | |
2883 | |
2884 class OutputStreamWriter { | 2663 class OutputStreamWriter { |
2885 public: | 2664 public: |
2886 explicit OutputStreamWriter(v8::OutputStream* stream) | 2665 explicit OutputStreamWriter(v8::OutputStream* stream) |
2887 : stream_(stream), | 2666 : stream_(stream), |
2888 chunk_size_(stream->GetChunkSize()), | 2667 chunk_size_(stream->GetChunkSize()), |
2889 chunk_(chunk_size_), | 2668 chunk_(chunk_size_), |
2890 chunk_pos_(0), | 2669 chunk_pos_(0), |
2891 aborted_(false) { | 2670 aborted_(false) { |
2892 ASSERT(chunk_size_ > 0); | 2671 ASSERT(chunk_size_ > 0); |
2893 } | 2672 } |
(...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3248 | 3027 |
3249 | 3028 |
3250 String* GetConstructorNameForHeapProfile(JSObject* object) { | 3029 String* GetConstructorNameForHeapProfile(JSObject* object) { |
3251 if (object->IsJSFunction()) return HEAP->closure_symbol(); | 3030 if (object->IsJSFunction()) return HEAP->closure_symbol(); |
3252 return object->constructor_name(); | 3031 return object->constructor_name(); |
3253 } | 3032 } |
3254 | 3033 |
3255 } } // namespace v8::internal | 3034 } } // namespace v8::internal |
3256 | 3035 |
3257 #endif // ENABLE_LOGGING_AND_PROFILING | 3036 #endif // ENABLE_LOGGING_AND_PROFILING |
OLD | NEW |