Index: content/common/discardable_shared_memory_heap.cc |
diff --git a/content/common/discardable_shared_memory_heap.cc b/content/common/discardable_shared_memory_heap.cc |
deleted file mode 100644 |
index b6559af4c103d42b6c631631d76fa9e32847342c..0000000000000000000000000000000000000000 |
--- a/content/common/discardable_shared_memory_heap.cc |
+++ /dev/null |
@@ -1,477 +0,0 @@ |
-// Copyright 2014 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "content/common/discardable_shared_memory_heap.h" |
- |
-#include <algorithm> |
-#include <utility> |
- |
-#include "base/format_macros.h" |
-#include "base/macros.h" |
-#include "base/memory/discardable_shared_memory.h" |
-#include "base/memory/ptr_util.h" |
-#include "base/strings/stringprintf.h" |
-#include "base/trace_event/memory_dump_manager.h" |
- |
-namespace content { |
-namespace { |
- |
-bool IsPowerOfTwo(size_t x) { |
- return (x & (x - 1)) == 0; |
-} |
- |
-bool IsInFreeList(DiscardableSharedMemoryHeap::Span* span) { |
- return span->previous() || span->next(); |
-} |
- |
-} // namespace |
- |
-DiscardableSharedMemoryHeap::Span::Span( |
- base::DiscardableSharedMemory* shared_memory, |
- size_t start, |
- size_t length) |
- : shared_memory_(shared_memory), |
- start_(start), |
- length_(length), |
- is_locked_(false) {} |
- |
-DiscardableSharedMemoryHeap::Span::~Span() { |
-} |
- |
-DiscardableSharedMemoryHeap::ScopedMemorySegment::ScopedMemorySegment( |
- DiscardableSharedMemoryHeap* heap, |
- std::unique_ptr<base::DiscardableSharedMemory> shared_memory, |
- size_t size, |
- int32_t id, |
- const base::Closure& deleted_callback) |
- : heap_(heap), |
- shared_memory_(std::move(shared_memory)), |
- size_(size), |
- id_(id), |
- deleted_callback_(deleted_callback) {} |
- |
-DiscardableSharedMemoryHeap::ScopedMemorySegment::~ScopedMemorySegment() { |
- heap_->ReleaseMemory(shared_memory_.get(), size_); |
- deleted_callback_.Run(); |
-} |
- |
-bool DiscardableSharedMemoryHeap::ScopedMemorySegment::IsUsed() const { |
- return heap_->IsMemoryUsed(shared_memory_.get(), size_); |
-} |
- |
-bool DiscardableSharedMemoryHeap::ScopedMemorySegment::IsResident() const { |
- return heap_->IsMemoryResident(shared_memory_.get()); |
-} |
- |
-bool DiscardableSharedMemoryHeap::ScopedMemorySegment::ContainsSpan( |
- Span* span) const { |
- return shared_memory_.get() == span->shared_memory(); |
-} |
- |
-base::trace_event::MemoryAllocatorDump* |
-DiscardableSharedMemoryHeap::ScopedMemorySegment::CreateMemoryAllocatorDump( |
- Span* span, |
- size_t block_size, |
- const char* name, |
- base::trace_event::ProcessMemoryDump* pmd) const { |
- DCHECK_EQ(shared_memory_.get(), span->shared_memory()); |
- base::trace_event::MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(name); |
- dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, |
- base::trace_event::MemoryAllocatorDump::kUnitsBytes, |
- static_cast<uint64_t>(span->length() * block_size)); |
- |
- pmd->AddSuballocation( |
- dump->guid(), |
- base::StringPrintf("discardable/segment_%d/allocated_objects", id_)); |
- return dump; |
-} |
- |
-void DiscardableSharedMemoryHeap::ScopedMemorySegment::OnMemoryDump( |
- base::trace_event::ProcessMemoryDump* pmd) const { |
- heap_->OnMemoryDump(shared_memory_.get(), size_, id_, pmd); |
-} |
- |
-DiscardableSharedMemoryHeap::DiscardableSharedMemoryHeap(size_t block_size) |
- : block_size_(block_size), num_blocks_(0), num_free_blocks_(0) { |
- DCHECK_NE(block_size_, 0u); |
- DCHECK(IsPowerOfTwo(block_size_)); |
-} |
- |
-DiscardableSharedMemoryHeap::~DiscardableSharedMemoryHeap() { |
- memory_segments_.clear(); |
- DCHECK_EQ(num_blocks_, 0u); |
- DCHECK_EQ(num_free_blocks_, 0u); |
- DCHECK_EQ(std::count_if(free_spans_, free_spans_ + arraysize(free_spans_), |
- [](const base::LinkedList<Span>& free_spans) { |
- return !free_spans.empty(); |
- }), |
- 0); |
-} |
- |
-std::unique_ptr<DiscardableSharedMemoryHeap::Span> |
-DiscardableSharedMemoryHeap::Grow( |
- std::unique_ptr<base::DiscardableSharedMemory> shared_memory, |
- size_t size, |
- int32_t id, |
- const base::Closure& deleted_callback) { |
- // Memory must be aligned to block size. |
- DCHECK_EQ( |
- reinterpret_cast<size_t>(shared_memory->memory()) & (block_size_ - 1), |
- 0u); |
- DCHECK_EQ(size & (block_size_ - 1), 0u); |
- |
- std::unique_ptr<Span> span( |
- new Span(shared_memory.get(), |
- reinterpret_cast<size_t>(shared_memory->memory()) / block_size_, |
- size / block_size_)); |
- DCHECK(spans_.find(span->start_) == spans_.end()); |
- DCHECK(spans_.find(span->start_ + span->length_ - 1) == spans_.end()); |
- RegisterSpan(span.get()); |
- |
- num_blocks_ += span->length_; |
- |
- // Start tracking if segment is resident by adding it to |memory_segments_|. |
- memory_segments_.push_back(new ScopedMemorySegment( |
- this, std::move(shared_memory), size, id, deleted_callback)); |
- |
- return span; |
-} |
- |
-void DiscardableSharedMemoryHeap::MergeIntoFreeLists( |
- std::unique_ptr<Span> span) { |
- DCHECK(span->shared_memory_); |
- |
- // First add length of |span| to |num_free_blocks_|. |
- num_free_blocks_ += span->length_; |
- |
- // Merge with previous span if possible. |
- SpanMap::iterator prev_it = spans_.find(span->start_ - 1); |
- if (prev_it != spans_.end() && IsInFreeList(prev_it->second)) { |
- std::unique_ptr<Span> prev = RemoveFromFreeList(prev_it->second); |
- DCHECK_EQ(prev->start_ + prev->length_, span->start_); |
- UnregisterSpan(prev.get()); |
- if (span->length_ > 1) |
- spans_.erase(span->start_); |
- span->start_ -= prev->length_; |
- span->length_ += prev->length_; |
- spans_[span->start_] = span.get(); |
- } |
- |
- // Merge with next span if possible. |
- SpanMap::iterator next_it = spans_.find(span->start_ + span->length_); |
- if (next_it != spans_.end() && IsInFreeList(next_it->second)) { |
- std::unique_ptr<Span> next = RemoveFromFreeList(next_it->second); |
- DCHECK_EQ(next->start_, span->start_ + span->length_); |
- UnregisterSpan(next.get()); |
- if (span->length_ > 1) |
- spans_.erase(span->start_ + span->length_ - 1); |
- span->length_ += next->length_; |
- spans_[span->start_ + span->length_ - 1] = span.get(); |
- } |
- |
- InsertIntoFreeList(std::move(span)); |
-} |
- |
-std::unique_ptr<DiscardableSharedMemoryHeap::Span> |
-DiscardableSharedMemoryHeap::Split(Span* span, size_t blocks) { |
- DCHECK(blocks); |
- DCHECK_LT(blocks, span->length_); |
- |
- std::unique_ptr<Span> leftover(new Span( |
- span->shared_memory_, span->start_ + blocks, span->length_ - blocks)); |
- DCHECK(leftover->length_ == 1 || |
- spans_.find(leftover->start_) == spans_.end()); |
- RegisterSpan(leftover.get()); |
- spans_[span->start_ + blocks - 1] = span; |
- span->length_ = blocks; |
- return leftover; |
-} |
- |
-std::unique_ptr<DiscardableSharedMemoryHeap::Span> |
-DiscardableSharedMemoryHeap::SearchFreeLists(size_t blocks, size_t slack) { |
- DCHECK(blocks); |
- |
- size_t length = blocks; |
- size_t max_length = blocks + slack; |
- |
- // Search array of free lists for a suitable span. |
- while (length - 1 < arraysize(free_spans_) - 1) { |
- const base::LinkedList<Span>& free_spans = free_spans_[length - 1]; |
- if (!free_spans.empty()) { |
- // Return the most recently used span located in tail. |
- return Carve(free_spans.tail()->value(), blocks); |
- } |
- |
- // Return early after surpassing |max_length|. |
- if (++length > max_length) |
- return nullptr; |
- } |
- |
- const base::LinkedList<Span>& overflow_free_spans = |
- free_spans_[arraysize(free_spans_) - 1]; |
- |
- // Search overflow free list for a suitable span. Starting with the most |
- // recently used span located in tail and moving towards head. |
- for (base::LinkNode<Span>* node = overflow_free_spans.tail(); |
- node != overflow_free_spans.end(); node = node->previous()) { |
- Span* span = node->value(); |
- if (span->length_ >= blocks && span->length_ <= max_length) |
- return Carve(span, blocks); |
- } |
- |
- return nullptr; |
-} |
- |
-void DiscardableSharedMemoryHeap::ReleaseFreeMemory() { |
- // Erase all free segments after rearranging the segments in such a way |
- // that used segments precede all free segments. |
- memory_segments_.erase( |
- std::partition( |
- memory_segments_.begin(), memory_segments_.end(), |
- [](const ScopedMemorySegment* segment) { return segment->IsUsed(); }), |
- memory_segments_.end()); |
-} |
- |
-void DiscardableSharedMemoryHeap::ReleasePurgedMemory() { |
- // Erase all purged segments after rearranging the segments in such a way |
- // that resident segments precede all purged segments. |
- memory_segments_.erase( |
- std::partition(memory_segments_.begin(), memory_segments_.end(), |
- [](const ScopedMemorySegment* segment) { |
- return segment->IsResident(); |
- }), |
- memory_segments_.end()); |
-} |
- |
-size_t DiscardableSharedMemoryHeap::GetSize() const { |
- return num_blocks_ * block_size_; |
-} |
- |
-size_t DiscardableSharedMemoryHeap::GetSizeOfFreeLists() const { |
- return num_free_blocks_ * block_size_; |
-} |
- |
-bool DiscardableSharedMemoryHeap::OnMemoryDump( |
- base::trace_event::ProcessMemoryDump* pmd) { |
- std::for_each( |
- memory_segments_.begin(), memory_segments_.end(), |
- [pmd](const ScopedMemorySegment* segment) { |
- segment->OnMemoryDump(pmd); |
- }); |
- return true; |
-} |
- |
-void DiscardableSharedMemoryHeap::InsertIntoFreeList( |
- std::unique_ptr<DiscardableSharedMemoryHeap::Span> span) { |
- DCHECK(!IsInFreeList(span.get())); |
- size_t index = std::min(span->length_, arraysize(free_spans_)) - 1; |
- free_spans_[index].Append(span.release()); |
-} |
- |
-std::unique_ptr<DiscardableSharedMemoryHeap::Span> |
-DiscardableSharedMemoryHeap::RemoveFromFreeList(Span* span) { |
- DCHECK(IsInFreeList(span)); |
- span->RemoveFromList(); |
- return base::WrapUnique(span); |
-} |
- |
-std::unique_ptr<DiscardableSharedMemoryHeap::Span> |
-DiscardableSharedMemoryHeap::Carve(Span* span, size_t blocks) { |
- std::unique_ptr<Span> serving = RemoveFromFreeList(span); |
- |
- const int extra = serving->length_ - blocks; |
- if (extra) { |
- std::unique_ptr<Span> leftover( |
- new Span(serving->shared_memory_, serving->start_ + blocks, extra)); |
- leftover->set_is_locked(false); |
- DCHECK(extra == 1 || spans_.find(leftover->start_) == spans_.end()); |
- RegisterSpan(leftover.get()); |
- |
- // No need to coalesce as the previous span of |leftover| was just split |
- // and the next span of |leftover| was not previously coalesced with |
- // |span|. |
- InsertIntoFreeList(std::move(leftover)); |
- |
- serving->length_ = blocks; |
- spans_[serving->start_ + blocks - 1] = serving.get(); |
- } |
- |
- // |serving| is no longer in the free list, remove its length from |
- // |num_free_blocks_|. |
- DCHECK_GE(num_free_blocks_, serving->length_); |
- num_free_blocks_ -= serving->length_; |
- |
- return serving; |
-} |
- |
-void DiscardableSharedMemoryHeap::RegisterSpan(Span* span) { |
- spans_[span->start_] = span; |
- if (span->length_ > 1) |
- spans_[span->start_ + span->length_ - 1] = span; |
-} |
- |
-void DiscardableSharedMemoryHeap::UnregisterSpan(Span* span) { |
- DCHECK(spans_.find(span->start_) != spans_.end()); |
- DCHECK_EQ(spans_[span->start_], span); |
- spans_.erase(span->start_); |
- if (span->length_ > 1) { |
- DCHECK(spans_.find(span->start_ + span->length_ - 1) != spans_.end()); |
- DCHECK_EQ(spans_[span->start_ + span->length_ - 1], span); |
- spans_.erase(span->start_ + span->length_ - 1); |
- } |
-} |
- |
-bool DiscardableSharedMemoryHeap::IsMemoryUsed( |
- const base::DiscardableSharedMemory* shared_memory, |
- size_t size) { |
- size_t offset = |
- reinterpret_cast<size_t>(shared_memory->memory()) / block_size_; |
- size_t length = size / block_size_; |
- DCHECK(spans_.find(offset) != spans_.end()); |
- Span* span = spans_[offset]; |
- DCHECK_LE(span->length_, length); |
- // Memory is used if first span is not in free list or shorter than segment. |
- return !IsInFreeList(span) || span->length_ != length; |
-} |
- |
-bool DiscardableSharedMemoryHeap::IsMemoryResident( |
- const base::DiscardableSharedMemory* shared_memory) { |
- return shared_memory->IsMemoryResident(); |
-} |
- |
-void DiscardableSharedMemoryHeap::ReleaseMemory( |
- const base::DiscardableSharedMemory* shared_memory, |
- size_t size) { |
- size_t offset = |
- reinterpret_cast<size_t>(shared_memory->memory()) / block_size_; |
- size_t end = offset + size / block_size_; |
- while (offset < end) { |
- DCHECK(spans_.find(offset) != spans_.end()); |
- Span* span = spans_[offset]; |
- DCHECK_EQ(span->shared_memory_, shared_memory); |
- span->shared_memory_ = nullptr; |
- UnregisterSpan(span); |
- |
- offset += span->length_; |
- |
- DCHECK_GE(num_blocks_, span->length_); |
- num_blocks_ -= span->length_; |
- |
- // If |span| is in the free list, remove it and update |num_free_blocks_|. |
- if (IsInFreeList(span)) { |
- DCHECK_GE(num_free_blocks_, span->length_); |
- num_free_blocks_ -= span->length_; |
- RemoveFromFreeList(span); |
- } |
- } |
-} |
- |
-void DiscardableSharedMemoryHeap::OnMemoryDump( |
- const base::DiscardableSharedMemory* shared_memory, |
- size_t size, |
- int32_t segment_id, |
- base::trace_event::ProcessMemoryDump* pmd) { |
- size_t allocated_objects_count = 0; |
- size_t allocated_objects_size_in_blocks = 0; |
- size_t locked_objects_size_in_blocks = 0; |
- size_t offset = |
- reinterpret_cast<size_t>(shared_memory->memory()) / block_size_; |
- size_t end = offset + size / block_size_; |
- while (offset < end) { |
- Span* span = spans_[offset]; |
- if (!IsInFreeList(span)) { |
- allocated_objects_size_in_blocks += span->length_; |
- locked_objects_size_in_blocks += span->is_locked_ ? span->length_ : 0; |
- allocated_objects_count++; |
- } |
- offset += span->length_; |
- } |
- size_t allocated_objects_size_in_bytes = |
- allocated_objects_size_in_blocks * block_size_; |
- size_t locked_objects_size_in_bytes = |
- locked_objects_size_in_blocks * block_size_; |
- |
- std::string segment_dump_name = |
- base::StringPrintf("discardable/segment_%d", segment_id); |
- base::trace_event::MemoryAllocatorDump* segment_dump = |
- pmd->CreateAllocatorDump(segment_dump_name); |
- // The size is added here so that telemetry picks up the size. Usually it is |
- // just enough to add it to the global dump. |
- segment_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, |
- base::trace_event::MemoryAllocatorDump::kUnitsBytes, |
- allocated_objects_size_in_bytes); |
- segment_dump->AddScalar("virtual_size", |
- base::trace_event::MemoryAllocatorDump::kUnitsBytes, |
- size); |
- |
- base::trace_event::MemoryAllocatorDump* obj_dump = |
- pmd->CreateAllocatorDump(segment_dump_name + "/allocated_objects"); |
- obj_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameObjectCount, |
- base::trace_event::MemoryAllocatorDump::kUnitsObjects, |
- allocated_objects_count); |
- obj_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, |
- base::trace_event::MemoryAllocatorDump::kUnitsBytes, |
- allocated_objects_size_in_bytes); |
- obj_dump->AddScalar("locked_size", |
- base::trace_event::MemoryAllocatorDump::kUnitsBytes, |
- locked_objects_size_in_bytes); |
- |
- // Emit an ownership edge towards a global allocator dump node. This allows |
- // to avoid double-counting segments when both browser and child process emit |
- // them. In the special case of single-process-mode, this will be the only |
- // dumper active and the single ownership edge will become a no-op in the UI. |
- // The global dump is created as a weak dump so that the segment is removed if |
- // the browser does not dump it (segment was purged). |
- const uint64_t tracing_process_id = |
- base::trace_event::MemoryDumpManager::GetInstance() |
- ->GetTracingProcessId(); |
- base::trace_event::MemoryAllocatorDumpGuid shared_segment_guid = |
- GetSegmentGUIDForTracing(tracing_process_id, segment_id); |
- pmd->CreateWeakSharedGlobalAllocatorDump(shared_segment_guid); |
- |
- // The size is added to the global dump so that it gets propagated to both the |
- // dumps associated. |
- pmd->GetSharedGlobalAllocatorDump(shared_segment_guid) |
- ->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, |
- base::trace_event::MemoryAllocatorDump::kUnitsBytes, |
- allocated_objects_size_in_bytes); |
- |
- // By creating an edge with a higher |importance| (w.r.t. browser-side dumps) |
- // the tracing UI will account the effective size of the segment to the child. |
- const int kImportance = 2; |
- pmd->AddOwnershipEdge(segment_dump->guid(), shared_segment_guid, kImportance); |
-} |
- |
-// static |
-base::trace_event::MemoryAllocatorDumpGuid |
-DiscardableSharedMemoryHeap::GetSegmentGUIDForTracing( |
- uint64_t tracing_process_id, |
- int32_t segment_id) { |
- return base::trace_event::MemoryAllocatorDumpGuid(base::StringPrintf( |
- "discardable-x-process/%" PRIx64 "/%d", tracing_process_id, segment_id)); |
-} |
- |
-base::trace_event::MemoryAllocatorDump* |
-DiscardableSharedMemoryHeap::CreateMemoryAllocatorDump( |
- Span* span, |
- const char* name, |
- base::trace_event::ProcessMemoryDump* pmd) const { |
- if (!span->shared_memory()) { |
- base::trace_event::MemoryAllocatorDump* dump = |
- pmd->CreateAllocatorDump(name); |
- dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, |
- base::trace_event::MemoryAllocatorDump::kUnitsBytes, 0u); |
- return dump; |
- } |
- |
- ScopedVector<ScopedMemorySegment>::const_iterator it = |
- std::find_if(memory_segments_.begin(), memory_segments_.end(), |
- [span](const ScopedMemorySegment* segment) { |
- return segment->ContainsSpan(span); |
- }); |
- DCHECK(it != memory_segments_.end()); |
- return (*it)->CreateMemoryAllocatorDump(span, block_size_, name, pmd); |
-} |
- |
-} // namespace content |