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