js/src/gc/Memory.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:470763e2f7b6
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/. */
6
7 #include "gc/Memory.h"
8
9 #include "js/HeapAPI.h"
10 #include "vm/Runtime.h"
11
12 using namespace js;
13 using namespace js::gc;
14
15 static bool
16 DecommitEnabled(JSRuntime *rt)
17 {
18 return rt->gcSystemPageSize == ArenaSize;
19 }
20
21 #if defined(XP_WIN)
22 #include "jswin.h"
23 #include <psapi.h>
24
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 }
33
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);
41
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 }
46
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);
70
71 /* Failure here indicates a race with another thread, so try again. */
72 }
73
74 JS_ASSERT(uintptr_t(p) % alignment == 0);
75 return p;
76 }
77
78 void
79 gc::UnmapPages(JSRuntime *rt, void *p, size_t size)
80 {
81 JS_ALWAYS_TRUE(VirtualFree(p, 0, MEM_RELEASE));
82 }
83
84 bool
85 gc::MarkPagesUnused(JSRuntime *rt, void *p, size_t size)
86 {
87 if (!DecommitEnabled(rt))
88 return true;
89
90 JS_ASSERT(uintptr_t(p) % rt->gcSystemPageSize == 0);
91 LPVOID p2 = VirtualAlloc(p, size, MEM_RESET, PAGE_READWRITE);
92 return p2 == p;
93 }
94
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 }
101
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 }
110
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 }
117
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 }
124
125 #elif defined(SOLARIS)
126
127 #include <sys/mman.h>
128 #include <unistd.h>
129
130 #ifndef MAP_NOSYNC
131 # define MAP_NOSYNC 0
132 #endif
133
134 void
135 gc::InitMemorySubsystem(JSRuntime *rt)
136 {
137 rt->gcSystemPageSize = rt->gcSystemAllocGranularity = size_t(sysconf(_SC_PAGESIZE));
138 }
139
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);
147
148 int prot = PROT_READ | PROT_WRITE;
149 int flags = MAP_PRIVATE | MAP_ANON | MAP_ALIGN | MAP_NOSYNC;
150
151 void *p = mmap((caddr_t)alignment, size, prot, flags, -1, 0);
152 if (p == MAP_FAILED)
153 return nullptr;
154 return p;
155 }
156
157 void
158 gc::UnmapPages(JSRuntime *rt, void *p, size_t size)
159 {
160 JS_ALWAYS_TRUE(0 == munmap((caddr_t)p, size));
161 }
162
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 }
169
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 }
176
177 size_t
178 gc::GetPageFaultCount()
179 {
180 return 0;
181 }
182
183 void *
184 gc::AllocateMappedContent(int fd, size_t offset, size_t length, size_t alignment)
185 {
186 // Not implemented.
187 return nullptr;
188 }
189
190 // Deallocate mapped memory for object.
191 void
192 gc::DeallocateMappedContent(void *p, size_t length)
193 {
194 // Not implemented.
195 }
196
197 #elif defined(XP_UNIX)
198
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>
205
206 void
207 gc::InitMemorySubsystem(JSRuntime *rt)
208 {
209 rt->gcSystemPageSize = rt->gcSystemAllocGranularity = size_t(sysconf(_SC_PAGESIZE));
210 }
211
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 }
245
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);
253
254 int prot = PROT_READ | PROT_WRITE;
255 int flags = MAP_PRIVATE | MAP_ANON;
256
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 }
264
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;
270
271 uintptr_t regionEnd = uintptr_t(region) + reqSize;
272 uintptr_t offset = uintptr_t(region) % alignment;
273 JS_ASSERT(offset < reqSize - size);
274
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)));
281
282 JS_ASSERT(uintptr_t(front) % alignment == 0);
283 return front;
284 }
285
286 void
287 gc::UnmapPages(JSRuntime *rt, void *p, size_t size)
288 {
289 JS_ALWAYS_TRUE(0 == munmap(p, size));
290 }
291
292 bool
293 gc::MarkPagesUnused(JSRuntime *rt, void *p, size_t size)
294 {
295 if (!DecommitEnabled(rt))
296 return false;
297
298 JS_ASSERT(uintptr_t(p) % rt->gcSystemPageSize == 0);
299 int result = madvise(p, size, MADV_DONTNEED);
300 return result != -1;
301 }
302
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 }
309
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 }
319
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;
330
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;
335
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;
342
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;
349
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;
354
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;
359
360 // Reset the data before target file, which we don't need to see.
361 memset(buf, 0, offset - pa_start);
362
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));
365
366 return buf + (offset - pa_start);
367 }
368
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
375
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 }
380
381 #else
382 #error "Memory mapping functions are not defined for your OS."
383 #endif

mercurial