| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/child/child_discardable_shared_memory_manager.h" | 5 #include "content/child/child_discardable_shared_memory_manager.h" |
| 6 | 6 |
| 7 #include "base/debug/crash_logging.h" | 7 #include "base/debug/crash_logging.h" |
| 8 #include "base/memory/discardable_shared_memory.h" | 8 #include "base/memory/discardable_shared_memory.h" |
| 9 #include "base/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" |
| 10 #include "base/process/process_metrics.h" | 10 #include "base/process/process_metrics.h" |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 45 ChildDiscardableSharedMemoryManager* manager_; | 45 ChildDiscardableSharedMemoryManager* manager_; |
| 46 scoped_ptr<DiscardableSharedMemoryHeap::Span> span_; | 46 scoped_ptr<DiscardableSharedMemoryHeap::Span> span_; |
| 47 | 47 |
| 48 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryShmemChunkImpl); | 48 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryShmemChunkImpl); |
| 49 }; | 49 }; |
| 50 | 50 |
| 51 } // namespace | 51 } // namespace |
| 52 | 52 |
| 53 ChildDiscardableSharedMemoryManager::ChildDiscardableSharedMemoryManager( | 53 ChildDiscardableSharedMemoryManager::ChildDiscardableSharedMemoryManager( |
| 54 ThreadSafeSender* sender) | 54 ThreadSafeSender* sender) |
| 55 : heap_(base::GetPageSize()), sender_(sender), bytes_allocated_(0) { | 55 : heap_(base::GetPageSize()), sender_(sender) { |
| 56 } | 56 } |
| 57 | 57 |
| 58 ChildDiscardableSharedMemoryManager::~ChildDiscardableSharedMemoryManager() { | 58 ChildDiscardableSharedMemoryManager::~ChildDiscardableSharedMemoryManager() { |
| 59 if (bytes_allocated_) | 59 // TODO(reveman): Determine if this DCHECK can be enabled. crbug.com/430533 |
| 60 BytesAllocatedChanged(0); | 60 // DCHECK_EQ(heap_.GetSize(), heap_.GetFreeListSize()); |
| 61 if (heap_.GetSize()) |
| 62 MemoryUsageChanged(0, 0); |
| 61 } | 63 } |
| 62 | 64 |
| 63 scoped_ptr<base::DiscardableMemoryShmemChunk> | 65 scoped_ptr<base::DiscardableMemoryShmemChunk> |
| 64 ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory( | 66 ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory( |
| 65 size_t size) { | 67 size_t size) { |
| 66 base::AutoLock lock(lock_); | 68 base::AutoLock lock(lock_); |
| 67 | 69 |
| 68 DCHECK_NE(size, 0u); | 70 DCHECK_NE(size, 0u); |
| 69 | 71 |
| 70 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.DiscardableAllocationSize", | 72 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.DiscardableAllocationSize", |
| 71 size / 1024, // In KB | 73 size / 1024, // In KB |
| 72 1, | 74 1, |
| 73 4 * 1024 * 1024, // 4 GB | 75 4 * 1024 * 1024, // 4 GB |
| 74 50); | 76 50); |
| 75 | 77 |
| 76 // Round up to multiple of page size. | 78 // Round up to multiple of page size. |
| 77 size_t pages = (size + base::GetPageSize() - 1) / base::GetPageSize(); | 79 size_t pages = (size + base::GetPageSize() - 1) / base::GetPageSize(); |
| 78 | 80 |
| 81 size_t heap_size_prior_to_releasing_purged_memory = heap_.GetSize(); |
| 79 for (;;) { | 82 for (;;) { |
| 80 // Search free list for available space. | 83 // Search free list for available space. |
| 81 scoped_ptr<DiscardableSharedMemoryHeap::Span> free_span = | 84 scoped_ptr<DiscardableSharedMemoryHeap::Span> free_span = |
| 82 heap_.SearchFreeList(pages); | 85 heap_.SearchFreeList(pages); |
| 83 if (!free_span.get()) | 86 if (!free_span.get()) |
| 84 break; | 87 break; |
| 85 | 88 |
| 86 // Attempt to lock |free_span|. Delete span and search free list again | 89 // Attempt to lock |free_span|. Delete span and search free list again |
| 87 // if locking failed. | 90 // if locking failed. |
| 88 if (free_span->shared_memory()->Lock( | 91 if (free_span->shared_memory()->Lock( |
| 89 free_span->start() * base::GetPageSize() - | 92 free_span->start() * base::GetPageSize() - |
| 90 reinterpret_cast<size_t>(free_span->shared_memory()->memory()), | 93 reinterpret_cast<size_t>(free_span->shared_memory()->memory()), |
| 91 free_span->length() * base::GetPageSize()) == | 94 free_span->length() * base::GetPageSize()) == |
| 92 base::DiscardableSharedMemory::FAILED) { | 95 base::DiscardableSharedMemory::FAILED) { |
| 93 DCHECK(!free_span->shared_memory()->IsMemoryResident()); | 96 DCHECK(!free_span->shared_memory()->IsMemoryResident()); |
| 94 // We have to release free memory before |free_span| can be destroyed. | 97 // We have to release purged memory before |free_span| can be destroyed. |
| 95 size_t bytes_released = heap_.ReleaseFreeMemory(); | 98 heap_.ReleasePurgedMemory(); |
| 96 DCHECK_NE(bytes_released, 0u); | |
| 97 DCHECK_LE(bytes_released, bytes_allocated_); | |
| 98 bytes_allocated_ -= bytes_released; | |
| 99 BytesAllocatedChanged(bytes_allocated_); | |
| 100 DCHECK(!free_span->shared_memory()); | 99 DCHECK(!free_span->shared_memory()); |
| 101 continue; | 100 continue; |
| 102 } | 101 } |
| 103 | 102 |
| 103 // Memory usage is guaranteed to have changed after having removed |
| 104 // at least one span from the free list. |
| 105 MemoryUsageChanged(heap_.GetSize(), heap_.GetFreeListSize()); |
| 106 |
| 104 return make_scoped_ptr( | 107 return make_scoped_ptr( |
| 105 new DiscardableMemoryShmemChunkImpl(this, free_span.Pass())); | 108 new DiscardableMemoryShmemChunkImpl(this, free_span.Pass())); |
| 106 } | 109 } |
| 107 | 110 |
| 108 // Release free memory and free up the address space. Failing to do this | 111 // Release purged memory to free up the address space before we attempt to |
| 109 // can result in the child process running out of address space. | 112 // allocate more memory. |
| 110 size_t bytes_released = heap_.ReleaseFreeMemory(); | 113 heap_.ReleasePurgedMemory(); |
| 111 if (bytes_released) { | 114 |
| 112 DCHECK_LE(bytes_released, bytes_allocated_); | 115 // Make sure crash keys are up to date in case allocation fails. |
| 113 bytes_allocated_ -= bytes_released; | 116 if (heap_.GetSize() != heap_size_prior_to_releasing_purged_memory) |
| 114 BytesAllocatedChanged(bytes_allocated_); | 117 MemoryUsageChanged(heap_.GetSize(), heap_.GetFreeListSize()); |
| 115 } | |
| 116 | 118 |
| 117 size_t pages_to_allocate = | 119 size_t pages_to_allocate = |
| 118 std::max(kAllocationSize / base::GetPageSize(), pages); | 120 std::max(kAllocationSize / base::GetPageSize(), pages); |
| 119 size_t allocation_size_in_bytes = pages_to_allocate * base::GetPageSize(); | 121 size_t allocation_size_in_bytes = pages_to_allocate * base::GetPageSize(); |
| 120 | 122 |
| 121 // Ask parent process to allocate a new discardable shared memory segment. | 123 // Ask parent process to allocate a new discardable shared memory segment. |
| 122 scoped_ptr<base::DiscardableSharedMemory> shared_memory( | 124 scoped_ptr<base::DiscardableSharedMemory> shared_memory( |
| 123 AllocateLockedDiscardableSharedMemory(allocation_size_in_bytes)); | 125 AllocateLockedDiscardableSharedMemory(allocation_size_in_bytes)); |
| 124 | 126 |
| 125 // Note: use mapped size rather than |allocation_size_in_bytes| as | |
| 126 // the latter only represent the amount of memory available for usage. | |
| 127 bytes_allocated_ += shared_memory->mapped_size(); | |
| 128 BytesAllocatedChanged(bytes_allocated_); | |
| 129 | |
| 130 // Create span for allocated memory. | 127 // Create span for allocated memory. |
| 131 scoped_ptr<DiscardableSharedMemoryHeap::Span> new_span( | 128 scoped_ptr<DiscardableSharedMemoryHeap::Span> new_span( |
| 132 heap_.Grow(shared_memory.Pass(), allocation_size_in_bytes)); | 129 heap_.Grow(shared_memory.Pass(), allocation_size_in_bytes)); |
| 133 | 130 |
| 134 // Unlock and insert any left over memory into free list. | 131 // Unlock and insert any left over memory into free list. |
| 135 if (pages < pages_to_allocate) { | 132 if (pages < pages_to_allocate) { |
| 136 scoped_ptr<DiscardableSharedMemoryHeap::Span> leftover = | 133 scoped_ptr<DiscardableSharedMemoryHeap::Span> leftover = |
| 137 heap_.Split(new_span.get(), pages); | 134 heap_.Split(new_span.get(), pages); |
| 138 leftover->shared_memory()->Unlock( | 135 leftover->shared_memory()->Unlock( |
| 139 leftover->start() * base::GetPageSize() - | 136 leftover->start() * base::GetPageSize() - |
| 140 reinterpret_cast<size_t>(leftover->shared_memory()->memory()), | 137 reinterpret_cast<size_t>(leftover->shared_memory()->memory()), |
| 141 leftover->length() * base::GetPageSize()); | 138 leftover->length() * base::GetPageSize()); |
| 142 heap_.MergeIntoFreeList(leftover.Pass()); | 139 heap_.MergeIntoFreeList(leftover.Pass()); |
| 143 } | 140 } |
| 144 | 141 |
| 142 MemoryUsageChanged(heap_.GetSize(), heap_.GetFreeListSize()); |
| 143 |
| 145 return make_scoped_ptr( | 144 return make_scoped_ptr( |
| 146 new DiscardableMemoryShmemChunkImpl(this, new_span.Pass())); | 145 new DiscardableMemoryShmemChunkImpl(this, new_span.Pass())); |
| 147 } | 146 } |
| 148 | 147 |
| 149 void ChildDiscardableSharedMemoryManager::ReleaseFreeMemory() { | 148 void ChildDiscardableSharedMemoryManager::ReleaseFreeMemory() { |
| 150 base::AutoLock lock(lock_); | 149 base::AutoLock lock(lock_); |
| 151 | 150 |
| 152 size_t bytes_released = heap_.ReleaseFreeMemory(); | 151 size_t heap_size_prior_to_releasing_memory = heap_.GetSize(); |
| 153 if (bytes_released) { | 152 |
| 154 DCHECK_LE(bytes_released, bytes_allocated_); | 153 // Release both purged and free memory. |
| 155 bytes_allocated_ -= bytes_released; | 154 heap_.ReleasePurgedMemory(); |
| 156 BytesAllocatedChanged(bytes_allocated_); | 155 heap_.ReleaseFreeMemory(); |
| 157 } | 156 |
| 157 if (heap_.GetSize() != heap_size_prior_to_releasing_memory) |
| 158 MemoryUsageChanged(heap_.GetSize(), heap_.GetFreeListSize()); |
| 158 } | 159 } |
| 159 | 160 |
| 160 bool ChildDiscardableSharedMemoryManager::LockSpan( | 161 bool ChildDiscardableSharedMemoryManager::LockSpan( |
| 161 DiscardableSharedMemoryHeap::Span* span) { | 162 DiscardableSharedMemoryHeap::Span* span) { |
| 162 base::AutoLock lock(lock_); | 163 base::AutoLock lock(lock_); |
| 163 | 164 |
| 164 if (!span->shared_memory()) | 165 if (!span->shared_memory()) |
| 165 return false; | 166 return false; |
| 166 | 167 |
| 167 size_t offset = span->start() * base::GetPageSize() - | 168 size_t offset = span->start() * base::GetPageSize() - |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 201 // Delete span instead of merging it into free list if memory is gone. | 202 // Delete span instead of merging it into free list if memory is gone. |
| 202 if (!span->shared_memory()) | 203 if (!span->shared_memory()) |
| 203 return; | 204 return; |
| 204 | 205 |
| 205 // Purge backing memory if span is greater than kAllocationSize. This will | 206 // Purge backing memory if span is greater than kAllocationSize. This will |
| 206 // prevent it from being reused and associated resources will be released. | 207 // prevent it from being reused and associated resources will be released. |
| 207 if (span->length() * base::GetPageSize() > kAllocationSize) | 208 if (span->length() * base::GetPageSize() > kAllocationSize) |
| 208 span->shared_memory()->Purge(base::Time::Now()); | 209 span->shared_memory()->Purge(base::Time::Now()); |
| 209 | 210 |
| 210 heap_.MergeIntoFreeList(span.Pass()); | 211 heap_.MergeIntoFreeList(span.Pass()); |
| 212 |
| 213 // Bytes of free memory changed. |
| 214 MemoryUsageChanged(heap_.GetSize(), heap_.GetFreeListSize()); |
| 211 } | 215 } |
| 212 | 216 |
| 213 scoped_ptr<base::DiscardableSharedMemory> | 217 scoped_ptr<base::DiscardableSharedMemory> |
| 214 ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory( | 218 ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory( |
| 215 size_t size) { | 219 size_t size) { |
| 216 TRACE_EVENT1("renderer", | 220 TRACE_EVENT1("renderer", |
| 217 "ChildDiscardableSharedMemoryManager::" | 221 "ChildDiscardableSharedMemoryManager::" |
| 218 "AllocateLockedDiscardableSharedMemory", | 222 "AllocateLockedDiscardableSharedMemory", |
| 219 "size", | 223 "size", |
| 220 size); | 224 size); |
| 221 | 225 |
| 222 base::SharedMemoryHandle handle = base::SharedMemory::NULLHandle(); | 226 base::SharedMemoryHandle handle = base::SharedMemory::NULLHandle(); |
| 223 sender_->Send( | 227 sender_->Send( |
| 224 new ChildProcessHostMsg_SyncAllocateLockedDiscardableSharedMemory( | 228 new ChildProcessHostMsg_SyncAllocateLockedDiscardableSharedMemory( |
| 225 size, &handle)); | 229 size, &handle)); |
| 226 CHECK(base::SharedMemory::IsHandleValid(handle)); | 230 CHECK(base::SharedMemory::IsHandleValid(handle)); |
| 227 scoped_ptr<base::DiscardableSharedMemory> memory( | 231 scoped_ptr<base::DiscardableSharedMemory> memory( |
| 228 new base::DiscardableSharedMemory(handle)); | 232 new base::DiscardableSharedMemory(handle)); |
| 229 CHECK(memory->Map(size)); | 233 CHECK(memory->Map(size)); |
| 230 return memory.Pass(); | 234 return memory.Pass(); |
| 231 } | 235 } |
| 232 | 236 |
| 233 void ChildDiscardableSharedMemoryManager::BytesAllocatedChanged( | 237 void ChildDiscardableSharedMemoryManager::MemoryUsageChanged( |
| 234 size_t new_bytes_allocated) const { | 238 size_t new_bytes_total, |
| 235 TRACE_COUNTER_ID1("renderer", "DiscardableMemoryUsage", this, | 239 size_t new_bytes_free) const { |
| 236 new_bytes_allocated); | 240 TRACE_COUNTER2("renderer", "DiscardableMemoryUsage", "allocated", |
| 241 new_bytes_total - new_bytes_free, "free", new_bytes_free); |
| 237 | 242 |
| 238 static const char kDiscardableMemoryUsageKey[] = "dm-usage"; | 243 static const char kDiscardableMemoryUsageKey[] = "dm-usage"; |
| 239 base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey, | 244 base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey, |
| 240 base::Uint64ToString(new_bytes_allocated)); | 245 base::Uint64ToString(new_bytes_total)); |
| 246 |
| 247 static const char kDiscardableMemoryUsageFreeKey[] = "dm-usage-free"; |
| 248 base::debug::SetCrashKeyValue(kDiscardableMemoryUsageFreeKey, |
| 249 base::Uint64ToString(new_bytes_free)); |
| 241 } | 250 } |
| 242 | 251 |
| 243 } // namespace content | 252 } // namespace content |
| OLD | NEW |