blob: 70f5f4b65e829c3457ea78012f397641fff33bf4 [file] [log] [blame]
// Copyright 2015 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.
#ifndef PageMemory_h
#define PageMemory_h
#include "platform/heap/Heap.h"
#include "wtf/Assertions.h"
#include "wtf/PageAllocator.h"
#if OS(POSIX)
#include <sys/mman.h>
#include <unistd.h>
#endif
namespace blink {
static size_t roundToOsPageSize(size_t size)
{
return (size + WTF::kSystemPageSize - 1) & ~(WTF::kSystemPageSize - 1);
}
class MemoryRegion {
public:
MemoryRegion(Address base, size_t size)
: m_base(base)
, m_size(size)
{
ASSERT(size > 0);
}
bool contains(Address addr) const
{
return m_base <= addr && addr < (m_base + m_size);
}
bool contains(const MemoryRegion& other) const
{
return contains(other.m_base) && contains(other.m_base + other.m_size - 1);
}
void release()
{
WTF::freePages(m_base, m_size);
}
WARN_UNUSED_RETURN bool commit()
{
WTF::recommitSystemPages(m_base, m_size);
return WTF::setSystemPagesAccessible(m_base, m_size);
}
void decommit()
{
WTF::decommitSystemPages(m_base, m_size);
WTF::setSystemPagesInaccessible(m_base, m_size);
}
Address base() const { return m_base; }
size_t size() const { return m_size; }
private:
Address m_base;
size_t m_size;
};
// TODO(haraken): Like partitionOutOfMemoryWithLotsOfUncommitedPages(),
// we should probably have a way to distinguish physical memory OOM from
// virtual address space OOM.
static NEVER_INLINE void blinkGCOutOfMemory()
{
IMMEDIATE_CRASH();
}
// A PageMemoryRegion represents a chunk of reserved virtual address
// space containing a number of blink heap pages. On Windows, reserved
// virtual address space can only be given back to the system as a
// whole. The PageMemoryRegion allows us to do that by keeping track
// of the number of pages using it in order to be able to release all
// of the virtual address space when there are no more pages using it.
class PageMemoryRegion : public MemoryRegion {
public:
~PageMemoryRegion()
{
Heap::removePageMemoryRegion(this);
release();
}
void pageDeleted(Address page)
{
markPageUnused(page);
if (!--m_numPages)
delete this;
}
void markPageUsed(Address page)
{
ASSERT(!m_inUse[index(page)]);
m_inUse[index(page)] = true;
}
void markPageUnused(Address page)
{
m_inUse[index(page)] = false;
}
static PageMemoryRegion* allocateLargePage(size_t size)
{
return allocate(size, 1);
}
static PageMemoryRegion* allocateNormalPages()
{
return allocate(blinkPageSize * blinkPagesPerRegion, blinkPagesPerRegion);
}
BasePage* pageFromAddress(Address address)
{
ASSERT(contains(address));
if (!m_inUse[index(address)])
return nullptr;
if (m_isLargePage)
return pageFromObject(base());
return pageFromObject(address);
}
private:
PageMemoryRegion(Address base, size_t size, unsigned numPages)
: MemoryRegion(base, size)
, m_isLargePage(numPages == 1)
, m_numPages(numPages)
{
Heap::addPageMemoryRegion(this);
for (size_t i = 0; i < blinkPagesPerRegion; ++i)
m_inUse[i] = false;
}
unsigned index(Address address)
{
ASSERT(contains(address));
if (m_isLargePage)
return 0;
size_t offset = blinkPageAddress(address) - base();
ASSERT(offset % blinkPageSize == 0);
return offset / blinkPageSize;
}
static PageMemoryRegion* allocate(size_t size, unsigned numPages)
{
// Round size up to the allocation granularity.
size = (size + WTF::kPageAllocationGranularityOffsetMask) & WTF::kPageAllocationGranularityBaseMask;
Address base = static_cast<Address>(WTF::allocPages(nullptr, size, blinkPageSize, WTF::PageInaccessible));
if (!base)
blinkGCOutOfMemory();
return new PageMemoryRegion(base, size, numPages);
}
bool m_isLargePage;
bool m_inUse[blinkPagesPerRegion];
unsigned m_numPages;
};
// Representation of the memory used for a Blink heap page.
//
// The representation keeps track of two memory regions:
//
// 1. The virtual memory reserved from the system in order to be able
// to free all the virtual memory reserved. Multiple PageMemory
// instances can share the same reserved memory region and
// therefore notify the reserved memory region on destruction so
// that the system memory can be given back when all PageMemory
// instances for that memory are gone.
//
// 2. The writable memory (a sub-region of the reserved virtual
// memory region) that is used for the actual heap page payload.
//
// Guard pages are created before and after the writable memory.
class PageMemory {
public:
~PageMemory()
{
__lsan_unregister_root_region(m_writable.base(), m_writable.size());
m_reserved->pageDeleted(writableStart());
}
WARN_UNUSED_RETURN bool commit()
{
m_reserved->markPageUsed(writableStart());
return m_writable.commit();
}
void decommit()
{
m_reserved->markPageUnused(writableStart());
m_writable.decommit();
}
void markUnused() { m_reserved->markPageUnused(writableStart()); }
PageMemoryRegion* region() { return m_reserved; }
Address writableStart() { return m_writable.base(); }
static PageMemory* setupPageMemoryInRegion(PageMemoryRegion* region, size_t pageOffset, size_t payloadSize)
{
// Setup the payload one guard page into the page memory.
Address payloadAddress = region->base() + pageOffset + blinkGuardPageSize;
return new PageMemory(region, MemoryRegion(payloadAddress, payloadSize));
}
// Allocate a virtual address space for one blink page with the
// following layout:
//
// [ guard os page | ... payload ... | guard os page ]
// ^---{ aligned to blink page size }
//
// The returned page memory region will be zeroed.
//
static PageMemory* allocate(size_t payloadSize)
{
ASSERT(payloadSize > 0);
// Virtual memory allocation routines operate in OS page sizes.
// Round up the requested size to nearest os page size.
payloadSize = roundToOsPageSize(payloadSize);
// Overallocate by 2 times OS page size to have space for a
// guard page at the beginning and end of blink heap page.
size_t allocationSize = payloadSize + 2 * blinkGuardPageSize;
PageMemoryRegion* pageMemoryRegion = PageMemoryRegion::allocateLargePage(allocationSize);
PageMemory* storage = setupPageMemoryInRegion(pageMemoryRegion, 0, payloadSize);
RELEASE_ASSERT(storage->commit());
return storage;
}
private:
PageMemory(PageMemoryRegion* reserved, const MemoryRegion& writable)
: m_reserved(reserved)
, m_writable(writable)
{
ASSERT(reserved->contains(writable));
// Register the writable area of the memory as part of the LSan root set.
// Only the writable area is mapped and can contain C++ objects. Those
// C++ objects can contain pointers to objects outside of the heap and
// should therefore be part of the LSan root set.
__lsan_register_root_region(m_writable.base(), m_writable.size());
}
PageMemoryRegion* m_reserved;
MemoryRegion m_writable;
};
} // namespace blink
#endif