js/src/gc/Memory.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/gc/Memory.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,383 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "gc/Memory.h"
    1.11 +
    1.12 +#include "js/HeapAPI.h"
    1.13 +#include "vm/Runtime.h"
    1.14 +
    1.15 +using namespace js;
    1.16 +using namespace js::gc;
    1.17 +
    1.18 +static bool
    1.19 +DecommitEnabled(JSRuntime *rt)
    1.20 +{
    1.21 +    return rt->gcSystemPageSize == ArenaSize;
    1.22 +}
    1.23 +
    1.24 +#if defined(XP_WIN)
    1.25 +#include "jswin.h"
    1.26 +#include <psapi.h>
    1.27 +
    1.28 +void
    1.29 +gc::InitMemorySubsystem(JSRuntime *rt)
    1.30 +{
    1.31 +    SYSTEM_INFO sysinfo;
    1.32 +    GetSystemInfo(&sysinfo);
    1.33 +    rt->gcSystemPageSize = sysinfo.dwPageSize;
    1.34 +    rt->gcSystemAllocGranularity = sysinfo.dwAllocationGranularity;
    1.35 +}
    1.36 +
    1.37 +void *
    1.38 +gc::MapAlignedPages(JSRuntime *rt, size_t size, size_t alignment)
    1.39 +{
    1.40 +    JS_ASSERT(size >= alignment);
    1.41 +    JS_ASSERT(size % alignment == 0);
    1.42 +    JS_ASSERT(size % rt->gcSystemPageSize == 0);
    1.43 +    JS_ASSERT(alignment % rt->gcSystemAllocGranularity == 0);
    1.44 +
    1.45 +    /* Special case: If we want allocation alignment, no further work is needed. */
    1.46 +    if (alignment == rt->gcSystemAllocGranularity) {
    1.47 +        return VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    1.48 +    }
    1.49 +
    1.50 +    /*
    1.51 +     * Windows requires that there be a 1:1 mapping between VM allocation
    1.52 +     * and deallocation operations.  Therefore, take care here to acquire the
    1.53 +     * final result via one mapping operation.  This means unmapping any
    1.54 +     * preliminary result that is not correctly aligned.
    1.55 +     */
    1.56 +    void *p = nullptr;
    1.57 +    while (!p) {
    1.58 +        /*
    1.59 +         * Over-allocate in order to map a memory region that is definitely
    1.60 +         * large enough, then deallocate and allocate again the correct size,
    1.61 +         * within the over-sized mapping.
    1.62 +         *
    1.63 +         * Since we're going to unmap the whole thing anyway, the first
    1.64 +         * mapping doesn't have to commit pages.
    1.65 +         */
    1.66 +        size_t reserveSize = size + alignment - rt->gcSystemPageSize;
    1.67 +        p = VirtualAlloc(nullptr, reserveSize, MEM_RESERVE, PAGE_READWRITE);
    1.68 +        if (!p)
    1.69 +            return nullptr;
    1.70 +        void *chunkStart = (void *)AlignBytes(uintptr_t(p), alignment);
    1.71 +        UnmapPages(rt, p, reserveSize);
    1.72 +        p = VirtualAlloc(chunkStart, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    1.73 +
    1.74 +        /* Failure here indicates a race with another thread, so try again. */
    1.75 +    }
    1.76 +
    1.77 +    JS_ASSERT(uintptr_t(p) % alignment == 0);
    1.78 +    return p;
    1.79 +}
    1.80 +
    1.81 +void
    1.82 +gc::UnmapPages(JSRuntime *rt, void *p, size_t size)
    1.83 +{
    1.84 +    JS_ALWAYS_TRUE(VirtualFree(p, 0, MEM_RELEASE));
    1.85 +}
    1.86 +
    1.87 +bool
    1.88 +gc::MarkPagesUnused(JSRuntime *rt, void *p, size_t size)
    1.89 +{
    1.90 +    if (!DecommitEnabled(rt))
    1.91 +        return true;
    1.92 +
    1.93 +    JS_ASSERT(uintptr_t(p) % rt->gcSystemPageSize == 0);
    1.94 +    LPVOID p2 = VirtualAlloc(p, size, MEM_RESET, PAGE_READWRITE);
    1.95 +    return p2 == p;
    1.96 +}
    1.97 +
    1.98 +bool
    1.99 +gc::MarkPagesInUse(JSRuntime *rt, void *p, size_t size)
   1.100 +{
   1.101 +    JS_ASSERT(uintptr_t(p) % rt->gcSystemPageSize == 0);
   1.102 +    return true;
   1.103 +}
   1.104 +
   1.105 +size_t
   1.106 +gc::GetPageFaultCount()
   1.107 +{
   1.108 +    PROCESS_MEMORY_COUNTERS pmc;
   1.109 +    if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
   1.110 +        return 0;
   1.111 +    return pmc.PageFaultCount;
   1.112 +}
   1.113 +
   1.114 +void *
   1.115 +gc::AllocateMappedContent(int fd, size_t offset, size_t length, size_t alignment)
   1.116 +{
   1.117 +    // TODO: Bug 988813 - Support memory mapped array buffer for Windows platform.
   1.118 +    return nullptr;
   1.119 +}
   1.120 +
   1.121 +// Deallocate mapped memory for object.
   1.122 +void
   1.123 +gc::DeallocateMappedContent(void *p, size_t length)
   1.124 +{
   1.125 +    // TODO: Bug 988813 - Support memory mapped array buffer for Windows platform.
   1.126 +}
   1.127 +
   1.128 +#elif defined(SOLARIS)
   1.129 +
   1.130 +#include <sys/mman.h>
   1.131 +#include <unistd.h>
   1.132 +
   1.133 +#ifndef MAP_NOSYNC
   1.134 +# define MAP_NOSYNC 0
   1.135 +#endif
   1.136 +
   1.137 +void
   1.138 +gc::InitMemorySubsystem(JSRuntime *rt)
   1.139 +{
   1.140 +    rt->gcSystemPageSize = rt->gcSystemAllocGranularity = size_t(sysconf(_SC_PAGESIZE));
   1.141 +}
   1.142 +
   1.143 +void *
   1.144 +gc::MapAlignedPages(JSRuntime *rt, size_t size, size_t alignment)
   1.145 +{
   1.146 +    JS_ASSERT(size >= alignment);
   1.147 +    JS_ASSERT(size % alignment == 0);
   1.148 +    JS_ASSERT(size % rt->gcSystemPageSize == 0);
   1.149 +    JS_ASSERT(alignment % rt->gcSystemAllocGranularity == 0);
   1.150 +
   1.151 +    int prot = PROT_READ | PROT_WRITE;
   1.152 +    int flags = MAP_PRIVATE | MAP_ANON | MAP_ALIGN | MAP_NOSYNC;
   1.153 +
   1.154 +    void *p = mmap((caddr_t)alignment, size, prot, flags, -1, 0);
   1.155 +    if (p == MAP_FAILED)
   1.156 +        return nullptr;
   1.157 +    return p;
   1.158 +}
   1.159 +
   1.160 +void
   1.161 +gc::UnmapPages(JSRuntime *rt, void *p, size_t size)
   1.162 +{
   1.163 +    JS_ALWAYS_TRUE(0 == munmap((caddr_t)p, size));
   1.164 +}
   1.165 +
   1.166 +bool
   1.167 +gc::MarkPagesUnused(JSRuntime *rt, void *p, size_t size)
   1.168 +{
   1.169 +    JS_ASSERT(uintptr_t(p) % rt->gcSystemPageSize == 0);
   1.170 +    return true;
   1.171 +}
   1.172 +
   1.173 +bool
   1.174 +gc::MarkPagesInUse(JSRuntime *rt, void *p, size_t size)
   1.175 +{
   1.176 +    JS_ASSERT(uintptr_t(p) % rt->gcSystemPageSize == 0);
   1.177 +    return true;
   1.178 +}
   1.179 +
   1.180 +size_t
   1.181 +gc::GetPageFaultCount()
   1.182 +{
   1.183 +    return 0;
   1.184 +}
   1.185 +
   1.186 +void *
   1.187 +gc::AllocateMappedContent(int fd, size_t offset, size_t length, size_t alignment)
   1.188 +{
   1.189 +    // Not implemented.
   1.190 +    return nullptr;
   1.191 +}
   1.192 +
   1.193 +// Deallocate mapped memory for object.
   1.194 +void
   1.195 +gc::DeallocateMappedContent(void *p, size_t length)
   1.196 +{
   1.197 +    // Not implemented.
   1.198 +}
   1.199 +
   1.200 +#elif defined(XP_UNIX)
   1.201 +
   1.202 +#include <algorithm>
   1.203 +#include <sys/mman.h>
   1.204 +#include <sys/resource.h>
   1.205 +#include <sys/stat.h>
   1.206 +#include <sys/types.h>
   1.207 +#include <unistd.h>
   1.208 +
   1.209 +void
   1.210 +gc::InitMemorySubsystem(JSRuntime *rt)
   1.211 +{
   1.212 +    rt->gcSystemPageSize = rt->gcSystemAllocGranularity = size_t(sysconf(_SC_PAGESIZE));
   1.213 +}
   1.214 +
   1.215 +static inline void *
   1.216 +MapMemory(size_t length, int prot, int flags, int fd, off_t offset)
   1.217 +{
   1.218 +#if defined(__ia64__)
   1.219 +    /*
   1.220 +     * The JS engine assumes that all allocated pointers have their high 17 bits clear,
   1.221 +     * which ia64's mmap doesn't support directly. However, we can emulate it by passing
   1.222 +     * mmap an "addr" parameter with those bits clear. The mmap will return that address,
   1.223 +     * or the nearest available memory above that address, providing a near-guarantee
   1.224 +     * that those bits are clear. If they are not, we return nullptr below to indicate
   1.225 +     * out-of-memory.
   1.226 +     *
   1.227 +     * The addr is chosen as 0x0000070000000000, which still allows about 120TB of virtual
   1.228 +     * address space.
   1.229 +     *
   1.230 +     * See Bug 589735 for more information.
   1.231 +     */
   1.232 +    void *region = mmap((void*)0x0000070000000000, length, prot, flags, fd, offset);
   1.233 +    if (region == MAP_FAILED)
   1.234 +        return MAP_FAILED;
   1.235 +    /*
   1.236 +     * If the allocated memory doesn't have its upper 17 bits clear, consider it
   1.237 +     * as out of memory.
   1.238 +     */
   1.239 +    if ((uintptr_t(region) + (length - 1)) & 0xffff800000000000) {
   1.240 +        JS_ALWAYS_TRUE(0 == munmap(region, length));
   1.241 +        return MAP_FAILED;
   1.242 +    }
   1.243 +    return region;
   1.244 +#else
   1.245 +    return mmap(nullptr, length, prot, flags, fd, offset);
   1.246 +#endif
   1.247 +}
   1.248 +
   1.249 +void *
   1.250 +gc::MapAlignedPages(JSRuntime *rt, size_t size, size_t alignment)
   1.251 +{
   1.252 +    JS_ASSERT(size >= alignment);
   1.253 +    JS_ASSERT(size % alignment == 0);
   1.254 +    JS_ASSERT(size % rt->gcSystemPageSize == 0);
   1.255 +    JS_ASSERT(alignment % rt->gcSystemAllocGranularity == 0);
   1.256 +
   1.257 +    int prot = PROT_READ | PROT_WRITE;
   1.258 +    int flags = MAP_PRIVATE | MAP_ANON;
   1.259 +
   1.260 +    /* Special case: If we want page alignment, no further work is needed. */
   1.261 +    if (alignment == rt->gcSystemAllocGranularity) {
   1.262 +        void *region = MapMemory(size, prot, flags, -1, 0);
   1.263 +        if (region == MAP_FAILED)
   1.264 +            return nullptr;
   1.265 +        return region;
   1.266 +    }
   1.267 +
   1.268 +    /* Overallocate and unmap the region's edges. */
   1.269 +    size_t reqSize = Min(size + 2 * alignment, 2 * size);
   1.270 +    void *region = MapMemory(reqSize, prot, flags, -1, 0);
   1.271 +    if (region == MAP_FAILED)
   1.272 +        return nullptr;
   1.273 +
   1.274 +    uintptr_t regionEnd = uintptr_t(region) + reqSize;
   1.275 +    uintptr_t offset = uintptr_t(region) % alignment;
   1.276 +    JS_ASSERT(offset < reqSize - size);
   1.277 +
   1.278 +    void *front = (void *)AlignBytes(uintptr_t(region), alignment);
   1.279 +    void *end = (void *)(uintptr_t(front) + size);
   1.280 +    if (front != region)
   1.281 +        JS_ALWAYS_TRUE(0 == munmap(region, alignment - offset));
   1.282 +    if (uintptr_t(end) != regionEnd)
   1.283 +        JS_ALWAYS_TRUE(0 == munmap(end, regionEnd - uintptr_t(end)));
   1.284 +
   1.285 +    JS_ASSERT(uintptr_t(front) % alignment == 0);
   1.286 +    return front;
   1.287 +}
   1.288 +
   1.289 +void
   1.290 +gc::UnmapPages(JSRuntime *rt, void *p, size_t size)
   1.291 +{
   1.292 +    JS_ALWAYS_TRUE(0 == munmap(p, size));
   1.293 +}
   1.294 +
   1.295 +bool
   1.296 +gc::MarkPagesUnused(JSRuntime *rt, void *p, size_t size)
   1.297 +{
   1.298 +    if (!DecommitEnabled(rt))
   1.299 +        return false;
   1.300 +
   1.301 +    JS_ASSERT(uintptr_t(p) % rt->gcSystemPageSize == 0);
   1.302 +    int result = madvise(p, size, MADV_DONTNEED);
   1.303 +    return result != -1;
   1.304 +}
   1.305 +
   1.306 +bool
   1.307 +gc::MarkPagesInUse(JSRuntime *rt, void *p, size_t size)
   1.308 +{
   1.309 +    JS_ASSERT(uintptr_t(p) % rt->gcSystemPageSize == 0);
   1.310 +    return true;
   1.311 +}
   1.312 +
   1.313 +size_t
   1.314 +gc::GetPageFaultCount()
   1.315 +{
   1.316 +    struct rusage usage;
   1.317 +    int err = getrusage(RUSAGE_SELF, &usage);
   1.318 +    if (err)
   1.319 +        return 0;
   1.320 +    return usage.ru_majflt;
   1.321 +}
   1.322 +
   1.323 +void *
   1.324 +gc::AllocateMappedContent(int fd, size_t offset, size_t length, size_t alignment)
   1.325 +{
   1.326 +#define NEED_PAGE_ALIGNED 0
   1.327 +    size_t pa_start; // Page aligned starting
   1.328 +    size_t pa_end; // Page aligned ending
   1.329 +    size_t pa_size; // Total page aligned size
   1.330 +    size_t page_size = sysconf(_SC_PAGESIZE); // Page size
   1.331 +    struct stat st;
   1.332 +    uint8_t *buf;
   1.333 +
   1.334 +    // Make sure file exists and do sanity check for offset and size.
   1.335 +    if (fstat(fd, &st) < 0 || offset >= (size_t) st.st_size ||
   1.336 +        length == 0 || length > (size_t) st.st_size - offset)
   1.337 +        return nullptr;
   1.338 +
   1.339 +    // Check for minimal alignment requirement.
   1.340 +#if NEED_PAGE_ALIGNED
   1.341 +    alignment = std::max(alignment, page_size);
   1.342 +#endif
   1.343 +    if (offset & (alignment - 1))
   1.344 +        return nullptr;
   1.345 +
   1.346 +    // Page aligned starting of the offset.
   1.347 +    pa_start = offset & ~(page_size - 1);
   1.348 +    // Calculate page aligned ending by adding one page to the page aligned
   1.349 +    // starting of data end position(offset + length - 1).
   1.350 +    pa_end = ((offset + length - 1) & ~(page_size - 1)) + page_size;
   1.351 +    pa_size = pa_end - pa_start;
   1.352 +
   1.353 +    // Ask for a continuous memory location.
   1.354 +    buf = (uint8_t *) MapMemory(pa_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
   1.355 +    if (buf == MAP_FAILED)
   1.356 +        return nullptr;
   1.357 +
   1.358 +    buf = (uint8_t *) mmap(buf, pa_size, PROT_READ | PROT_WRITE,
   1.359 +                           MAP_PRIVATE | MAP_FIXED, fd, pa_start);
   1.360 +    if (buf == MAP_FAILED)
   1.361 +        return nullptr;
   1.362 +
   1.363 +    // Reset the data before target file, which we don't need to see.
   1.364 +    memset(buf, 0, offset - pa_start);
   1.365 +
   1.366 +    // Reset the data after target file, which we don't need to see.
   1.367 +    memset(buf + (offset - pa_start) + length, 0, pa_end - (offset + length));
   1.368 +
   1.369 +    return buf + (offset - pa_start);
   1.370 +}
   1.371 +
   1.372 +void
   1.373 +gc::DeallocateMappedContent(void *p, size_t length)
   1.374 +{
   1.375 +    void *pa_start; // Page aligned starting
   1.376 +    size_t page_size = sysconf(_SC_PAGESIZE); // Page size
   1.377 +    size_t total_size; // Total allocated size
   1.378 +
   1.379 +    pa_start = (void *)(uintptr_t(p) & ~(page_size - 1));
   1.380 +    total_size = ((uintptr_t(p) + length) & ~(page_size - 1)) + page_size - uintptr_t(pa_start);
   1.381 +    munmap(pa_start, total_size);
   1.382 +}
   1.383 +
   1.384 +#else
   1.385 +#error "Memory mapping functions are not defined for your OS."
   1.386 +#endif

mercurial