| /* |
| * Copyright (C) 2013 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #ifndef Heap_h |
| #define Heap_h |
| |
| #include "heap/HeapExport.h" |
| #include "heap/Visitor.h" |
| |
| #include "wtf/Assertions.h" |
| |
| #include <stdint.h> |
| |
| namespace WebCore { |
| |
| // ASAN integration defintions |
| #if COMPILER(CLANG) |
| #define USE_ASAN (__has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)) |
| #else |
| #define USE_ASAN 0 |
| #endif |
| |
| #if USE_ASAN |
| extern "C" { |
| // Marks memory region [addr, addr+size) as unaddressable. |
| // This memory must be previously allocated by the user program. Accessing |
| // addresses in this region from instrumented code is forbidden until |
| // this region is unpoisoned. This function is not guaranteed to poison |
| // the whole region - it may poison only subregion of [addr, addr+size) due |
| // to ASan alignment restrictions. |
| // Method is NOT thread-safe in the sense that no two threads can |
| // (un)poison memory in the same memory region simultaneously. |
| void __asan_poison_memory_region(void const volatile*, size_t); |
| // Marks memory region [addr, addr+size) as addressable. |
| // This memory must be previously allocated by the user program. Accessing |
| // addresses in this region is allowed until this region is poisoned again. |
| // This function may unpoison a superregion of [addr, addr+size) due to |
| // ASan alignment restrictions. |
| // Method is NOT thread-safe in the sense that no two threads can |
| // (un)poison memory in the same memory region simultaneously. |
| void __asan_unpoison_memory_region(void const volatile*, size_t); |
| |
| // User code should use macros instead of functions. |
| #define ASAN_POISON_MEMORY_REGION(addr, size) \ |
| __asan_poison_memory_region((addr), (size)) |
| #define ASAN_UNPOISON_MEMORY_REGION(addr, size) \ |
| __asan_unpoison_memory_region((addr), (size)) |
| #define NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) |
| const size_t asanMagic = 0xabefeed0; |
| const size_t asanDeferMemoryReuseCount = 2; |
| const size_t asanDeferMemoryReuseMask = 0x3; |
| } |
| #else |
| #define ASAN_POISON_MEMORY_REGION(addr, size) \ |
| ((void)(addr), (void)(size)) |
| #define ASAN_UNPOISON_MEMORY_REGION(addr, size) \ |
| ((void)(addr), (void)(size)) |
| #define NO_SANITIZE_ADDRESS |
| #endif |
| |
| const size_t blinkPageSizeLog2 = 17; |
| const size_t blinkPageSize = 1 << blinkPageSizeLog2; |
| const size_t blinkPageOffsetMask = blinkPageSize - 1; |
| const size_t blinkPageBaseMask = ~blinkPageOffsetMask; |
| // Double precision floats are more efficient when 8 byte aligned, so we 8 byte |
| // align all allocations even on 32 bit. |
| const size_t allocationGranularity = 8; |
| const size_t allocationMask = allocationGranularity - 1; |
| const size_t objectStartBitMapSize = (blinkPageSize + ((8 * allocationGranularity) - 1)) / (8 * allocationGranularity); |
| const size_t reservedForObjectBitMap = ((objectStartBitMapSize + allocationMask) & ~allocationMask); |
| const size_t maxHeapObjectSize = 1 << 27; |
| |
| const size_t markBitMask = 1; |
| const size_t freeListMask = 2; |
| const size_t debugBitMask = 4; |
| const size_t sizeMask = ~7; |
| const uint8_t freelistZapValue = 42; |
| const uint8_t finalizedZapValue = 24; |
| |
| typedef uint8_t* Address; |
| |
| class PageMemory; |
| |
| size_t osPageSize(); |
| |
| #ifndef NDEBUG |
| // Sanity check for a page header address: the address of the page |
| // header should be OS page size away from being Blink page size |
| // aligned. |
| inline bool isPageHeaderAddress(Address address) |
| { |
| return !((reinterpret_cast<uintptr_t>(address) & blinkPageOffsetMask) - osPageSize()); |
| } |
| #endif |
| |
| // Common header for heap pages. |
| class BaseHeapPage { |
| public: |
| BaseHeapPage(PageMemory* storage, const GCInfo* gcInfo) |
| : m_storage(storage) |
| , m_gcInfo(gcInfo) |
| { |
| ASSERT(isPageHeaderAddress(reinterpret_cast<Address>(this))); |
| } |
| |
| // Check if the given address could point to an object in this |
| // heap page. If so, find the start of that object and mark it |
| // using the given Visitor. |
| // |
| // Returns true if the object was found and marked, returns false |
| // otherwise. |
| // |
| // This is used during conservative stack scanning to |
| // conservatively mark all objects that could be referenced from |
| // the stack. |
| virtual bool checkAndMarkPointer(Visitor*, Address) = 0; |
| |
| Address address() { return reinterpret_cast<Address>(this); } |
| PageMemory* storage() const { return m_storage; } |
| const GCInfo* gcInfo() { return m_gcInfo; } |
| |
| private: |
| PageMemory* m_storage; |
| // The BaseHeapPage contains three pointers (vtable, m_storage, |
| // and m_gcInfo) and therefore not 8 byte aligned on 32 bit |
| // architectures. Force 8 byte alignment with a union. |
| union { |
| const GCInfo* m_gcInfo; |
| uint64_t m_ensureAligned; |
| }; |
| }; |
| |
| COMPILE_ASSERT(!(sizeof(BaseHeapPage) % allocationGranularity), BaseHeapPage_should_be_8_byte_aligned); |
| |
| // Large allocations are allocated as separate objects and linked in a |
| // list. |
| // |
| // In order to use the same memory allocation routines for everything |
| // allocated in the heap, large objects are considered heap pages |
| // containing only one object. |
| // |
| // The layout of a large heap object is as follows: |
| // |
| // | BaseHeapPage | next pointer | FinalizedHeapObjectHeader or HeapObjectHeader | payload | |
| template<typename Header> |
| class LargeHeapObject : public BaseHeapPage { |
| public: |
| LargeHeapObject(PageMemory* storage, const GCInfo* gcInfo) : BaseHeapPage(storage, gcInfo) { } |
| |
| virtual bool checkAndMarkPointer(Visitor*, Address); |
| |
| void link(LargeHeapObject<Header>** previousNext) |
| { |
| m_next = *previousNext; |
| *previousNext = this; |
| } |
| |
| void unlink(LargeHeapObject<Header>** previousNext) |
| { |
| *previousNext = m_next; |
| } |
| |
| bool contains(Address object) |
| { |
| return (address() <= object) && (object <= (address() + size())); |
| } |
| |
| LargeHeapObject<Header>* next() |
| { |
| return m_next; |
| } |
| |
| size_t size() |
| { |
| return heapObjectHeader()->size() + sizeof(LargeHeapObject<Header>); |
| } |
| |
| Address payload() { return heapObjectHeader()->payload(); } |
| size_t payloadSize() { return heapObjectHeader()->payloadSize(); } |
| |
| Header* heapObjectHeader() |
| { |
| Address headerAddress = address() + sizeof(LargeHeapObject<Header>); |
| return reinterpret_cast<Header*>(headerAddress); |
| } |
| |
| bool isMarked(); |
| void unmark(); |
| // FIXME: Add back when HeapStats have been added. |
| // void getStats(HeapStats&); |
| void mark(Visitor*); |
| void finalize(); |
| |
| private: |
| friend class Heap; |
| // FIXME: Add back when ThreadHeap has been added. |
| // friend class ThreadHeap<Header>; |
| |
| LargeHeapObject<Header>* m_next; |
| }; |
| |
| // The BasicObjectHeader is the minimal object header. It is used when |
| // encountering heap space of size allocationGranularity to mark it as |
| // as freelist entry. |
| class BasicObjectHeader { |
| public: |
| NO_SANITIZE_ADDRESS |
| explicit BasicObjectHeader(size_t encodedSize) |
| : m_size(encodedSize) { } |
| |
| static size_t freeListEncodedSize(size_t size) { return size | freeListMask; } |
| |
| NO_SANITIZE_ADDRESS |
| bool isFree() { return m_size & freeListMask; } |
| |
| NO_SANITIZE_ADDRESS |
| size_t size() const { return m_size & sizeMask; } |
| |
| protected: |
| size_t m_size; |
| }; |
| |
| // Our heap object layout is layered with the HeapObjectHeader closest |
| // to the payload, this can be wrapped in a FinalizedObjectHeader if the |
| // object is on the GeneralHeap and not on a specific TypedHeap. |
| // Finally if the object is a large object (> blinkPageSize/2) then it is |
| // wrapped with a LargeObjectHeader. |
| // |
| // Object memory layout: |
| // [ LargeObjectHeader | ] [ FinalizedObjectHeader | ] HeapObjectHeader | payload |
| // The [ ] notation denotes that the LargeObjectHeader and the FinalizedObjectHeader |
| // are independently optional. |
| class HeapObjectHeader : public BasicObjectHeader { |
| public: |
| NO_SANITIZE_ADDRESS |
| explicit HeapObjectHeader(size_t encodedSize) |
| : BasicObjectHeader(encodedSize) |
| #ifndef NDEBUG |
| , m_magic(magic) |
| #endif |
| { } |
| |
| NO_SANITIZE_ADDRESS |
| HeapObjectHeader(size_t encodedSize, const GCInfo*) |
| : BasicObjectHeader(encodedSize) |
| #ifndef NDEBUG |
| , m_magic(magic) |
| #endif |
| { } |
| |
| inline void checkHeader() const; |
| inline bool isMarked() const; |
| |
| inline void mark(); |
| inline void unmark(); |
| |
| inline Address payload(); |
| inline size_t payloadSize(); |
| inline Address payloadEnd(); |
| |
| inline void setDebugMark(); |
| inline void clearDebugMark(); |
| inline bool hasDebugMark() const; |
| |
| // Zap magic number with a new magic number that means there was once an |
| // object allocated here, but it was freed because nobody marked it during |
| // GC. |
| void zapMagic(); |
| |
| static void finalize(const GCInfo*, Address, size_t); |
| static HeapObjectHeader* fromPayload(const void*); |
| |
| static const intptr_t magic = 0xc0de247; |
| static const intptr_t zappedMagic = 0xC0DEdead; |
| // The zap value for vtables should be < 4K to ensure it cannot be |
| // used for dispatch. |
| static const intptr_t zappedVTable = 0xd0d; |
| |
| private: |
| #ifndef NDEBUG |
| intptr_t m_magic; |
| #endif |
| }; |
| |
| const size_t objectHeaderSize = sizeof(HeapObjectHeader); |
| |
| NO_SANITIZE_ADDRESS |
| void HeapObjectHeader::checkHeader() const |
| { |
| // FIXME: with ThreadLocalHeaps Heap::contains is not thread safe |
| // but introducing locks in this place does not seem like a good |
| // idea. |
| #ifndef NDEBUG |
| ASSERT(m_magic == magic); |
| #endif |
| } |
| |
| Address HeapObjectHeader::payload() |
| { |
| return reinterpret_cast<Address>(this) + objectHeaderSize; |
| } |
| |
| size_t HeapObjectHeader::payloadSize() |
| { |
| return (size() - objectHeaderSize) & ~allocationMask; |
| } |
| |
| Address HeapObjectHeader::payloadEnd() |
| { |
| return reinterpret_cast<Address>(this) + size(); |
| } |
| |
| NO_SANITIZE_ADDRESS |
| void HeapObjectHeader::mark() |
| { |
| checkHeader(); |
| m_size |= markBitMask; |
| } |
| |
| // Each object on the GeneralHeap needs to carry a pointer to its |
| // own GCInfo structure for tracing and potential finalization. |
| class FinalizedHeapObjectHeader : public HeapObjectHeader { |
| public: |
| NO_SANITIZE_ADDRESS |
| FinalizedHeapObjectHeader(size_t encodedSize, const GCInfo* gcInfo) |
| : HeapObjectHeader(encodedSize) |
| , m_gcInfo(gcInfo) |
| { |
| } |
| |
| inline Address payload(); |
| inline size_t payloadSize(); |
| |
| NO_SANITIZE_ADDRESS |
| const GCInfo* gcInfo() { return m_gcInfo; } |
| |
| NO_SANITIZE_ADDRESS |
| const char* typeMarker() { return m_gcInfo->m_typeMarker; } |
| |
| NO_SANITIZE_ADDRESS |
| TraceCallback traceCallback() { return m_gcInfo->m_trace; } |
| |
| void finalize(); |
| |
| NO_SANITIZE_ADDRESS |
| inline bool hasFinalizer() { return m_gcInfo->hasFinalizer(); } |
| |
| static FinalizedHeapObjectHeader* fromPayload(const void*); |
| |
| #if TRACE_GC_USING_CLASSOF |
| const char* classOf() |
| { |
| const char* className = 0; |
| if (m_gcInfo->m_classOf) |
| className = m_gcInfo->m_classOf(payload()); |
| return className ? className : typeMarker(); |
| } |
| #endif |
| |
| private: |
| const GCInfo* m_gcInfo; |
| }; |
| |
| const size_t finalizedHeaderSize = sizeof(FinalizedHeapObjectHeader); |
| |
| Address FinalizedHeapObjectHeader::payload() |
| { |
| return reinterpret_cast<Address>(this) + finalizedHeaderSize; |
| } |
| |
| size_t FinalizedHeapObjectHeader::payloadSize() |
| { |
| return size() - finalizedHeaderSize; |
| } |
| |
| class HEAP_EXPORT Heap { |
| public: |
| static void init(intptr_t* startOfStack); |
| static void shutdown(); |
| }; |
| |
| } |
| |
| #endif // Heap_h |