js/src/gc/Memory.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "gc/Memory.h"
     9 #include "js/HeapAPI.h"
    10 #include "vm/Runtime.h"
    12 using namespace js;
    13 using namespace js::gc;
    15 static bool
    16 DecommitEnabled(JSRuntime *rt)
    17 {
    18     return rt->gcSystemPageSize == ArenaSize;
    19 }
    21 #if defined(XP_WIN)
    22 #include "jswin.h"
    23 #include <psapi.h>
    25 void
    26 gc::InitMemorySubsystem(JSRuntime *rt)
    27 {
    28     SYSTEM_INFO sysinfo;
    29     GetSystemInfo(&sysinfo);
    30     rt->gcSystemPageSize = sysinfo.dwPageSize;
    31     rt->gcSystemAllocGranularity = sysinfo.dwAllocationGranularity;
    32 }
    34 void *
    35 gc::MapAlignedPages(JSRuntime *rt, size_t size, size_t alignment)
    36 {
    37     JS_ASSERT(size >= alignment);
    38     JS_ASSERT(size % alignment == 0);
    39     JS_ASSERT(size % rt->gcSystemPageSize == 0);
    40     JS_ASSERT(alignment % rt->gcSystemAllocGranularity == 0);
    42     /* Special case: If we want allocation alignment, no further work is needed. */
    43     if (alignment == rt->gcSystemAllocGranularity) {
    44         return VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    45     }
    47     /*
    48      * Windows requires that there be a 1:1 mapping between VM allocation
    49      * and deallocation operations.  Therefore, take care here to acquire the
    50      * final result via one mapping operation.  This means unmapping any
    51      * preliminary result that is not correctly aligned.
    52      */
    53     void *p = nullptr;
    54     while (!p) {
    55         /*
    56          * Over-allocate in order to map a memory region that is definitely
    57          * large enough, then deallocate and allocate again the correct size,
    58          * within the over-sized mapping.
    59          *
    60          * Since we're going to unmap the whole thing anyway, the first
    61          * mapping doesn't have to commit pages.
    62          */
    63         size_t reserveSize = size + alignment - rt->gcSystemPageSize;
    64         p = VirtualAlloc(nullptr, reserveSize, MEM_RESERVE, PAGE_READWRITE);
    65         if (!p)
    66             return nullptr;
    67         void *chunkStart = (void *)AlignBytes(uintptr_t(p), alignment);
    68         UnmapPages(rt, p, reserveSize);
    69         p = VirtualAlloc(chunkStart, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    71         /* Failure here indicates a race with another thread, so try again. */
    72     }
    74     JS_ASSERT(uintptr_t(p) % alignment == 0);
    75     return p;
    76 }
    78 void
    79 gc::UnmapPages(JSRuntime *rt, void *p, size_t size)
    80 {
    81     JS_ALWAYS_TRUE(VirtualFree(p, 0, MEM_RELEASE));
    82 }
    84 bool
    85 gc::MarkPagesUnused(JSRuntime *rt, void *p, size_t size)
    86 {
    87     if (!DecommitEnabled(rt))
    88         return true;
    90     JS_ASSERT(uintptr_t(p) % rt->gcSystemPageSize == 0);
    91     LPVOID p2 = VirtualAlloc(p, size, MEM_RESET, PAGE_READWRITE);
    92     return p2 == p;
    93 }
    95 bool
    96 gc::MarkPagesInUse(JSRuntime *rt, void *p, size_t size)
    97 {
    98     JS_ASSERT(uintptr_t(p) % rt->gcSystemPageSize == 0);
    99     return true;
   100 }
   102 size_t
   103 gc::GetPageFaultCount()
   104 {
   105     PROCESS_MEMORY_COUNTERS pmc;
   106     if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
   107         return 0;
   108     return pmc.PageFaultCount;
   109 }
   111 void *
   112 gc::AllocateMappedContent(int fd, size_t offset, size_t length, size_t alignment)
   113 {
   114     // TODO: Bug 988813 - Support memory mapped array buffer for Windows platform.
   115     return nullptr;
   116 }
   118 // Deallocate mapped memory for object.
   119 void
   120 gc::DeallocateMappedContent(void *p, size_t length)
   121 {
   122     // TODO: Bug 988813 - Support memory mapped array buffer for Windows platform.
   123 }
   125 #elif defined(SOLARIS)
   127 #include <sys/mman.h>
   128 #include <unistd.h>
   130 #ifndef MAP_NOSYNC
   131 # define MAP_NOSYNC 0
   132 #endif
   134 void
   135 gc::InitMemorySubsystem(JSRuntime *rt)
   136 {
   137     rt->gcSystemPageSize = rt->gcSystemAllocGranularity = size_t(sysconf(_SC_PAGESIZE));
   138 }
   140 void *
   141 gc::MapAlignedPages(JSRuntime *rt, size_t size, size_t alignment)
   142 {
   143     JS_ASSERT(size >= alignment);
   144     JS_ASSERT(size % alignment == 0);
   145     JS_ASSERT(size % rt->gcSystemPageSize == 0);
   146     JS_ASSERT(alignment % rt->gcSystemAllocGranularity == 0);
   148     int prot = PROT_READ | PROT_WRITE;
   149     int flags = MAP_PRIVATE | MAP_ANON | MAP_ALIGN | MAP_NOSYNC;
   151     void *p = mmap((caddr_t)alignment, size, prot, flags, -1, 0);
   152     if (p == MAP_FAILED)
   153         return nullptr;
   154     return p;
   155 }
   157 void
   158 gc::UnmapPages(JSRuntime *rt, void *p, size_t size)
   159 {
   160     JS_ALWAYS_TRUE(0 == munmap((caddr_t)p, size));
   161 }
   163 bool
   164 gc::MarkPagesUnused(JSRuntime *rt, void *p, size_t size)
   165 {
   166     JS_ASSERT(uintptr_t(p) % rt->gcSystemPageSize == 0);
   167     return true;
   168 }
   170 bool
   171 gc::MarkPagesInUse(JSRuntime *rt, void *p, size_t size)
   172 {
   173     JS_ASSERT(uintptr_t(p) % rt->gcSystemPageSize == 0);
   174     return true;
   175 }
   177 size_t
   178 gc::GetPageFaultCount()
   179 {
   180     return 0;
   181 }
   183 void *
   184 gc::AllocateMappedContent(int fd, size_t offset, size_t length, size_t alignment)
   185 {
   186     // Not implemented.
   187     return nullptr;
   188 }
   190 // Deallocate mapped memory for object.
   191 void
   192 gc::DeallocateMappedContent(void *p, size_t length)
   193 {
   194     // Not implemented.
   195 }
   197 #elif defined(XP_UNIX)
   199 #include <algorithm>
   200 #include <sys/mman.h>
   201 #include <sys/resource.h>
   202 #include <sys/stat.h>
   203 #include <sys/types.h>
   204 #include <unistd.h>
   206 void
   207 gc::InitMemorySubsystem(JSRuntime *rt)
   208 {
   209     rt->gcSystemPageSize = rt->gcSystemAllocGranularity = size_t(sysconf(_SC_PAGESIZE));
   210 }
   212 static inline void *
   213 MapMemory(size_t length, int prot, int flags, int fd, off_t offset)
   214 {
   215 #if defined(__ia64__)
   216     /*
   217      * The JS engine assumes that all allocated pointers have their high 17 bits clear,
   218      * which ia64's mmap doesn't support directly. However, we can emulate it by passing
   219      * mmap an "addr" parameter with those bits clear. The mmap will return that address,
   220      * or the nearest available memory above that address, providing a near-guarantee
   221      * that those bits are clear. If they are not, we return nullptr below to indicate
   222      * out-of-memory.
   223      *
   224      * The addr is chosen as 0x0000070000000000, which still allows about 120TB of virtual
   225      * address space.
   226      *
   227      * See Bug 589735 for more information.
   228      */
   229     void *region = mmap((void*)0x0000070000000000, length, prot, flags, fd, offset);
   230     if (region == MAP_FAILED)
   231         return MAP_FAILED;
   232     /*
   233      * If the allocated memory doesn't have its upper 17 bits clear, consider it
   234      * as out of memory.
   235      */
   236     if ((uintptr_t(region) + (length - 1)) & 0xffff800000000000) {
   237         JS_ALWAYS_TRUE(0 == munmap(region, length));
   238         return MAP_FAILED;
   239     }
   240     return region;
   241 #else
   242     return mmap(nullptr, length, prot, flags, fd, offset);
   243 #endif
   244 }
   246 void *
   247 gc::MapAlignedPages(JSRuntime *rt, size_t size, size_t alignment)
   248 {
   249     JS_ASSERT(size >= alignment);
   250     JS_ASSERT(size % alignment == 0);
   251     JS_ASSERT(size % rt->gcSystemPageSize == 0);
   252     JS_ASSERT(alignment % rt->gcSystemAllocGranularity == 0);
   254     int prot = PROT_READ | PROT_WRITE;
   255     int flags = MAP_PRIVATE | MAP_ANON;
   257     /* Special case: If we want page alignment, no further work is needed. */
   258     if (alignment == rt->gcSystemAllocGranularity) {
   259         void *region = MapMemory(size, prot, flags, -1, 0);
   260         if (region == MAP_FAILED)
   261             return nullptr;
   262         return region;
   263     }
   265     /* Overallocate and unmap the region's edges. */
   266     size_t reqSize = Min(size + 2 * alignment, 2 * size);
   267     void *region = MapMemory(reqSize, prot, flags, -1, 0);
   268     if (region == MAP_FAILED)
   269         return nullptr;
   271     uintptr_t regionEnd = uintptr_t(region) + reqSize;
   272     uintptr_t offset = uintptr_t(region) % alignment;
   273     JS_ASSERT(offset < reqSize - size);
   275     void *front = (void *)AlignBytes(uintptr_t(region), alignment);
   276     void *end = (void *)(uintptr_t(front) + size);
   277     if (front != region)
   278         JS_ALWAYS_TRUE(0 == munmap(region, alignment - offset));
   279     if (uintptr_t(end) != regionEnd)
   280         JS_ALWAYS_TRUE(0 == munmap(end, regionEnd - uintptr_t(end)));
   282     JS_ASSERT(uintptr_t(front) % alignment == 0);
   283     return front;
   284 }
   286 void
   287 gc::UnmapPages(JSRuntime *rt, void *p, size_t size)
   288 {
   289     JS_ALWAYS_TRUE(0 == munmap(p, size));
   290 }
   292 bool
   293 gc::MarkPagesUnused(JSRuntime *rt, void *p, size_t size)
   294 {
   295     if (!DecommitEnabled(rt))
   296         return false;
   298     JS_ASSERT(uintptr_t(p) % rt->gcSystemPageSize == 0);
   299     int result = madvise(p, size, MADV_DONTNEED);
   300     return result != -1;
   301 }
   303 bool
   304 gc::MarkPagesInUse(JSRuntime *rt, void *p, size_t size)
   305 {
   306     JS_ASSERT(uintptr_t(p) % rt->gcSystemPageSize == 0);
   307     return true;
   308 }
   310 size_t
   311 gc::GetPageFaultCount()
   312 {
   313     struct rusage usage;
   314     int err = getrusage(RUSAGE_SELF, &usage);
   315     if (err)
   316         return 0;
   317     return usage.ru_majflt;
   318 }
   320 void *
   321 gc::AllocateMappedContent(int fd, size_t offset, size_t length, size_t alignment)
   322 {
   323 #define NEED_PAGE_ALIGNED 0
   324     size_t pa_start; // Page aligned starting
   325     size_t pa_end; // Page aligned ending
   326     size_t pa_size; // Total page aligned size
   327     size_t page_size = sysconf(_SC_PAGESIZE); // Page size
   328     struct stat st;
   329     uint8_t *buf;
   331     // Make sure file exists and do sanity check for offset and size.
   332     if (fstat(fd, &st) < 0 || offset >= (size_t) st.st_size ||
   333         length == 0 || length > (size_t) st.st_size - offset)
   334         return nullptr;
   336     // Check for minimal alignment requirement.
   337 #if NEED_PAGE_ALIGNED
   338     alignment = std::max(alignment, page_size);
   339 #endif
   340     if (offset & (alignment - 1))
   341         return nullptr;
   343     // Page aligned starting of the offset.
   344     pa_start = offset & ~(page_size - 1);
   345     // Calculate page aligned ending by adding one page to the page aligned
   346     // starting of data end position(offset + length - 1).
   347     pa_end = ((offset + length - 1) & ~(page_size - 1)) + page_size;
   348     pa_size = pa_end - pa_start;
   350     // Ask for a continuous memory location.
   351     buf = (uint8_t *) MapMemory(pa_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
   352     if (buf == MAP_FAILED)
   353         return nullptr;
   355     buf = (uint8_t *) mmap(buf, pa_size, PROT_READ | PROT_WRITE,
   356                            MAP_PRIVATE | MAP_FIXED, fd, pa_start);
   357     if (buf == MAP_FAILED)
   358         return nullptr;
   360     // Reset the data before target file, which we don't need to see.
   361     memset(buf, 0, offset - pa_start);
   363     // Reset the data after target file, which we don't need to see.
   364     memset(buf + (offset - pa_start) + length, 0, pa_end - (offset + length));
   366     return buf + (offset - pa_start);
   367 }
   369 void
   370 gc::DeallocateMappedContent(void *p, size_t length)
   371 {
   372     void *pa_start; // Page aligned starting
   373     size_t page_size = sysconf(_SC_PAGESIZE); // Page size
   374     size_t total_size; // Total allocated size
   376     pa_start = (void *)(uintptr_t(p) & ~(page_size - 1));
   377     total_size = ((uintptr_t(p) + length) & ~(page_size - 1)) + page_size - uintptr_t(pa_start);
   378     munmap(pa_start, total_size);
   379 }
   381 #else
   382 #error "Memory mapping functions are not defined for your OS."
   383 #endif

mercurial