Chromium Code Reviews| 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 1062 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1073 size_t copySize = actualOldSize; | 1073 size_t copySize = actualOldSize; |
| 1074 if (newSize < copySize) | 1074 if (newSize < copySize) |
| 1075 copySize = newSize; | 1075 copySize = newSize; |
| 1076 | 1076 |
| 1077 memcpy(ret, ptr, copySize); | 1077 memcpy(ret, ptr, copySize); |
| 1078 partitionFreeGeneric(root, ptr); | 1078 partitionFreeGeneric(root, ptr); |
| 1079 return ret; | 1079 return ret; |
| 1080 #endif | 1080 #endif |
| 1081 } | 1081 } |
| 1082 | 1082 |
| 1083 static size_t partitionPurgePage(const PartitionPage* page, bool discard) | 1083 static size_t partitionPurgePage(PartitionPage* page, bool discard) |
| 1084 { | 1084 { |
| 1085 const PartitionBucket* bucket = page->bucket; | 1085 const PartitionBucket* bucket = page->bucket; |
| 1086 if (bucket->slotSize < kSystemPageSize || !page->numAllocatedSlots) | 1086 size_t slotSize = bucket->slotSize; |
| 1087 if (slotSize < kSystemPageSize || !page->numAllocatedSlots) | |
| 1087 return 0; | 1088 return 0; |
| 1088 | 1089 |
| 1089 size_t bucketNumSlots = partitionBucketSlots(bucket); | 1090 size_t bucketNumSlots = partitionBucketSlots(bucket); |
| 1090 size_t discardableBytes = 0; | 1091 size_t discardableBytes = 0; |
| 1091 | 1092 |
| 1092 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page)); | 1093 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page)); |
| 1093 if (rawSize) { | 1094 if (rawSize) { |
| 1094 uint32_t usedBytes = static_cast<uint32_t>(partitionRoundUpToSystemPage( rawSize)); | 1095 uint32_t usedBytes = static_cast<uint32_t>(partitionRoundUpToSystemPage( rawSize)); |
| 1095 discardableBytes = bucket->slotSize - usedBytes; | 1096 discardableBytes = bucket->slotSize - usedBytes; |
| 1096 if (discardableBytes && discard) { | 1097 if (discardableBytes && discard) { |
| 1097 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page)); | 1098 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page)); |
| 1098 ptr += usedBytes; | 1099 ptr += usedBytes; |
| 1099 discardSystemPages(ptr, discardableBytes); | 1100 discardSystemPages(ptr, discardableBytes); |
| 1100 } | 1101 } |
| 1101 return discardableBytes; | 1102 return discardableBytes; |
| 1102 } | 1103 } |
| 1103 | 1104 |
| 1104 const size_t maxSlotCount = (kPartitionPageSize * kMaxPartitionPagesPerSlotS pan) / kSystemPageSize; | 1105 const size_t maxSlotCount = (kPartitionPageSize * kMaxPartitionPagesPerSlotS pan) / kSystemPageSize; |
| 1105 ASSERT(bucketNumSlots <= maxSlotCount); | 1106 ASSERT(bucketNumSlots <= maxSlotCount); |
| 1107 ASSERT(page->numUnprovisionedSlots < bucketNumSlots); | |
| 1108 size_t numSlots = bucketNumSlots - page->numUnprovisionedSlots; | |
| 1106 char slotUsage[maxSlotCount]; | 1109 char slotUsage[maxSlotCount]; |
| 1107 size_t lastSlot = static_cast<size_t>(-1); | 1110 size_t lastSlot = static_cast<size_t>(-1); |
| 1108 memset(slotUsage, 1, sizeof(slotUsage)); | 1111 memset(slotUsage, 1, numSlots); |
| 1109 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page)); | 1112 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page)); |
| 1110 PartitionFreelistEntry* entry = page->freelistHead; | 1113 PartitionFreelistEntry* entry = page->freelistHead; |
| 1111 // First, walk the freelist for this page and make a bitmap of which slots | 1114 // First, walk the freelist for this page and make a bitmap of which slots |
| 1112 // are not in use. | 1115 // are not in use. |
| 1113 while (entry) { | 1116 while (entry) { |
| 1114 size_t slotIndex = (reinterpret_cast<char*>(entry) - ptr) / bucket->slot Size; | 1117 size_t slotIndex = (reinterpret_cast<char*>(entry) - ptr) / slotSize; |
| 1115 ASSERT(slotIndex < bucketNumSlots); | 1118 ASSERT(slotIndex < numSlots); |
| 1116 slotUsage[slotIndex] = 0; | 1119 slotUsage[slotIndex] = 0; |
| 1117 entry = partitionFreelistMask(entry->next); | 1120 entry = partitionFreelistMask(entry->next); |
| 1118 // If we have a slot where the masked freelist entry is 0, we can | 1121 // If we have a slot where the masked freelist entry is 0, we can |
| 1119 // actually discard that freelist entry because touching a discarded | 1122 // actually discard that freelist entry because touching a discarded |
| 1120 // page is guaranteed to return original content or 0. | 1123 // page is guaranteed to return original content or 0. |
| 1121 // (Note that this optimization won't fire on big endian machines | 1124 // (Note that this optimization won't fire on big endian machines |
| 1122 // because the masking function is negation.) | 1125 // because the masking function is negation.) |
| 1123 if (!partitionFreelistMask(entry)) | 1126 if (!partitionFreelistMask(entry)) |
| 1124 lastSlot = slotIndex; | 1127 lastSlot = slotIndex; |
| 1125 } | 1128 } |
| 1129 | |
| 1130 // If the slot(s) at the end of the slot span are not in used, we can | |
| 1131 // truncate them entirely and rewrite the freelist. | |
| 1132 size_t truncatedSlots = 0; | |
| 1133 while (!slotUsage[numSlots - 1]) { | |
| 1134 truncatedSlots++; | |
| 1135 numSlots--; | |
| 1136 ASSERT(numSlots); | |
| 1137 } | |
| 1138 // First, do the work of calculating the discardable bytes. Don't actually | |
| 1139 // discard anything unless the discard flag was passed in. | |
| 1140 char* beginPtr; | |
| 1141 char* endPtr; | |
| 1142 size_t unprovisionedBytes = 0; | |
| 1143 if (truncatedSlots) { | |
| 1144 beginPtr = ptr + (numSlots * slotSize); | |
| 1145 endPtr = beginPtr + (slotSize * truncatedSlots); | |
| 1146 beginPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterp ret_cast<size_t>(beginPtr))); | |
| 1147 // We round the end pointer here up and not down because we're at the | |
| 1148 // end of a slot span, so we "own" all the way up the page boundary. | |
| 1149 endPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterpre t_cast<size_t>(endPtr))); | |
| 1150 ASSERT(endPtr <= ptr + partitionBucketBytes(bucket)); | |
| 1151 if (beginPtr < endPtr) { | |
| 1152 unprovisionedBytes = endPtr - beginPtr; | |
| 1153 discardableBytes += unprovisionedBytes; | |
| 1154 } | |
| 1155 } | |
| 1156 if (truncatedSlots && discard) { | |
|
haraken
2015/06/24 13:53:07
if (truncatedSlots && discard) => if (unprovisione
| |
| 1157 size_t numNewEntries = 0; | |
| 1158 page->numUnprovisionedSlots += truncatedSlots; | |
| 1159 // Rewrite the freelist. | |
| 1160 PartitionFreelistEntry** entryPtr = &page->freelistHead; | |
| 1161 for (size_t slotIndex = 0; slotIndex < numSlots; ++slotIndex) { | |
| 1162 if (slotUsage[slotIndex]) | |
| 1163 continue; | |
| 1164 PartitionFreelistEntry* entry = reinterpret_cast<PartitionFreelistEn try*>(ptr + (slotSize * slotIndex)); | |
| 1165 *entryPtr = partitionFreelistMask(entry); | |
| 1166 entryPtr = reinterpret_cast<PartitionFreelistEntry**>(entry); | |
| 1167 numNewEntries++; | |
| 1168 } | |
| 1169 // Terminate the freelist chain. | |
| 1170 *entryPtr = nullptr; | |
| 1171 // The freelist head is stored unmasked. | |
| 1172 page->freelistHead = partitionFreelistMask(page->freelistHead); | |
| 1173 ASSERT(numNewEntries == numSlots - page->numAllocatedSlots); | |
| 1174 // Discard the memory. | |
| 1175 discardSystemPages(beginPtr, unprovisionedBytes); | |
| 1176 } | |
| 1177 | |
| 1126 // Next, walk the slots and for any not in use, consider where the system | 1178 // Next, walk the slots and for any not in use, consider where the system |
| 1127 // page boundaries occur. We can release any system pages back to the | 1179 // page boundaries occur. We can release any system pages back to the |
| 1128 // system as long as we don't interfere with a freelist pointer or an | 1180 // system as long as we don't interfere with a freelist pointer or an |
| 1129 // adjacent slot. | 1181 // adjacent slot. |
| 1130 // TODO(cevans): I think we can "truncate" the page, i.e. increase the | 1182 for (size_t i = 0; i < numSlots; ++i) { |
| 1131 // value of page->numUnprovisionedSlots and rewrite(!) the freelist, if | |
| 1132 // we find that to be a win too. | |
| 1133 for (size_t i = 0; i < bucketNumSlots; ++i) { | |
| 1134 if (slotUsage[i]) | 1183 if (slotUsage[i]) |
| 1135 continue; | 1184 continue; |
| 1136 // The first address we can safely discard is just after the freelist | 1185 // The first address we can safely discard is just after the freelist |
| 1137 // pointer. There's one quirk: if the freelist pointer is actually a | 1186 // pointer. There's one quirk: if the freelist pointer is actually a |
| 1138 // null, we can discard that pointer value too. | 1187 // null, we can discard that pointer value too. |
| 1139 char* beginPtr = ptr + (i * bucket->slotSize); | 1188 char* beginPtr = ptr + (i * slotSize); |
| 1140 char* endPtr = beginPtr + bucket->slotSize; | 1189 char* endPtr = beginPtr + slotSize; |
| 1141 if (i != lastSlot) | 1190 if (i != lastSlot) |
| 1142 beginPtr += sizeof(PartitionFreelistEntry); | 1191 beginPtr += sizeof(PartitionFreelistEntry); |
| 1143 beginPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterp ret_cast<size_t>(beginPtr))); | 1192 beginPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterp ret_cast<size_t>(beginPtr))); |
| 1144 endPtr = reinterpret_cast<char*>(partitionRoundDownToSystemPage(reinterp ret_cast<size_t>(endPtr))); | 1193 endPtr = reinterpret_cast<char*>(partitionRoundDownToSystemPage(reinterp ret_cast<size_t>(endPtr))); |
| 1145 if (beginPtr < endPtr) { | 1194 if (beginPtr < endPtr) { |
| 1146 size_t partialSlotBytes = endPtr - beginPtr; | 1195 size_t partialSlotBytes = endPtr - beginPtr; |
| 1147 discardableBytes += partialSlotBytes; | 1196 discardableBytes += partialSlotBytes; |
| 1148 if (discard) | 1197 if (discard) |
| 1149 discardSystemPages(beginPtr, partialSlotBytes); | 1198 discardSystemPages(beginPtr, partialSlotBytes); |
| 1150 } | 1199 } |
| 1151 } | 1200 } |
| 1152 return discardableBytes; | 1201 return discardableBytes; |
| 1153 } | 1202 } |
| 1154 | 1203 |
| 1155 static void partitionPurgeBucket(const PartitionBucket* bucket) | 1204 static void partitionPurgeBucket(PartitionBucket* bucket) |
| 1156 { | 1205 { |
| 1157 if (bucket->activePagesHead != &PartitionRootGeneric::gSeedPage) { | 1206 if (bucket->activePagesHead != &PartitionRootGeneric::gSeedPage) { |
| 1158 for (const PartitionPage* page = bucket->activePagesHead; page; page = p age->nextPage) { | 1207 for (PartitionPage* page = bucket->activePagesHead; page; page = page->n extPage) { |
| 1159 ASSERT(page != &PartitionRootGeneric::gSeedPage); | 1208 ASSERT(page != &PartitionRootGeneric::gSeedPage); |
| 1160 (void) partitionPurgePage(page, true); | 1209 (void) partitionPurgePage(page, true); |
| 1161 } | 1210 } |
| 1162 } | 1211 } |
| 1163 } | 1212 } |
| 1164 | 1213 |
| 1165 void partitionPurgeMemory(PartitionRoot* root, int flags) | 1214 void partitionPurgeMemory(PartitionRoot* root, int flags) |
| 1166 { | 1215 { |
| 1167 if (flags & PartitionPurgeDecommitEmptyPages) | 1216 if (flags & PartitionPurgeDecommitEmptyPages) |
| 1168 partitionDecommitEmptyPages(root); | 1217 partitionDecommitEmptyPages(root); |
| 1169 // We don't currently do anything for PartitionPurgeDiscardUnusedSystemPages | 1218 // We don't currently do anything for PartitionPurgeDiscardUnusedSystemPages |
| 1170 // here because that flag is only useful for allocations >= system page | 1219 // here because that flag is only useful for allocations >= system page |
| 1171 // size. We only have allocations that large inside generic partitions | 1220 // size. We only have allocations that large inside generic partitions |
| 1172 // at the moment. | 1221 // at the moment. |
| 1173 } | 1222 } |
| 1174 | 1223 |
| 1175 void partitionPurgeMemoryGeneric(PartitionRootGeneric* root, int flags) | 1224 void partitionPurgeMemoryGeneric(PartitionRootGeneric* root, int flags) |
| 1176 { | 1225 { |
| 1177 spinLockLock(&root->lock); | 1226 spinLockLock(&root->lock); |
| 1178 if (flags & PartitionPurgeDecommitEmptyPages) | 1227 if (flags & PartitionPurgeDecommitEmptyPages) |
| 1179 partitionDecommitEmptyPages(root); | 1228 partitionDecommitEmptyPages(root); |
| 1180 if (flags & PartitionPurgeDiscardUnusedSystemPages) { | 1229 if (flags & PartitionPurgeDiscardUnusedSystemPages) { |
| 1181 for (size_t i = 0; i < kGenericNumBuckets; ++i) { | 1230 for (size_t i = 0; i < kGenericNumBuckets; ++i) { |
| 1182 const PartitionBucket* bucket = &root->buckets[i]; | 1231 PartitionBucket* bucket = &root->buckets[i]; |
| 1183 if (bucket->slotSize >= kSystemPageSize) | 1232 if (bucket->slotSize >= kSystemPageSize) |
| 1184 partitionPurgeBucket(bucket); | 1233 partitionPurgeBucket(bucket); |
| 1185 } | 1234 } |
| 1186 } | 1235 } |
| 1187 spinLockUnlock(&root->lock); | 1236 spinLockUnlock(&root->lock); |
| 1188 } | 1237 } |
| 1189 | 1238 |
| 1190 static void partitionDumpPageStats(PartitionBucketMemoryStats* statsOut, const P artitionPage* page) | 1239 static void partitionDumpPageStats(PartitionBucketMemoryStats* statsOut, const P artitionPage* page) |
| 1191 { | 1240 { |
| 1192 uint16_t bucketNumSlots = partitionBucketSlots(page->bucket); | 1241 uint16_t bucketNumSlots = partitionBucketSlots(page->bucket); |
| 1193 | 1242 |
| 1194 if (partitionPageStateIsDecommitted(page)) { | 1243 if (partitionPageStateIsDecommitted(page)) { |
| 1195 ++statsOut->numDecommittedPages; | 1244 ++statsOut->numDecommittedPages; |
| 1196 return; | 1245 return; |
| 1197 } | 1246 } |
| 1198 | 1247 |
| 1199 statsOut->discardableBytes += partitionPurgePage(page, false); | 1248 statsOut->discardableBytes += partitionPurgePage(const_cast<PartitionPage*>( page), false); |
| 1200 | 1249 |
| 1201 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page)); | 1250 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page)); |
| 1202 if (rawSize) | 1251 if (rawSize) |
| 1203 statsOut->activeBytes += static_cast<uint32_t>(rawSize); | 1252 statsOut->activeBytes += static_cast<uint32_t>(rawSize); |
| 1204 else | 1253 else |
| 1205 statsOut->activeBytes += (page->numAllocatedSlots * statsOut->bucketSlot Size); | 1254 statsOut->activeBytes += (page->numAllocatedSlots * statsOut->bucketSlot Size); |
| 1206 | 1255 |
| 1207 size_t pageBytesResident = partitionRoundUpToSystemPage((bucketNumSlots - pa ge->numUnprovisionedSlots) * statsOut->bucketSlotSize); | 1256 size_t pageBytesResident = partitionRoundUpToSystemPage((bucketNumSlots - pa ge->numUnprovisionedSlots) * statsOut->bucketSlotSize); |
| 1208 statsOut->residentBytes += pageBytesResident; | 1257 statsOut->residentBytes += pageBytesResident; |
| 1209 if (partitionPageStateIsEmpty(page)) { | 1258 if (partitionPageStateIsEmpty(page)) { |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1320 // partitionsDumpBucketStats is called after collecting stats because it | 1369 // partitionsDumpBucketStats is called after collecting stats because it |
| 1321 // can use PartitionAlloc to allocate and this can affect the statistics. | 1370 // can use PartitionAlloc to allocate and this can affect the statistics. |
| 1322 for (size_t i = 0; i < partitionNumBuckets; ++i) { | 1371 for (size_t i = 0; i < partitionNumBuckets; ++i) { |
| 1323 if (memoryStats[i].isValid) | 1372 if (memoryStats[i].isValid) |
| 1324 partitionStatsDumper->partitionsDumpBucketStats(partitionName, &memo ryStats[i]); | 1373 partitionStatsDumper->partitionsDumpBucketStats(partitionName, &memo ryStats[i]); |
| 1325 } | 1374 } |
| 1326 } | 1375 } |
| 1327 | 1376 |
| 1328 } // namespace WTF | 1377 } // namespace WTF |
| 1329 | 1378 |
| OLD | NEW |