|
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 |