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