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 |