Commit | Line | Data |
---|---|---|
587fd086 FP |
1 | /* Heap management routines for GNU Emacs on the Microsoft Windows |
2 | API. Copyright (C) 1994, 2001-2014 Free Software Foundation, Inc. | |
95ed0025 | 3 | |
587fd086 | 4 | This file is part of GNU Emacs. |
95ed0025 | 5 | |
587fd086 FP |
6 | GNU Emacs is free software: you can redistribute it and/or modify |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation, either version 3 of the License, or | |
9 | (at your option) any later version. | |
95ed0025 | 10 | |
587fd086 FP |
11 | GNU Emacs is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
95ed0025 | 15 | |
587fd086 FP |
16 | You should have received a copy of the GNU General Public License |
17 | along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |
95ed0025 | 18 | |
9ec0b715 | 19 | /* |
587fd086 | 20 | Geoff Voelker (voelker@cs.washington.edu) 7-29-94 |
95ed0025 RS |
21 | */ |
22 | ||
587fd086 FP |
23 | /* |
24 | Heavily modified by Fabrice Popineau (fabrice.popineau@gmail.com) 28-02-2014 | |
25 | */ | |
26 | ||
27 | /* | |
28 | Memory allocation scheme for w32/w64: | |
29 | ||
30 | - Buffers are mmap'ed using a very simple emulation of mmap/munmap | |
31 | - During the temacs phase: | |
32 | * we use a private heap declared to be stored into the `dumped_data' | |
33 | * unfortunately, this heap cannot be made growable, so the size of | |
34 | blocks it can allocate is limited to (0x80000 - pagesize) | |
35 | * the blocks that are larger than this are allocated from the end | |
36 | of the `dumped_data' array; there are not so many of them. | |
37 | We use a very simple first-fit scheme to reuse those blocks. | |
38 | * we check that the private heap does not cross the area used | |
39 | by the bigger chunks. | |
40 | - During the emacs phase: | |
41 | * we create a private heap for new memory blocks | |
42 | * we make sure that we never free a block that has been dumped. | |
43 | Freeing a dumped block could work in principle, but may prove | |
44 | unreliable if we distribute binaries of emacs.exe: MS does not | |
45 | guarantee that the heap data structures are the same across all | |
46 | versions of their OS, even though the API is available since XP. */ | |
47 | ||
4838e624 | 48 | #include <config.h> |
95ed0025 | 49 | #include <stdio.h> |
6c572f9a | 50 | #include <errno.h> |
95ed0025 | 51 | |
587fd086 | 52 | #include <sys/mman.h> |
501199a3 | 53 | #include "w32common.h" |
489f9371 | 54 | #include "w32heap.h" |
8dfdd41f | 55 | #include "lisp.h" /* for VALMASK */ |
95ed0025 | 56 | |
587fd086 FP |
57 | /* We chose to leave those declarations here. They are used only in |
58 | this file. The RtlCreateHeap is available since XP. It is located | |
59 | in ntdll.dll and is available with the DDK. People often | |
60 | complained that HeapCreate doesn't offer the ability to create a | |
61 | heap at a given place, which we need here, and which RtlCreateHeap | |
62 | provides. We reproduce here the definitions available with the | |
63 | DDK. */ | |
64 | ||
65 | typedef PVOID (WINAPI * RtlCreateHeap_Proc) ( | |
66 | /* _In_ */ ULONG Flags, | |
67 | /* _In_opt_ */ PVOID HeapBase, | |
68 | /* _In_opt_ */ SIZE_T ReserveSize, | |
69 | /* _In_opt_ */ SIZE_T CommitSize, | |
70 | /* _In_opt_ */ PVOID Lock, | |
71 | /* _In_opt_ */ PVOID Parameters | |
72 | ); | |
73 | ||
74 | typedef LONG NTSTATUS; | |
75 | ||
76 | typedef NTSTATUS | |
77 | (NTAPI * PRTL_HEAP_COMMIT_ROUTINE)( | |
78 | IN PVOID Base, | |
79 | IN OUT PVOID *CommitAddress, | |
80 | IN OUT PSIZE_T CommitSize | |
81 | ); | |
82 | ||
83 | typedef struct _RTL_HEAP_PARAMETERS { | |
84 | ULONG Length; | |
85 | SIZE_T SegmentReserve; | |
86 | SIZE_T SegmentCommit; | |
87 | SIZE_T DeCommitFreeBlockThreshold; | |
88 | SIZE_T DeCommitTotalFreeThreshold; | |
89 | SIZE_T MaximumAllocationSize; | |
90 | SIZE_T VirtualMemoryThreshold; | |
91 | SIZE_T InitialCommit; | |
92 | SIZE_T InitialReserve; | |
93 | PRTL_HEAP_COMMIT_ROUTINE CommitRoutine; | |
94 | SIZE_T Reserved[ 2 ]; | |
95 | } RTL_HEAP_PARAMETERS, *PRTL_HEAP_PARAMETERS; | |
96 | ||
97 | /* We reserve space for dumping emacs lisp byte-code inside a static | |
98 | array. By storing it in an array, the generic mechanism in | |
99 | unexecw32.c will be able to dump it without the need to add a | |
100 | special segment to the executable. In order to be able to do this | |
101 | without losing too much space, we need to create a Windows heap at | |
102 | the specific address of the static array. The RtlCreateHeap | |
103 | available inside the NT kernel since XP will do this. It allows to | |
104 | create a non-growable heap at a specific address. So before | |
105 | dumping, we create a non-growable heap at the address of the | |
106 | dumped_data[] array. After dumping, we reuse memory allocated | |
107 | there without being able to free it (but most of it is not meant to | |
108 | be freed anyway), and we use a new private heap for all new | |
109 | allocations. */ | |
110 | ||
89b36202 EZ |
111 | /* FIXME: Most of the space reserved for dumped_data[] is only used by |
112 | the 1st bootstrap-emacs.exe built while bootstrapping. Once the | |
113 | preloaded Lisp files are byte-compiled, the next loadup uses less | |
114 | than half of the size stated below. It would be nice to find a way | |
115 | to build only the first bootstrap-emacs.exe with the large size, | |
116 | and reset that to a lower value afterwards. */ | |
117 | #ifdef _WIN64 | |
118 | # define DUMPED_HEAP_SIZE (18*1024*1024) | |
119 | #else | |
6348c9d7 | 120 | # define DUMPED_HEAP_SIZE (11*1024*1024) |
89b36202 EZ |
121 | #endif |
122 | ||
123 | static unsigned char dumped_data[DUMPED_HEAP_SIZE]; | |
e54c8cd1 | 124 | |
221e0a20 | 125 | /* Info for keeping track of our dynamic heap used after dumping. */ |
95ed0025 RS |
126 | unsigned char *data_region_base = NULL; |
127 | unsigned char *data_region_end = NULL; | |
587fd086 | 128 | static DWORD_PTR committed = 0; |
95ed0025 | 129 | |
587fd086 FP |
130 | /* The maximum block size that can be handled by a non-growable w32 |
131 | heap is limited by the MaxBlockSize value below. | |
132 | ||
133 | This point deserves and explanation. | |
134 | ||
135 | The W32 heap allocator can be used for a growable | |
136 | heap or a non-growable one. | |
137 | ||
138 | A growable heap is not compatible with a fixed base address for the | |
139 | heap. Only a non-growable one is. One drawback of non-growable | |
140 | heaps is that they can hold only objects smaller than a certain | |
141 | size (the one defined below). Most of the largest blocks are GC'ed | |
142 | before dumping. In any case and to be safe, we implement a simple | |
143 | first-fit allocation algorithm starting at the end of the | |
144 | dumped_data[] array like depicted below: | |
95ed0025 | 145 | |
587fd086 FP |
146 | ---------------------------------------------- |
147 | | | | | | |
148 | | Private heap |-> <-| Big chunks | | |
149 | | | | | | |
150 | ---------------------------------------------- | |
151 | ^ ^ ^ | |
152 | dumped_data dumped_data bc_limit | |
153 | + committed | |
154 | ||
155 | */ | |
221e0a20 EZ |
156 | |
157 | /* Info for managing our preload heap, which is essentially a fixed size | |
158 | data area in the executable. */ | |
587fd086 FP |
159 | #define PAGE_SIZE 0x1000 |
160 | #define MaxBlockSize (0x80000 - PAGE_SIZE) | |
161 | ||
162 | #define MAX_BLOCKS 0x40 | |
163 | ||
164 | static struct | |
95ed0025 | 165 | { |
587fd086 FP |
166 | unsigned char *address; |
167 | size_t size; | |
168 | DWORD occupied; | |
169 | } blocks[MAX_BLOCKS]; | |
170 | ||
171 | static DWORD blocks_number = 0; | |
172 | static unsigned char *bc_limit; | |
173 | ||
174 | /* Handle for the private heap: | |
175 | - inside the dumped_data[] array before dump, | |
176 | - outside of it after dump. | |
177 | */ | |
178 | HANDLE heap = NULL; | |
179 | ||
180 | /* We redirect the standard allocation functions. */ | |
181 | malloc_fn the_malloc_fn; | |
182 | realloc_fn the_realloc_fn; | |
183 | free_fn the_free_fn; | |
95ed0025 | 184 | |
587fd086 FP |
185 | /* It doesn't seem to be useful to allocate from a file mapping. |
186 | It would be if the memory was shared. | |
187 | http://stackoverflow.com/questions/307060/what-is-the-purpose-of-allocating-pages-in-the-pagefile-with-createfilemapping */ | |
188 | ||
189 | /* This is the function to commit memory when the heap allocator | |
190 | claims for new memory. Before dumping, we allocate space | |
191 | from the fixed size dumped_data[] array. | |
192 | */ | |
193 | NTSTATUS NTAPI | |
194 | dumped_data_commit (PVOID Base, PVOID *CommitAddress, PSIZE_T CommitSize) | |
011db670 | 195 | { |
587fd086 FP |
196 | /* This is used before dumping. |
197 | ||
198 | The private heap is stored at dumped_data[] address. | |
199 | We commit contiguous areas of the dumped_data array | |
200 | as requests arrive. */ | |
201 | *CommitAddress = data_region_base + committed; | |
202 | committed += *CommitSize; | |
203 | if (((unsigned char *)(*CommitAddress)) + *CommitSize >= bc_limit) | |
709fd16b | 204 | { |
587fd086 FP |
205 | /* Check that the private heap area does not overlap the big |
206 | chunks area. */ | |
207 | fprintf(stderr, | |
208 | "dumped_data_commit: memory exhausted.\nEnlarge dumped_data[]!\n"); | |
209 | exit (-1); | |
709fd16b | 210 | } |
587fd086 | 211 | return 0; |
011db670 | 212 | } |
587fd086 FP |
213 | |
214 | /* Heap creation. */ | |
215 | ||
904e7cf8 | 216 | /* We want to turn on Low Fragmentation Heap for XP and older systems. |
587fd086 FP |
217 | MinGW32 lacks those definitions. */ |
218 | #ifndef _W64 | |
219 | typedef enum _HEAP_INFORMATION_CLASS { | |
220 | HeapCompatibilityInformation | |
221 | } HEAP_INFORMATION_CLASS; | |
222 | ||
223 | typedef WINBASEAPI BOOL (WINAPI * HeapSetInformation_Proc)(HANDLE,HEAP_INFORMATION_CLASS,PVOID,SIZE_T); | |
224 | #endif | |
225 | ||
226 | void | |
227 | init_heap (void) | |
fab624aa | 228 | { |
587fd086 FP |
229 | if (using_dynamic_heap) |
230 | { | |
231 | unsigned long enable_lfh = 2; | |
232 | ||
233 | /* After dumping, use a new private heap. We explicitly enable | |
e67cf8c6 EZ |
234 | the low fragmentation heap (LFH) here, for the sake of pre |
235 | Vista versions. Note: this will harmlessly fail on Vista and | |
236 | later, where the low-fragmentation heap is enabled by | |
587fd086 FP |
237 | default. It will also fail on pre-Vista versions when Emacs |
238 | is run under a debugger; set _NO_DEBUG_HEAP=1 in the | |
239 | environment before starting GDB to get low fragmentation heap | |
240 | on XP and older systems, for the price of losing "certain | |
241 | heap debug options"; for the details see | |
242 | http://msdn.microsoft.com/en-us/library/windows/desktop/aa366705%28v=vs.85%29.aspx. */ | |
243 | data_region_end = data_region_base; | |
244 | ||
245 | /* Create the private heap. */ | |
246 | heap = HeapCreate(0, 0, 0); | |
247 | ||
248 | #ifndef _W64 | |
904e7cf8 | 249 | /* Set the low-fragmentation heap for OS before Vista. */ |
587fd086 FP |
250 | HMODULE hm_kernel32dll = LoadLibrary("kernel32.dll"); |
251 | HeapSetInformation_Proc s_pfn_Heap_Set_Information = (HeapSetInformation_Proc) GetProcAddress(hm_kernel32dll, "HeapSetInformation"); | |
252 | if (s_pfn_Heap_Set_Information != NULL) | |
253 | if (s_pfn_Heap_Set_Information ((PVOID) heap, | |
254 | HeapCompatibilityInformation, | |
255 | &enable_lfh, sizeof(enable_lfh)) == 0) | |
6c572f9a EZ |
256 | DebPrint (("Enabling Low Fragmentation Heap failed: error %ld\n", |
257 | GetLastError ())); | |
62aba0d4 | 258 | #endif |
fab624aa | 259 | |
587fd086 FP |
260 | the_malloc_fn = malloc_after_dump; |
261 | the_realloc_fn = realloc_after_dump; | |
262 | the_free_fn = free_after_dump; | |
263 | } | |
264 | else | |
fab624aa | 265 | { |
587fd086 FP |
266 | /* Find the RtlCreateHeap function. Headers for this function |
267 | are provided with the w32 ddk, but the function is available | |
268 | in ntdll.dll since XP. */ | |
269 | HMODULE hm_ntdll = LoadLibrary ("ntdll.dll"); | |
270 | RtlCreateHeap_Proc s_pfn_Rtl_Create_Heap | |
271 | = (RtlCreateHeap_Proc) GetProcAddress (hm_ntdll, "RtlCreateHeap"); | |
272 | /* Specific parameters for the private heap. */ | |
273 | RTL_HEAP_PARAMETERS params; | |
274 | ZeroMemory(¶ms, sizeof(params)); | |
275 | params.Length = sizeof(RTL_HEAP_PARAMETERS); | |
276 | ||
277 | data_region_base = (unsigned char *)ROUND_UP (dumped_data, 0x1000); | |
278 | data_region_end = bc_limit = dumped_data + DUMPED_HEAP_SIZE; | |
279 | ||
280 | params.InitialCommit = committed = 0x1000; | |
281 | params.InitialReserve = sizeof(dumped_data); | |
282 | /* Use our own routine to commit memory from the dumped_data | |
283 | array. */ | |
284 | params.CommitRoutine = &dumped_data_commit; | |
285 | ||
286 | /* Create the private heap. */ | |
287 | heap = s_pfn_Rtl_Create_Heap (0, data_region_base, 0, 0, NULL, ¶ms); | |
288 | the_malloc_fn = malloc_before_dump; | |
289 | the_realloc_fn = realloc_before_dump; | |
290 | the_free_fn = free_before_dump; | |
fab624aa EZ |
291 | } |
292 | ||
587fd086 FP |
293 | /* Update system version information to match current system. */ |
294 | cache_system_info (); | |
fab624aa | 295 | } |
011db670 | 296 | |
587fd086 FP |
297 | #undef malloc |
298 | #undef realloc | |
587fd086 FP |
299 | #undef free |
300 | ||
301 | /* FREEABLE_P checks if the block can be safely freed. */ | |
302 | #define FREEABLE_P(addr) \ | |
303 | ((unsigned char *)(addr) < dumped_data \ | |
304 | || (unsigned char *)(addr) >= dumped_data + DUMPED_HEAP_SIZE) | |
011db670 | 305 | |
95ed0025 | 306 | void * |
587fd086 | 307 | malloc_after_dump (size_t size) |
95ed0025 | 308 | { |
587fd086 FP |
309 | /* Use the new private heap. */ |
310 | void *p = HeapAlloc (heap, 0, size); | |
ce20e03e | 311 | |
0dd0ad37 | 312 | /* After dump, keep track of the "brk value" for sbrk(0). */ |
6c572f9a | 313 | if (p) |
0dd0ad37 EZ |
314 | { |
315 | unsigned char *new_brk = (unsigned char *)p + size; | |
316 | ||
317 | if (new_brk > data_region_end) | |
318 | data_region_end = new_brk; | |
319 | } | |
6c572f9a EZ |
320 | else |
321 | errno = ENOMEM; | |
587fd086 FP |
322 | return p; |
323 | } | |
ce20e03e | 324 | |
587fd086 FP |
325 | void * |
326 | malloc_before_dump (size_t size) | |
327 | { | |
328 | void *p; | |
329 | ||
330 | /* Before dumping. The private heap can handle only requests for | |
331 | less than MaxBlockSize. */ | |
332 | if (size < MaxBlockSize) | |
333 | { | |
334 | /* Use the private heap if possible. */ | |
335 | p = HeapAlloc (heap, 0, size); | |
6c572f9a EZ |
336 | if (!p) |
337 | errno = ENOMEM; | |
587fd086 FP |
338 | } |
339 | else | |
95ed0025 | 340 | { |
587fd086 FP |
341 | /* Find the first big chunk that can hold the requested size. */ |
342 | int i = 0; | |
343 | ||
344 | for (i = 0; i < blocks_number; i++) | |
345 | { | |
346 | if (blocks[i].occupied == 0 && blocks[i].size >= size) | |
347 | break; | |
348 | } | |
349 | if (i < blocks_number) | |
3bbabc43 | 350 | { |
587fd086 FP |
351 | /* If found, use it. */ |
352 | p = blocks[i].address; | |
353 | blocks[i].occupied = TRUE; | |
354 | } | |
355 | else | |
356 | { | |
357 | /* Allocate a new big chunk from the end of the dumped_data | |
358 | array. */ | |
359 | if (blocks_number >= MAX_BLOCKS) | |
360 | { | |
361 | fprintf(stderr, | |
362 | "malloc_before_dump: no more big chunks available.\nEnlarge MAX_BLOCKS!\n"); | |
363 | exit (-1); | |
364 | } | |
365 | bc_limit -= size; | |
366 | bc_limit = (unsigned char *)ROUND_DOWN (bc_limit, 0x10); | |
367 | p = bc_limit; | |
368 | blocks[blocks_number].address = p; | |
369 | blocks[blocks_number].size = size; | |
370 | blocks[blocks_number].occupied = TRUE; | |
371 | blocks_number++; | |
372 | if (bc_limit < dumped_data + committed) | |
373 | { | |
374 | /* Check that areas do not overlap. */ | |
375 | fprintf(stderr, | |
376 | "malloc_before_dump: memory exhausted.\nEnlarge dumped_data[]!\n"); | |
377 | exit (-1); | |
378 | } | |
379 | } | |
380 | } | |
381 | return p; | |
382 | } | |
383 | ||
384 | /* Re-allocate the previously allocated block in ptr, making the new | |
385 | block SIZE bytes long. */ | |
386 | void * | |
387 | realloc_after_dump (void *ptr, size_t size) | |
388 | { | |
389 | void *p; | |
95ed0025 | 390 | |
587fd086 FP |
391 | /* After dumping. */ |
392 | if (FREEABLE_P (ptr)) | |
393 | { | |
394 | /* Reallocate the block since it lies in the new heap. */ | |
395 | p = HeapReAlloc (heap, 0, ptr, size); | |
6c572f9a EZ |
396 | if (!p) |
397 | errno = ENOMEM; | |
ce20e03e | 398 | } |
587fd086 | 399 | else |
95ed0025 | 400 | { |
587fd086 FP |
401 | /* If the block lies in the dumped data, do not free it. Only |
402 | allocate a new one. */ | |
403 | p = HeapAlloc (heap, 0, size); | |
6c572f9a EZ |
404 | if (p) |
405 | CopyMemory (p, ptr, size); | |
406 | else | |
407 | errno = ENOMEM; | |
95ed0025 | 408 | } |
0dd0ad37 | 409 | /* After dump, keep track of the "brk value" for sbrk(0). */ |
6c572f9a | 410 | if (p) |
0dd0ad37 EZ |
411 | { |
412 | unsigned char *new_brk = (unsigned char *)p + size; | |
413 | ||
414 | if (new_brk > data_region_end) | |
415 | data_region_end = new_brk; | |
416 | } | |
587fd086 FP |
417 | return p; |
418 | } | |
ce20e03e | 419 | |
587fd086 FP |
420 | void * |
421 | realloc_before_dump (void *ptr, size_t size) | |
422 | { | |
423 | void *p; | |
424 | ||
425 | /* Before dumping. */ | |
426 | if (dumped_data < (unsigned char *)ptr | |
427 | && (unsigned char *)ptr < bc_limit && size <= MaxBlockSize) | |
6c572f9a EZ |
428 | { |
429 | p = HeapReAlloc (heap, 0, ptr, size); | |
430 | if (!p) | |
431 | errno = ENOMEM; | |
432 | } | |
587fd086 FP |
433 | else |
434 | { | |
435 | /* In this case, either the new block is too large for the heap, | |
436 | or the old block was already too large. In both cases, | |
437 | malloc_before_dump() and free_before_dump() will take care of | |
438 | reallocation. */ | |
439 | p = malloc_before_dump (size); | |
be04283a EZ |
440 | /* If SIZE is below MaxBlockSize, malloc_before_dump will try to |
441 | allocate it in the fixed heap. If that fails, we could have | |
442 | kept the block in its original place, above bc_limit, instead | |
443 | of failing the call as below. But this doesn't seem to be | |
444 | worth the added complexity, as loadup allocates only a very | |
445 | small number of large blocks, and never reallocates them. */ | |
6c572f9a EZ |
446 | if (p) |
447 | { | |
448 | CopyMemory (p, ptr, size); | |
449 | free_before_dump (ptr); | |
450 | } | |
587fd086 FP |
451 | } |
452 | return p; | |
95ed0025 RS |
453 | } |
454 | ||
587fd086 | 455 | /* Free a block allocated by `malloc', `realloc' or `calloc'. */ |
95ed0025 | 456 | void |
587fd086 | 457 | free_after_dump (void *ptr) |
95ed0025 | 458 | { |
587fd086 FP |
459 | /* After dumping. */ |
460 | if (FREEABLE_P (ptr)) | |
461 | { | |
462 | /* Free the block if it is in the new private heap. */ | |
463 | HeapFree (heap, 0, ptr); | |
464 | } | |
465 | } | |
30d2b1c2 | 466 | |
587fd086 FP |
467 | void |
468 | free_before_dump (void *ptr) | |
469 | { | |
470 | /* Before dumping. */ | |
471 | if (dumped_data < (unsigned char *)ptr | |
472 | && (unsigned char *)ptr < bc_limit) | |
30d2b1c2 | 473 | { |
587fd086 FP |
474 | /* Free the block if it is allocated in the private heap. */ |
475 | HeapFree (heap, 0, ptr); | |
476 | } | |
477 | else | |
478 | { | |
479 | /* Look for the big chunk. */ | |
480 | int i; | |
30d2b1c2 | 481 | |
a7f999dc | 482 | for (i = 0; i < blocks_number; i++) |
30d2b1c2 | 483 | { |
587fd086 FP |
484 | if (blocks[i].address == ptr) |
485 | { | |
486 | /* Reset block occupation if found. */ | |
487 | blocks[i].occupied = 0; | |
488 | break; | |
489 | } | |
490 | /* What if the block is not found? We should trigger an | |
491 | error here. */ | |
492 | eassert (i < blocks_number); | |
30d2b1c2 | 493 | } |
30d2b1c2 | 494 | } |
587fd086 FP |
495 | } |
496 | ||
d2ff520a EZ |
497 | #ifdef ENABLE_CHECKING |
498 | void | |
499 | report_temacs_memory_usage (void) | |
500 | { | |
350aea69 EZ |
501 | DWORD blocks_used = 0; |
502 | size_t large_mem_used = 0; | |
a7f999dc EZ |
503 | int i; |
504 | ||
505 | for (i = 0; i < blocks_number; i++) | |
506 | if (blocks[i].occupied) | |
507 | { | |
508 | blocks_used++; | |
509 | large_mem_used += blocks[i].size; | |
510 | } | |
511 | ||
d2ff520a EZ |
512 | /* Emulate 'message', which writes to stderr in non-interactive |
513 | sessions. */ | |
514 | fprintf (stderr, | |
a7f999dc EZ |
515 | "Dump memory usage: Heap: %" PRIu64 " Large blocks(%lu/%lu): %" PRIu64 "/%" PRIu64 "\n", |
516 | (unsigned long long)committed, blocks_used, blocks_number, | |
517 | (unsigned long long)large_mem_used, | |
d2ff520a EZ |
518 | (unsigned long long)(dumped_data + DUMPED_HEAP_SIZE - bc_limit)); |
519 | } | |
520 | #endif | |
521 | ||
587fd086 FP |
522 | /* Emulate getpagesize. */ |
523 | int | |
524 | getpagesize (void) | |
525 | { | |
526 | return sysinfo_cache.dwPageSize; | |
527 | } | |
528 | ||
529 | void * | |
530 | sbrk (ptrdiff_t increment) | |
531 | { | |
0dd0ad37 EZ |
532 | /* data_region_end is the address beyond the last allocated byte. |
533 | The sbrk() function is not emulated at all, except for a 0 value | |
534 | of its parameter. This is needed by the Emacs Lisp function | |
535 | `memory-limit'. */ | |
536 | eassert (increment == 0); | |
587fd086 FP |
537 | return data_region_end; |
538 | } | |
539 | ||
540 | #define MAX_BUFFER_SIZE (512 * 1024 * 1024) | |
541 | ||
542 | /* MMAP allocation for buffers. */ | |
543 | void * | |
544 | mmap_alloc (void **var, size_t nbytes) | |
545 | { | |
546 | void *p = NULL; | |
547 | ||
548 | /* We implement amortized allocation. We start by reserving twice | |
549 | the size requested and commit only the size requested. Then | |
550 | realloc could proceed and use the reserved pages, reallocating | |
551 | only if needed. Buffer shrink would happen only so that we stay | |
552 | in the 2x range. This is a big win when visiting compressed | |
553 | files, where the final size of the buffer is not known in | |
554 | advance, and the buffer is enlarged several times as the data is | |
555 | decompressed on the fly. */ | |
556 | if (nbytes < MAX_BUFFER_SIZE) | |
557 | p = VirtualAlloc (NULL, (nbytes * 2), MEM_RESERVE, PAGE_READWRITE); | |
558 | ||
559 | /* If it fails, or if the request is above 512MB, try with the | |
560 | requested size. */ | |
561 | if (p == NULL) | |
562 | p = VirtualAlloc (NULL, nbytes, MEM_RESERVE, PAGE_READWRITE); | |
563 | ||
564 | if (p != NULL) | |
30d2b1c2 | 565 | { |
587fd086 FP |
566 | /* Now, commit pages for NBYTES. */ |
567 | *var = VirtualAlloc (p, nbytes, MEM_COMMIT, PAGE_READWRITE); | |
30d2b1c2 | 568 | } |
801f68b9 | 569 | |
6c572f9a EZ |
570 | if (!p) |
571 | { | |
572 | if (GetLastError () == ERROR_NOT_ENOUGH_MEMORY) | |
573 | errno = ENOMEM; | |
574 | else | |
575 | { | |
576 | DebPrint (("mmap_alloc: error %ld\n", GetLastError ())); | |
577 | errno = EINVAL; | |
578 | } | |
579 | } | |
587fd086 FP |
580 | |
581 | return *var = p; | |
95ed0025 RS |
582 | } |
583 | ||
95ed0025 | 584 | void |
587fd086 | 585 | mmap_free (void **var) |
95ed0025 | 586 | { |
587fd086 FP |
587 | if (*var) |
588 | { | |
589 | if (VirtualFree (*var, 0, MEM_RELEASE) == 0) | |
6c572f9a | 590 | DebPrint (("mmap_free: error %ld\n", GetLastError ())); |
587fd086 FP |
591 | *var = NULL; |
592 | } | |
593 | } | |
ce20e03e | 594 | |
587fd086 FP |
595 | void * |
596 | mmap_realloc (void **var, size_t nbytes) | |
597 | { | |
598 | MEMORY_BASIC_INFORMATION memInfo, m2; | |
599 | ||
600 | if (*var == NULL) | |
601 | return mmap_alloc (var, nbytes); | |
602 | ||
603 | /* This case happens in init_buffer(). */ | |
604 | if (nbytes == 0) | |
605 | { | |
606 | mmap_free (var); | |
607 | return mmap_alloc (var, nbytes); | |
608 | } | |
609 | ||
610 | if (VirtualQuery (*var, &memInfo, sizeof (memInfo)) == 0) | |
6c572f9a | 611 | DebPrint (("mmap_realloc: VirtualQuery error = %ld\n", GetLastError ())); |
587fd086 FP |
612 | |
613 | /* We need to enlarge the block. */ | |
614 | if (memInfo.RegionSize < nbytes) | |
615 | { | |
616 | if (VirtualQuery (*var + memInfo.RegionSize, &m2, sizeof(m2)) == 0) | |
6c572f9a EZ |
617 | DebPrint (("mmap_realloc: VirtualQuery error = %ld\n", |
618 | GetLastError ())); | |
587fd086 FP |
619 | /* If there is enough room in the current reserved area, then |
620 | commit more pages as needed. */ | |
621 | if (m2.State == MEM_RESERVE | |
622 | && nbytes <= memInfo.RegionSize + m2.RegionSize) | |
623 | { | |
624 | void *p; | |
625 | ||
626 | p = VirtualAlloc (*var + memInfo.RegionSize, | |
627 | nbytes - memInfo.RegionSize, | |
628 | MEM_COMMIT, PAGE_READWRITE); | |
629 | if (!p /* && GetLastError() != ERROR_NOT_ENOUGH_MEMORY */) | |
6c572f9a EZ |
630 | { |
631 | DebPrint (("realloc enlarge: VirtualAlloc error %ld\n", | |
632 | GetLastError ())); | |
633 | errno = ENOMEM; | |
634 | } | |
587fd086 FP |
635 | return *var; |
636 | } | |
637 | else | |
638 | { | |
639 | /* Else we must actually enlarge the block by allocating a | |
640 | new one and copying previous contents from the old to the | |
641 | new one. */ | |
642 | void *old_ptr = *var; | |
643 | ||
644 | if (mmap_alloc (var, nbytes)) | |
645 | { | |
646 | CopyMemory (*var, old_ptr, memInfo.RegionSize); | |
647 | mmap_free (&old_ptr); | |
648 | return *var; | |
649 | } | |
650 | else | |
651 | { | |
652 | /* We failed to enlarge the buffer. */ | |
653 | *var = old_ptr; | |
654 | return NULL; | |
655 | } | |
656 | } | |
657 | } | |
658 | ||
659 | /* If we are shrinking by more than one page... */ | |
660 | if (memInfo.RegionSize > nbytes + getpagesize()) | |
661 | { | |
662 | /* If we are shrinking a lot... */ | |
663 | if ((memInfo.RegionSize / 2) > nbytes) | |
664 | { | |
665 | /* Let's give some memory back to the system and release | |
666 | some pages. */ | |
667 | void *old_ptr = *var; | |
668 | ||
669 | if (mmap_alloc (var, nbytes)) | |
670 | { | |
671 | CopyMemory (*var, old_ptr, nbytes); | |
672 | mmap_free (&old_ptr); | |
673 | return *var; | |
674 | } | |
675 | else | |
676 | { | |
677 | /* In case we fail to shrink, try to go on with the old block. | |
678 | But that means there is a lot of memory pressure. | |
679 | We could also decommit pages. */ | |
680 | *var = old_ptr; | |
681 | return *var; | |
682 | } | |
683 | } | |
684 | ||
685 | /* We still can decommit pages. */ | |
686 | if (VirtualFree (*var + nbytes + get_page_size(), | |
687 | memInfo.RegionSize - nbytes - get_page_size(), | |
688 | MEM_DECOMMIT) == 0) | |
6c572f9a | 689 | DebPrint (("mmap_realloc: VirtualFree error %ld\n", GetLastError ())); |
587fd086 FP |
690 | return *var; |
691 | } | |
ce20e03e | 692 | |
587fd086 FP |
693 | /* Not enlarging, not shrinking by more than one page. */ |
694 | return *var; | |
95ed0025 | 695 | } |