| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 473 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 484 PartitionPage* secondaryPage = reinterpret_cast<PartitionPage*>(pageChar
Ptr); | 484 PartitionPage* secondaryPage = reinterpret_cast<PartitionPage*>(pageChar
Ptr); |
| 485 secondaryPage->pageOffset = i; | 485 secondaryPage->pageOffset = i; |
| 486 } | 486 } |
| 487 } | 487 } |
| 488 | 488 |
| 489 static ALWAYS_INLINE size_t partitionRoundUpToSystemPage(size_t size) | 489 static ALWAYS_INLINE size_t partitionRoundUpToSystemPage(size_t size) |
| 490 { | 490 { |
| 491 return (size + kSystemPageOffsetMask) & kSystemPageBaseMask; | 491 return (size + kSystemPageOffsetMask) & kSystemPageBaseMask; |
| 492 } | 492 } |
| 493 | 493 |
| 494 static ALWAYS_INLINE size_t partitionRoundDownToSystemPage(size_t size) |
| 495 { |
| 496 return size & kSystemPageBaseMask; |
| 497 } |
| 498 |
| 494 static ALWAYS_INLINE char* partitionPageAllocAndFillFreelist(PartitionPage* page
) | 499 static ALWAYS_INLINE char* partitionPageAllocAndFillFreelist(PartitionPage* page
) |
| 495 { | 500 { |
| 496 ASSERT(page != &PartitionRootGeneric::gSeedPage); | 501 ASSERT(page != &PartitionRootGeneric::gSeedPage); |
| 497 uint16_t numSlots = page->numUnprovisionedSlots; | 502 uint16_t numSlots = page->numUnprovisionedSlots; |
| 498 ASSERT(numSlots); | 503 ASSERT(numSlots); |
| 499 PartitionBucket* bucket = page->bucket; | 504 PartitionBucket* bucket = page->bucket; |
| 500 // We should only get here when _every_ slot is either used or unprovisioned
. | 505 // We should only get here when _every_ slot is either used or unprovisioned
. |
| 501 // (The third state is "on the freelist". If we have a non-empty freelist, w
e should not get here.) | 506 // (The third state is "on the freelist". If we have a non-empty freelist, w
e should not get here.) |
| 502 ASSERT(numSlots + page->numAllocatedSlots == partitionBucketSlots(bucket)); | 507 ASSERT(numSlots + page->numAllocatedSlots == partitionBucketSlots(bucket)); |
| 503 // Similarly, make explicitly sure that the freelist is empty. | 508 // Similarly, make explicitly sure that the freelist is empty. |
| (...skipping 387 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 891 static void partitionDecommitEmptyPages(PartitionRootBase* root) | 896 static void partitionDecommitEmptyPages(PartitionRootBase* root) |
| 892 { | 897 { |
| 893 for (size_t i = 0; i < kMaxFreeableSpans; ++i) { | 898 for (size_t i = 0; i < kMaxFreeableSpans; ++i) { |
| 894 PartitionPage* page = root->globalEmptyPageRing[i]; | 899 PartitionPage* page = root->globalEmptyPageRing[i]; |
| 895 if (page) | 900 if (page) |
| 896 partitionDecommitPageIfPossible(root, page); | 901 partitionDecommitPageIfPossible(root, page); |
| 897 root->globalEmptyPageRing[i] = nullptr; | 902 root->globalEmptyPageRing[i] = nullptr; |
| 898 } | 903 } |
| 899 } | 904 } |
| 900 | 905 |
| 901 void partitionPurgeMemory(PartitionRoot* root, int flags) | |
| 902 { | |
| 903 if (flags & PartitionPurgeDecommitEmptyPages) | |
| 904 partitionDecommitEmptyPages(root); | |
| 905 } | |
| 906 | |
| 907 void partitionPurgeMemoryGeneric(PartitionRootGeneric* root, int flags) | |
| 908 { | |
| 909 spinLockLock(&root->lock); | |
| 910 if (flags & PartitionPurgeDecommitEmptyPages) | |
| 911 partitionDecommitEmptyPages(root); | |
| 912 spinLockUnlock(&root->lock); | |
| 913 } | |
| 914 | |
| 915 void partitionFreeSlowPath(PartitionPage* page) | 906 void partitionFreeSlowPath(PartitionPage* page) |
| 916 { | 907 { |
| 917 PartitionBucket* bucket = page->bucket; | 908 PartitionBucket* bucket = page->bucket; |
| 918 ASSERT(page != &PartitionRootGeneric::gSeedPage); | 909 ASSERT(page != &PartitionRootGeneric::gSeedPage); |
| 919 if (LIKELY(page->numAllocatedSlots == 0)) { | 910 if (LIKELY(page->numAllocatedSlots == 0)) { |
| 920 // Page became fully unused. | 911 // Page became fully unused. |
| 921 if (UNLIKELY(partitionBucketIsDirectMapped(bucket))) { | 912 if (UNLIKELY(partitionBucketIsDirectMapped(bucket))) { |
| 922 partitionDirectUnmap(page); | 913 partitionDirectUnmap(page); |
| 923 return; | 914 return; |
| 924 } | 915 } |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1063 size_t copySize = actualOldSize; | 1054 size_t copySize = actualOldSize; |
| 1064 if (newSize < copySize) | 1055 if (newSize < copySize) |
| 1065 copySize = newSize; | 1056 copySize = newSize; |
| 1066 | 1057 |
| 1067 memcpy(ret, ptr, copySize); | 1058 memcpy(ret, ptr, copySize); |
| 1068 partitionFreeGeneric(root, ptr); | 1059 partitionFreeGeneric(root, ptr); |
| 1069 return ret; | 1060 return ret; |
| 1070 #endif | 1061 #endif |
| 1071 } | 1062 } |
| 1072 | 1063 |
| 1064 static size_t partitionPurgePage(const PartitionPage* page, bool discard) |
| 1065 { |
| 1066 const PartitionBucket* bucket = page->bucket; |
| 1067 if (bucket->slotSize < kSystemPageSize || !page->numAllocatedSlots) |
| 1068 return 0; |
| 1069 |
| 1070 size_t bucketNumSlots = partitionBucketSlots(bucket); |
| 1071 size_t discardableBytes = 0; |
| 1072 |
| 1073 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page)); |
| 1074 if (rawSize) { |
| 1075 uint32_t usedBytes = static_cast<uint32_t>(partitionRoundUpToSystemPage(
rawSize)); |
| 1076 discardableBytes = bucket->slotSize - usedBytes; |
| 1077 if (discardableBytes && discard) { |
| 1078 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page)); |
| 1079 ptr += usedBytes; |
| 1080 discardSystemPages(ptr, discardableBytes); |
| 1081 } |
| 1082 return discardableBytes; |
| 1083 } |
| 1084 |
| 1085 const size_t maxSlotCount = (kPartitionPageSize * kMaxPartitionPagesPerSlotS
pan) / kSystemPageSize; |
| 1086 ASSERT(bucketNumSlots <= maxSlotCount); |
| 1087 char slotUsage[maxSlotCount]; |
| 1088 size_t lastSlot = static_cast<size_t>(-1); |
| 1089 memset(slotUsage, 1, sizeof(slotUsage)); |
| 1090 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page)); |
| 1091 PartitionFreelistEntry* entry = page->freelistHead; |
| 1092 // First, walk the freelist for this page and make a bitmap of which slots |
| 1093 // are not in use. |
| 1094 while (entry) { |
| 1095 size_t slotIndex = (reinterpret_cast<char*>(entry) - ptr) / bucket->slot
Size; |
| 1096 ASSERT(slotIndex < bucketNumSlots); |
| 1097 slotUsage[slotIndex] = 0; |
| 1098 entry = partitionFreelistMask(entry->next); |
| 1099 // If we have a slot where the masked freelist entry is 0, we can |
| 1100 // actually discard that freelist entry because touching a discarded |
| 1101 // page is guaranteed to return original content or 0. |
| 1102 // (Note that this optimization won't fire on big endian machines |
| 1103 // because the masking function is negation.) |
| 1104 if (!partitionFreelistMask(entry)) |
| 1105 lastSlot = slotIndex; |
| 1106 } |
| 1107 // Next, walk the slots and for any not in use, consider where the system |
| 1108 // page boundaries occur. We can release any system pages back to the |
| 1109 // system as long as we don't interfere with a freelist pointer or an |
| 1110 // adjacent slot. |
| 1111 // TODO(cevans): I think we can "truncate" the page, i.e. increase the |
| 1112 // value of page->numUnprovisionedSlots and rewrite(!) the freelist, if |
| 1113 // we find that to be a win too. |
| 1114 for (size_t i = 0; i < bucketNumSlots; ++i) { |
| 1115 if (slotUsage[i]) |
| 1116 continue; |
| 1117 // The first address we can safely discard is just after the freelist |
| 1118 // pointer. There's one quirk: if the freelist pointer is actually a |
| 1119 // null, we can discard that pointer value too. |
| 1120 char* beginPtr = ptr + (i * bucket->slotSize); |
| 1121 char* endPtr = beginPtr + bucket->slotSize; |
| 1122 if (i != lastSlot) |
| 1123 beginPtr += sizeof(PartitionFreelistEntry); |
| 1124 beginPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterp
ret_cast<size_t>(beginPtr))); |
| 1125 endPtr = reinterpret_cast<char*>(partitionRoundDownToSystemPage(reinterp
ret_cast<size_t>(endPtr))); |
| 1126 if (beginPtr < endPtr) { |
| 1127 size_t partialSlotBytes = endPtr - beginPtr; |
| 1128 discardableBytes += partialSlotBytes; |
| 1129 if (discard) |
| 1130 discardSystemPages(beginPtr, partialSlotBytes); |
| 1131 } |
| 1132 } |
| 1133 return discardableBytes; |
| 1134 } |
| 1135 |
| 1136 static void partitionPurgeBucket(const PartitionBucket* bucket) |
| 1137 { |
| 1138 if (bucket->activePagesHead != &PartitionRootGeneric::gSeedPage) { |
| 1139 for (const PartitionPage* page = bucket->activePagesHead; page; page = p
age->nextPage) { |
| 1140 ASSERT(page != &PartitionRootGeneric::gSeedPage); |
| 1141 (void) partitionPurgePage(page, true); |
| 1142 } |
| 1143 } |
| 1144 } |
| 1145 |
| 1146 void partitionPurgeMemory(PartitionRoot* root, int flags) |
| 1147 { |
| 1148 if (flags & PartitionPurgeDecommitEmptyPages) |
| 1149 partitionDecommitEmptyPages(root); |
| 1150 // We don't currently do anything for PartitionPurgeDiscardUnusedSystemPages |
| 1151 // here because that flag is only useful for allocations >= system page |
| 1152 // size. We only have allocations that large inside generic partitions |
| 1153 // at the moment. |
| 1154 } |
| 1155 |
| 1156 void partitionPurgeMemoryGeneric(PartitionRootGeneric* root, int flags) |
| 1157 { |
| 1158 spinLockLock(&root->lock); |
| 1159 if (flags & PartitionPurgeDecommitEmptyPages) |
| 1160 partitionDecommitEmptyPages(root); |
| 1161 if (flags & PartitionPurgeDiscardUnusedSystemPages) { |
| 1162 for (size_t i = 0; i < kGenericNumBuckets; ++i) { |
| 1163 const PartitionBucket* bucket = &root->buckets[i]; |
| 1164 if (bucket->slotSize >= kSystemPageSize) |
| 1165 partitionPurgeBucket(bucket); |
| 1166 } |
| 1167 } |
| 1168 spinLockUnlock(&root->lock); |
| 1169 } |
| 1170 |
| 1073 static void partitionDumpPageStats(PartitionBucketMemoryStats* statsOut, const P
artitionPage* page) | 1171 static void partitionDumpPageStats(PartitionBucketMemoryStats* statsOut, const P
artitionPage* page) |
| 1074 { | 1172 { |
| 1075 uint16_t bucketNumSlots = partitionBucketSlots(page->bucket); | 1173 uint16_t bucketNumSlots = partitionBucketSlots(page->bucket); |
| 1076 | 1174 |
| 1077 if (partitionPageStateIsDecommitted(page)) { | 1175 if (partitionPageStateIsDecommitted(page)) { |
| 1078 ++statsOut->numDecommittedPages; | 1176 ++statsOut->numDecommittedPages; |
| 1177 return; |
| 1178 } |
| 1179 |
| 1180 statsOut->discardableBytes += partitionPurgePage(page, false); |
| 1181 |
| 1182 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page)); |
| 1183 if (rawSize) |
| 1184 statsOut->activeBytes += static_cast<uint32_t>(rawSize); |
| 1185 else |
| 1186 statsOut->activeBytes += (page->numAllocatedSlots * statsOut->bucketSlot
Size); |
| 1187 |
| 1188 size_t pageBytesResident = partitionRoundUpToSystemPage((bucketNumSlots - pa
ge->numUnprovisionedSlots) * statsOut->bucketSlotSize); |
| 1189 statsOut->residentBytes += pageBytesResident; |
| 1190 if (partitionPageStateIsEmpty(page)) { |
| 1191 statsOut->decommittableBytes += pageBytesResident; |
| 1192 ++statsOut->numEmptyPages; |
| 1193 } else if (page->numAllocatedSlots == bucketNumSlots) { |
| 1194 ++statsOut->numFullPages; |
| 1079 } else { | 1195 } else { |
| 1080 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page
)); | 1196 ASSERT(page->numAllocatedSlots > 0); |
| 1081 if (rawSize) | 1197 ++statsOut->numActivePages; |
| 1082 statsOut->activeBytes += static_cast<uint32_t>(partitionRoundUpToSys
temPage(rawSize)); | |
| 1083 else | |
| 1084 statsOut->activeBytes += (page->numAllocatedSlots * statsOut->bucket
SlotSize); | |
| 1085 size_t pageBytesResident = (bucketNumSlots - page->numUnprovisionedSlots
) * statsOut->bucketSlotSize; | |
| 1086 // Round up to system page size. | |
| 1087 size_t pageBytesResidentRounded = partitionRoundUpToSystemPage(pageBytes
Resident); | |
| 1088 statsOut->residentBytes += pageBytesResidentRounded; | |
| 1089 if (partitionPageStateIsEmpty(page)) { | |
| 1090 statsOut->decommittableBytes += pageBytesResidentRounded; | |
| 1091 ++statsOut->numEmptyPages; | |
| 1092 } else if (page->numAllocatedSlots == bucketNumSlots) { | |
| 1093 ++statsOut->numFullPages; | |
| 1094 } else { | |
| 1095 ASSERT(page->numAllocatedSlots > 0); | |
| 1096 ++statsOut->numActivePages; | |
| 1097 } | |
| 1098 } | 1198 } |
| 1099 } | 1199 } |
| 1100 | 1200 |
| 1101 static void partitionDumpBucketStats(PartitionBucketMemoryStats* statsOut, const
PartitionBucket* bucket) | 1201 static void partitionDumpBucketStats(PartitionBucketMemoryStats* statsOut, const
PartitionBucket* bucket) |
| 1102 { | 1202 { |
| 1103 ASSERT(!partitionBucketIsDirectMapped(bucket)); | 1203 ASSERT(!partitionBucketIsDirectMapped(bucket)); |
| 1104 statsOut->isValid = false; | 1204 statsOut->isValid = false; |
| 1105 // If the active page list is empty (== &PartitionRootGeneric::gSeedPage), | 1205 // If the active page list is empty (== &PartitionRootGeneric::gSeedPage), |
| 1106 // the bucket might still need to be reported if it has a list of empty, | 1206 // the bucket might still need to be reported if it has a list of empty, |
| 1107 // decommitted or full pages. | 1207 // decommitted or full pages. |
| (...skipping 24 matching lines...) Expand all Loading... |
| 1132 for (const PartitionPage* page = bucket->activePagesHead; page; page = p
age->nextPage) { | 1232 for (const PartitionPage* page = bucket->activePagesHead; page; page = p
age->nextPage) { |
| 1133 ASSERT(page != &PartitionRootGeneric::gSeedPage); | 1233 ASSERT(page != &PartitionRootGeneric::gSeedPage); |
| 1134 partitionDumpPageStats(statsOut, page); | 1234 partitionDumpPageStats(statsOut, page); |
| 1135 } | 1235 } |
| 1136 } | 1236 } |
| 1137 } | 1237 } |
| 1138 | 1238 |
| 1139 void partitionDumpStatsGeneric(PartitionRootGeneric* partition, const char* part
itionName, PartitionStatsDumper* partitionStatsDumper) | 1239 void partitionDumpStatsGeneric(PartitionRootGeneric* partition, const char* part
itionName, PartitionStatsDumper* partitionStatsDumper) |
| 1140 { | 1240 { |
| 1141 PartitionBucketMemoryStats bucketStats[kGenericNumBuckets]; | 1241 PartitionBucketMemoryStats bucketStats[kGenericNumBuckets]; |
| 1242 static const size_t kMaxReportableDirectMaps = 4096; |
| 1243 uint32_t directMapLengths[kMaxReportableDirectMaps]; |
| 1244 size_t numDirectMappedAllocations = 0; |
| 1245 |
| 1142 spinLockLock(&partition->lock); | 1246 spinLockLock(&partition->lock); |
| 1247 |
| 1143 for (size_t i = 0; i < kGenericNumBuckets; ++i) { | 1248 for (size_t i = 0; i < kGenericNumBuckets; ++i) { |
| 1144 const PartitionBucket* bucket = &partition->buckets[i]; | 1249 const PartitionBucket* bucket = &partition->buckets[i]; |
| 1145 // Don't report the pseudo buckets that the generic allocator sets up in | 1250 // Don't report the pseudo buckets that the generic allocator sets up in |
| 1146 // order to preserve a fast size->bucket map (see | 1251 // order to preserve a fast size->bucket map (see |
| 1147 // partitionAllocGenericInit for details). | 1252 // partitionAllocGenericInit for details). |
| 1148 if (!bucket->activePagesHead) | 1253 if (!bucket->activePagesHead) |
| 1149 bucketStats[i].isValid = false; | 1254 bucketStats[i].isValid = false; |
| 1150 else | 1255 else |
| 1151 partitionDumpBucketStats(&bucketStats[i], bucket); | 1256 partitionDumpBucketStats(&bucketStats[i], bucket); |
| 1152 } | 1257 } |
| 1153 | 1258 |
| 1154 static const size_t kMaxReportableDirectMaps = 4096; | |
| 1155 uint32_t directMapLengths[kMaxReportableDirectMaps]; | |
| 1156 size_t numDirectMappedAllocations = 0; | |
| 1157 for (PartitionDirectMapExtent* extent = partition->directMapList; extent; ex
tent = extent->nextExtent) { | 1259 for (PartitionDirectMapExtent* extent = partition->directMapList; extent; ex
tent = extent->nextExtent) { |
| 1158 ASSERT(!extent->nextExtent || extent->nextExtent->prevExtent == extent); | 1260 ASSERT(!extent->nextExtent || extent->nextExtent->prevExtent == extent); |
| 1159 directMapLengths[numDirectMappedAllocations] = extent->bucket->slotSize; | 1261 directMapLengths[numDirectMappedAllocations] = extent->bucket->slotSize; |
| 1160 ++numDirectMappedAllocations; | 1262 ++numDirectMappedAllocations; |
| 1161 if (numDirectMappedAllocations == kMaxReportableDirectMaps) | 1263 if (numDirectMappedAllocations == kMaxReportableDirectMaps) |
| 1162 break; | 1264 break; |
| 1163 } | 1265 } |
| 1164 | 1266 |
| 1165 spinLockUnlock(&partition->lock); | 1267 spinLockUnlock(&partition->lock); |
| 1166 | 1268 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1199 // partitionsDumpBucketStats is called after collecting stats because it | 1301 // partitionsDumpBucketStats is called after collecting stats because it |
| 1200 // can use PartitionAlloc to allocate and this can affect the statistics. | 1302 // can use PartitionAlloc to allocate and this can affect the statistics. |
| 1201 for (size_t i = 0; i < partitionNumBuckets; ++i) { | 1303 for (size_t i = 0; i < partitionNumBuckets; ++i) { |
| 1202 if (memoryStats[i].isValid) | 1304 if (memoryStats[i].isValid) |
| 1203 partitionStatsDumper->partitionsDumpBucketStats(partitionName, &memo
ryStats[i]); | 1305 partitionStatsDumper->partitionsDumpBucketStats(partitionName, &memo
ryStats[i]); |
| 1204 } | 1306 } |
| 1205 } | 1307 } |
| 1206 | 1308 |
| 1207 } // namespace WTF | 1309 } // namespace WTF |
| 1208 | 1310 |
| OLD | NEW |