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; | |
4f1e5507 | 203 | /* Check that the private heap area does not overlap the big chunks area. */ |
587fd086 | 204 | if (((unsigned char *)(*CommitAddress)) + *CommitSize >= bc_limit) |
709fd16b | 205 | { |
4f1e5507 EZ |
206 | fprintf (stderr, |
207 | "dumped_data_commit: memory exhausted.\nEnlarge dumped_data[]!\n"); | |
587fd086 | 208 | exit (-1); |
709fd16b | 209 | } |
587fd086 | 210 | return 0; |
011db670 | 211 | } |
587fd086 FP |
212 | |
213 | /* Heap creation. */ | |
214 | ||
904e7cf8 | 215 | /* We want to turn on Low Fragmentation Heap for XP and older systems. |
587fd086 FP |
216 | MinGW32 lacks those definitions. */ |
217 | #ifndef _W64 | |
218 | typedef enum _HEAP_INFORMATION_CLASS { | |
219 | HeapCompatibilityInformation | |
220 | } HEAP_INFORMATION_CLASS; | |
221 | ||
222 | typedef WINBASEAPI BOOL (WINAPI * HeapSetInformation_Proc)(HANDLE,HEAP_INFORMATION_CLASS,PVOID,SIZE_T); | |
223 | #endif | |
224 | ||
225 | void | |
226 | init_heap (void) | |
fab624aa | 227 | { |
587fd086 FP |
228 | if (using_dynamic_heap) |
229 | { | |
230 | unsigned long enable_lfh = 2; | |
231 | ||
232 | /* After dumping, use a new private heap. We explicitly enable | |
e67cf8c6 EZ |
233 | the low fragmentation heap (LFH) here, for the sake of pre |
234 | Vista versions. Note: this will harmlessly fail on Vista and | |
235 | later, where the low-fragmentation heap is enabled by | |
587fd086 FP |
236 | default. It will also fail on pre-Vista versions when Emacs |
237 | is run under a debugger; set _NO_DEBUG_HEAP=1 in the | |
238 | environment before starting GDB to get low fragmentation heap | |
239 | on XP and older systems, for the price of losing "certain | |
240 | heap debug options"; for the details see | |
241 | http://msdn.microsoft.com/en-us/library/windows/desktop/aa366705%28v=vs.85%29.aspx. */ | |
242 | data_region_end = data_region_base; | |
243 | ||
244 | /* Create the private heap. */ | |
4f1e5507 | 245 | heap = HeapCreate (0, 0, 0); |
587fd086 FP |
246 | |
247 | #ifndef _W64 | |
904e7cf8 | 248 | /* Set the low-fragmentation heap for OS before Vista. */ |
4f1e5507 EZ |
249 | HMODULE hm_kernel32dll = LoadLibrary ("kernel32.dll"); |
250 | HeapSetInformation_Proc s_pfn_Heap_Set_Information = (HeapSetInformation_Proc) GetProcAddress (hm_kernel32dll, "HeapSetInformation"); | |
587fd086 | 251 | if (s_pfn_Heap_Set_Information != NULL) |
4f1e5507 EZ |
252 | { |
253 | if (s_pfn_Heap_Set_Information ((PVOID) heap, | |
254 | HeapCompatibilityInformation, | |
255 | &enable_lfh, sizeof(enable_lfh)) == 0) | |
256 | DebPrint (("Enabling Low Fragmentation Heap failed: error %ld\n", | |
257 | GetLastError ())); | |
258 | } | |
62aba0d4 | 259 | #endif |
fab624aa | 260 | |
587fd086 FP |
261 | the_malloc_fn = malloc_after_dump; |
262 | the_realloc_fn = realloc_after_dump; | |
263 | the_free_fn = free_after_dump; | |
264 | } | |
265 | else | |
fab624aa | 266 | { |
587fd086 FP |
267 | /* Find the RtlCreateHeap function. Headers for this function |
268 | are provided with the w32 ddk, but the function is available | |
269 | in ntdll.dll since XP. */ | |
270 | HMODULE hm_ntdll = LoadLibrary ("ntdll.dll"); | |
271 | RtlCreateHeap_Proc s_pfn_Rtl_Create_Heap | |
272 | = (RtlCreateHeap_Proc) GetProcAddress (hm_ntdll, "RtlCreateHeap"); | |
273 | /* Specific parameters for the private heap. */ | |
274 | RTL_HEAP_PARAMETERS params; | |
4f1e5507 | 275 | ZeroMemory (¶ms, sizeof(params)); |
587fd086 FP |
276 | params.Length = sizeof(RTL_HEAP_PARAMETERS); |
277 | ||
278 | data_region_base = (unsigned char *)ROUND_UP (dumped_data, 0x1000); | |
279 | data_region_end = bc_limit = dumped_data + DUMPED_HEAP_SIZE; | |
280 | ||
281 | params.InitialCommit = committed = 0x1000; | |
282 | params.InitialReserve = sizeof(dumped_data); | |
283 | /* Use our own routine to commit memory from the dumped_data | |
284 | array. */ | |
285 | params.CommitRoutine = &dumped_data_commit; | |
286 | ||
287 | /* Create the private heap. */ | |
4f1e5507 EZ |
288 | if (s_pfn_Rtl_Create_Heap == NULL) |
289 | { | |
290 | fprintf (stderr, "Cannot build Emacs without RtlCreateHeap being available; exiting.\n"); | |
291 | exit (-1); | |
292 | } | |
587fd086 FP |
293 | heap = s_pfn_Rtl_Create_Heap (0, data_region_base, 0, 0, NULL, ¶ms); |
294 | the_malloc_fn = malloc_before_dump; | |
295 | the_realloc_fn = realloc_before_dump; | |
296 | the_free_fn = free_before_dump; | |
fab624aa EZ |
297 | } |
298 | ||
587fd086 FP |
299 | /* Update system version information to match current system. */ |
300 | cache_system_info (); | |
fab624aa | 301 | } |
011db670 | 302 | |
587fd086 FP |
303 | #undef malloc |
304 | #undef realloc | |
587fd086 FP |
305 | #undef free |
306 | ||
307 | /* FREEABLE_P checks if the block can be safely freed. */ | |
308 | #define FREEABLE_P(addr) \ | |
309 | ((unsigned char *)(addr) < dumped_data \ | |
310 | || (unsigned char *)(addr) >= dumped_data + DUMPED_HEAP_SIZE) | |
011db670 | 311 | |
95ed0025 | 312 | void * |
587fd086 | 313 | malloc_after_dump (size_t size) |
95ed0025 | 314 | { |
587fd086 FP |
315 | /* Use the new private heap. */ |
316 | void *p = HeapAlloc (heap, 0, size); | |
ce20e03e | 317 | |
0dd0ad37 | 318 | /* After dump, keep track of the "brk value" for sbrk(0). */ |
6c572f9a | 319 | if (p) |
0dd0ad37 EZ |
320 | { |
321 | unsigned char *new_brk = (unsigned char *)p + size; | |
322 | ||
323 | if (new_brk > data_region_end) | |
324 | data_region_end = new_brk; | |
325 | } | |
6c572f9a EZ |
326 | else |
327 | errno = ENOMEM; | |
587fd086 FP |
328 | return p; |
329 | } | |
ce20e03e | 330 | |
587fd086 FP |
331 | void * |
332 | malloc_before_dump (size_t size) | |
333 | { | |
334 | void *p; | |
335 | ||
336 | /* Before dumping. The private heap can handle only requests for | |
337 | less than MaxBlockSize. */ | |
338 | if (size < MaxBlockSize) | |
339 | { | |
340 | /* Use the private heap if possible. */ | |
341 | p = HeapAlloc (heap, 0, size); | |
6c572f9a EZ |
342 | if (!p) |
343 | errno = ENOMEM; | |
587fd086 FP |
344 | } |
345 | else | |
95ed0025 | 346 | { |
587fd086 FP |
347 | /* Find the first big chunk that can hold the requested size. */ |
348 | int i = 0; | |
349 | ||
350 | for (i = 0; i < blocks_number; i++) | |
351 | { | |
352 | if (blocks[i].occupied == 0 && blocks[i].size >= size) | |
353 | break; | |
354 | } | |
355 | if (i < blocks_number) | |
3bbabc43 | 356 | { |
587fd086 FP |
357 | /* If found, use it. */ |
358 | p = blocks[i].address; | |
359 | blocks[i].occupied = TRUE; | |
360 | } | |
361 | else | |
362 | { | |
363 | /* Allocate a new big chunk from the end of the dumped_data | |
364 | array. */ | |
365 | if (blocks_number >= MAX_BLOCKS) | |
366 | { | |
4f1e5507 EZ |
367 | fprintf (stderr, |
368 | "malloc_before_dump: no more big chunks available.\nEnlarge MAX_BLOCKS!\n"); | |
587fd086 FP |
369 | exit (-1); |
370 | } | |
371 | bc_limit -= size; | |
372 | bc_limit = (unsigned char *)ROUND_DOWN (bc_limit, 0x10); | |
373 | p = bc_limit; | |
374 | blocks[blocks_number].address = p; | |
375 | blocks[blocks_number].size = size; | |
376 | blocks[blocks_number].occupied = TRUE; | |
377 | blocks_number++; | |
4f1e5507 | 378 | /* Check that areas do not overlap. */ |
587fd086 FP |
379 | if (bc_limit < dumped_data + committed) |
380 | { | |
4f1e5507 EZ |
381 | fprintf (stderr, |
382 | "malloc_before_dump: memory exhausted.\nEnlarge dumped_data[]!\n"); | |
587fd086 FP |
383 | exit (-1); |
384 | } | |
385 | } | |
386 | } | |
387 | return p; | |
388 | } | |
389 | ||
390 | /* Re-allocate the previously allocated block in ptr, making the new | |
391 | block SIZE bytes long. */ | |
392 | void * | |
393 | realloc_after_dump (void *ptr, size_t size) | |
394 | { | |
395 | void *p; | |
95ed0025 | 396 | |
587fd086 FP |
397 | /* After dumping. */ |
398 | if (FREEABLE_P (ptr)) | |
399 | { | |
400 | /* Reallocate the block since it lies in the new heap. */ | |
401 | p = HeapReAlloc (heap, 0, ptr, size); | |
6c572f9a EZ |
402 | if (!p) |
403 | errno = ENOMEM; | |
ce20e03e | 404 | } |
587fd086 | 405 | else |
95ed0025 | 406 | { |
587fd086 FP |
407 | /* If the block lies in the dumped data, do not free it. Only |
408 | allocate a new one. */ | |
409 | p = HeapAlloc (heap, 0, size); | |
6c572f9a EZ |
410 | if (p) |
411 | CopyMemory (p, ptr, size); | |
412 | else | |
413 | errno = ENOMEM; | |
95ed0025 | 414 | } |
0dd0ad37 | 415 | /* After dump, keep track of the "brk value" for sbrk(0). */ |
6c572f9a | 416 | if (p) |
0dd0ad37 EZ |
417 | { |
418 | unsigned char *new_brk = (unsigned char *)p + size; | |
419 | ||
420 | if (new_brk > data_region_end) | |
421 | data_region_end = new_brk; | |
422 | } | |
587fd086 FP |
423 | return p; |
424 | } | |
ce20e03e | 425 | |
587fd086 FP |
426 | void * |
427 | realloc_before_dump (void *ptr, size_t size) | |
428 | { | |
429 | void *p; | |
430 | ||
431 | /* Before dumping. */ | |
432 | if (dumped_data < (unsigned char *)ptr | |
433 | && (unsigned char *)ptr < bc_limit && size <= MaxBlockSize) | |
6c572f9a EZ |
434 | { |
435 | p = HeapReAlloc (heap, 0, ptr, size); | |
436 | if (!p) | |
437 | errno = ENOMEM; | |
438 | } | |
587fd086 FP |
439 | else |
440 | { | |
441 | /* In this case, either the new block is too large for the heap, | |
442 | or the old block was already too large. In both cases, | |
443 | malloc_before_dump() and free_before_dump() will take care of | |
444 | reallocation. */ | |
445 | p = malloc_before_dump (size); | |
be04283a EZ |
446 | /* If SIZE is below MaxBlockSize, malloc_before_dump will try to |
447 | allocate it in the fixed heap. If that fails, we could have | |
448 | kept the block in its original place, above bc_limit, instead | |
449 | of failing the call as below. But this doesn't seem to be | |
450 | worth the added complexity, as loadup allocates only a very | |
451 | small number of large blocks, and never reallocates them. */ | |
6c572f9a EZ |
452 | if (p) |
453 | { | |
454 | CopyMemory (p, ptr, size); | |
455 | free_before_dump (ptr); | |
456 | } | |
587fd086 FP |
457 | } |
458 | return p; | |
95ed0025 RS |
459 | } |
460 | ||
587fd086 | 461 | /* Free a block allocated by `malloc', `realloc' or `calloc'. */ |
95ed0025 | 462 | void |
587fd086 | 463 | free_after_dump (void *ptr) |
95ed0025 | 464 | { |
587fd086 FP |
465 | /* After dumping. */ |
466 | if (FREEABLE_P (ptr)) | |
467 | { | |
468 | /* Free the block if it is in the new private heap. */ | |
469 | HeapFree (heap, 0, ptr); | |
470 | } | |
471 | } | |
30d2b1c2 | 472 | |
587fd086 FP |
473 | void |
474 | free_before_dump (void *ptr) | |
475 | { | |
476 | /* Before dumping. */ | |
477 | if (dumped_data < (unsigned char *)ptr | |
478 | && (unsigned char *)ptr < bc_limit) | |
30d2b1c2 | 479 | { |
587fd086 FP |
480 | /* Free the block if it is allocated in the private heap. */ |
481 | HeapFree (heap, 0, ptr); | |
482 | } | |
483 | else | |
484 | { | |
485 | /* Look for the big chunk. */ | |
486 | int i; | |
30d2b1c2 | 487 | |
a7f999dc | 488 | for (i = 0; i < blocks_number; i++) |
30d2b1c2 | 489 | { |
587fd086 FP |
490 | if (blocks[i].address == ptr) |
491 | { | |
492 | /* Reset block occupation if found. */ | |
493 | blocks[i].occupied = 0; | |
494 | break; | |
495 | } | |
496 | /* What if the block is not found? We should trigger an | |
497 | error here. */ | |
498 | eassert (i < blocks_number); | |
30d2b1c2 | 499 | } |
30d2b1c2 | 500 | } |
587fd086 FP |
501 | } |
502 | ||
d2ff520a EZ |
503 | #ifdef ENABLE_CHECKING |
504 | void | |
505 | report_temacs_memory_usage (void) | |
506 | { | |
350aea69 EZ |
507 | DWORD blocks_used = 0; |
508 | size_t large_mem_used = 0; | |
a7f999dc EZ |
509 | int i; |
510 | ||
511 | for (i = 0; i < blocks_number; i++) | |
512 | if (blocks[i].occupied) | |
513 | { | |
514 | blocks_used++; | |
515 | large_mem_used += blocks[i].size; | |
516 | } | |
517 | ||
d2ff520a EZ |
518 | /* Emulate 'message', which writes to stderr in non-interactive |
519 | sessions. */ | |
520 | fprintf (stderr, | |
a7f999dc EZ |
521 | "Dump memory usage: Heap: %" PRIu64 " Large blocks(%lu/%lu): %" PRIu64 "/%" PRIu64 "\n", |
522 | (unsigned long long)committed, blocks_used, blocks_number, | |
523 | (unsigned long long)large_mem_used, | |
d2ff520a EZ |
524 | (unsigned long long)(dumped_data + DUMPED_HEAP_SIZE - bc_limit)); |
525 | } | |
526 | #endif | |
527 | ||
587fd086 FP |
528 | /* Emulate getpagesize. */ |
529 | int | |
530 | getpagesize (void) | |
531 | { | |
532 | return sysinfo_cache.dwPageSize; | |
533 | } | |
534 | ||
535 | void * | |
536 | sbrk (ptrdiff_t increment) | |
537 | { | |
0dd0ad37 EZ |
538 | /* data_region_end is the address beyond the last allocated byte. |
539 | The sbrk() function is not emulated at all, except for a 0 value | |
540 | of its parameter. This is needed by the Emacs Lisp function | |
541 | `memory-limit'. */ | |
542 | eassert (increment == 0); | |
587fd086 FP |
543 | return data_region_end; |
544 | } | |
545 | ||
546 | #define MAX_BUFFER_SIZE (512 * 1024 * 1024) | |
547 | ||
548 | /* MMAP allocation for buffers. */ | |
549 | void * | |
550 | mmap_alloc (void **var, size_t nbytes) | |
551 | { | |
552 | void *p = NULL; | |
553 | ||
554 | /* We implement amortized allocation. We start by reserving twice | |
555 | the size requested and commit only the size requested. Then | |
556 | realloc could proceed and use the reserved pages, reallocating | |
557 | only if needed. Buffer shrink would happen only so that we stay | |
558 | in the 2x range. This is a big win when visiting compressed | |
559 | files, where the final size of the buffer is not known in | |
560 | advance, and the buffer is enlarged several times as the data is | |
561 | decompressed on the fly. */ | |
562 | if (nbytes < MAX_BUFFER_SIZE) | |
563 | p = VirtualAlloc (NULL, (nbytes * 2), MEM_RESERVE, PAGE_READWRITE); | |
564 | ||
565 | /* If it fails, or if the request is above 512MB, try with the | |
566 | requested size. */ | |
567 | if (p == NULL) | |
568 | p = VirtualAlloc (NULL, nbytes, MEM_RESERVE, PAGE_READWRITE); | |
569 | ||
570 | if (p != NULL) | |
30d2b1c2 | 571 | { |
587fd086 FP |
572 | /* Now, commit pages for NBYTES. */ |
573 | *var = VirtualAlloc (p, nbytes, MEM_COMMIT, PAGE_READWRITE); | |
30d2b1c2 | 574 | } |
801f68b9 | 575 | |
6c572f9a EZ |
576 | if (!p) |
577 | { | |
578 | if (GetLastError () == ERROR_NOT_ENOUGH_MEMORY) | |
579 | errno = ENOMEM; | |
580 | else | |
581 | { | |
582 | DebPrint (("mmap_alloc: error %ld\n", GetLastError ())); | |
583 | errno = EINVAL; | |
584 | } | |
585 | } | |
587fd086 FP |
586 | |
587 | return *var = p; | |
95ed0025 RS |
588 | } |
589 | ||
95ed0025 | 590 | void |
587fd086 | 591 | mmap_free (void **var) |
95ed0025 | 592 | { |
587fd086 FP |
593 | if (*var) |
594 | { | |
595 | if (VirtualFree (*var, 0, MEM_RELEASE) == 0) | |
6c572f9a | 596 | DebPrint (("mmap_free: error %ld\n", GetLastError ())); |
587fd086 FP |
597 | *var = NULL; |
598 | } | |
599 | } | |
ce20e03e | 600 | |
587fd086 FP |
601 | void * |
602 | mmap_realloc (void **var, size_t nbytes) | |
603 | { | |
604 | MEMORY_BASIC_INFORMATION memInfo, m2; | |
605 | ||
606 | if (*var == NULL) | |
607 | return mmap_alloc (var, nbytes); | |
608 | ||
609 | /* This case happens in init_buffer(). */ | |
610 | if (nbytes == 0) | |
611 | { | |
612 | mmap_free (var); | |
613 | return mmap_alloc (var, nbytes); | |
614 | } | |
615 | ||
616 | if (VirtualQuery (*var, &memInfo, sizeof (memInfo)) == 0) | |
6c572f9a | 617 | DebPrint (("mmap_realloc: VirtualQuery error = %ld\n", GetLastError ())); |
587fd086 FP |
618 | |
619 | /* We need to enlarge the block. */ | |
620 | if (memInfo.RegionSize < nbytes) | |
621 | { | |
622 | if (VirtualQuery (*var + memInfo.RegionSize, &m2, sizeof(m2)) == 0) | |
6c572f9a EZ |
623 | DebPrint (("mmap_realloc: VirtualQuery error = %ld\n", |
624 | GetLastError ())); | |
587fd086 FP |
625 | /* If there is enough room in the current reserved area, then |
626 | commit more pages as needed. */ | |
627 | if (m2.State == MEM_RESERVE | |
628 | && nbytes <= memInfo.RegionSize + m2.RegionSize) | |
629 | { | |
630 | void *p; | |
631 | ||
632 | p = VirtualAlloc (*var + memInfo.RegionSize, | |
633 | nbytes - memInfo.RegionSize, | |
634 | MEM_COMMIT, PAGE_READWRITE); | |
635 | if (!p /* && GetLastError() != ERROR_NOT_ENOUGH_MEMORY */) | |
6c572f9a EZ |
636 | { |
637 | DebPrint (("realloc enlarge: VirtualAlloc error %ld\n", | |
638 | GetLastError ())); | |
639 | errno = ENOMEM; | |
640 | } | |
587fd086 FP |
641 | return *var; |
642 | } | |
643 | else | |
644 | { | |
645 | /* Else we must actually enlarge the block by allocating a | |
646 | new one and copying previous contents from the old to the | |
647 | new one. */ | |
648 | void *old_ptr = *var; | |
649 | ||
650 | if (mmap_alloc (var, nbytes)) | |
651 | { | |
652 | CopyMemory (*var, old_ptr, memInfo.RegionSize); | |
653 | mmap_free (&old_ptr); | |
654 | return *var; | |
655 | } | |
656 | else | |
657 | { | |
658 | /* We failed to enlarge the buffer. */ | |
659 | *var = old_ptr; | |
660 | return NULL; | |
661 | } | |
662 | } | |
663 | } | |
664 | ||
665 | /* If we are shrinking by more than one page... */ | |
666 | if (memInfo.RegionSize > nbytes + getpagesize()) | |
667 | { | |
668 | /* If we are shrinking a lot... */ | |
669 | if ((memInfo.RegionSize / 2) > nbytes) | |
670 | { | |
671 | /* Let's give some memory back to the system and release | |
672 | some pages. */ | |
673 | void *old_ptr = *var; | |
674 | ||
675 | if (mmap_alloc (var, nbytes)) | |
676 | { | |
677 | CopyMemory (*var, old_ptr, nbytes); | |
678 | mmap_free (&old_ptr); | |
679 | return *var; | |
680 | } | |
681 | else | |
682 | { | |
683 | /* In case we fail to shrink, try to go on with the old block. | |
684 | But that means there is a lot of memory pressure. | |
685 | We could also decommit pages. */ | |
686 | *var = old_ptr; | |
687 | return *var; | |
688 | } | |
689 | } | |
690 | ||
691 | /* We still can decommit pages. */ | |
692 | if (VirtualFree (*var + nbytes + get_page_size(), | |
693 | memInfo.RegionSize - nbytes - get_page_size(), | |
694 | MEM_DECOMMIT) == 0) | |
6c572f9a | 695 | DebPrint (("mmap_realloc: VirtualFree error %ld\n", GetLastError ())); |
587fd086 FP |
696 | return *var; |
697 | } | |
ce20e03e | 698 | |
587fd086 FP |
699 | /* Not enlarging, not shrinking by more than one page. */ |
700 | return *var; | |
95ed0025 | 701 | } |