2 * Copyright (c) 1999, 2006 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 /* Author: Bertrand Serlet, August 1999 */
26 #include "scalable_malloc.h"
27 #include "malloc_printf.h"
30 #include <pthread_internals.h>
34 #include <mach/vm_statistics.h>
35 #include <mach/mach_init.h>
36 #include <sys/types.h>
39 /********************* DEFINITIONS ************************/
41 #define DEBUG_MALLOC 0 // set to one to debug malloc itself
43 #define DEBUG_CLIENT 0 // set to one to help debug a nasty memory smasher
46 #warning DEBUG_MALLOC ENABLED
49 # define CHECK_LOCKED(szone, fun) \
51 if (__is_threaded && TRY_LOCK(szone->lock)) { \
52 malloc_printf("*** lock was not set %p in %s\n", szone->lock, fun); \
56 # define INLINE __inline__
57 # define ALWAYSINLINE __attribute__((always_inline))
58 # define CHECK_LOCKED(szone, fun) {}
62 * Access to global variables is slow, so optimise our handling of vm_page_size
65 #define _vm_page_size vm_page_size /* to get to the originals */
66 #define _vm_page_shift vm_page_shift
67 #define vm_page_size 4096 /* our normal working sizes */
68 #define vm_page_shift 12
70 typedef unsigned short msize_t
; // a size in multiples of SHIFT_SMALL_QUANTUM or SHIFT_TINY_QUANTUM
83 uintptr_t address_and_num_pages
;
84 // this type represents both an address and a number of pages
85 // the low bits are the number of pages; the high bits are the address
86 // note that the exact number of bits used for depends on the page size
87 // also, this cannot represent pointers larger than 1 << (vm_page_shift * 2)
90 typedef unsigned char grain_t
;
92 #define CHECK_REGIONS (1 << 31)
94 #define MAX_RECORDER_BUFFER 256
96 /********************* DEFINITIONS for tiny ************************/
99 * Memory in the Tiny range is allocated from regions (heaps) pointed to by the szone's tiny_regions
102 * Each region is laid out as a heap, followed by a header block, all within
103 * a 1MB (2^20) block. This means there are 64520 16-byte blocks and the header is
104 * 16138 bytes, making the total 1048458 bytes, leaving 118 bytes unused.
105 * The header block is arranged:
110 * 0xffffffff pad word
114 * pad word (not written)
118 * Each bitfield comprises NUM_TINY_BLOCKS bits, and refers to the corresponding TINY_QUANTUM block
121 * The bitfields are used to encode the state of memory within the heap. The header bit indicates
122 * that the corresponding quantum is the first quantum in a block (either in use or free). The
123 * in-use bit is set for the header if the block has been handed out (allocated). If the header
124 * bit is not set, the in-use bit is invalid.
126 * The szone maintains an array of 32 freelists, each of which is used to hold
127 * free objects of the corresponding quantum size.
129 * A free block is laid out depending on its size, in order to fit all free
130 * blocks in 16 bytes, on both 32 and 64 bit platforms. One quantum blocks do
131 * not store their size in the block, instead relying on the header information
132 * to determine their size. Blocks of two or more quanta have room to store
133 * their size in the block, and store it both after the 'next' pointer, and in
134 * the last 2 bytes of the block.
137 * Offset (32-bit mode) (64-bit mode)
143 * Offset (32-bit mode) (64-bit mode)
146 * 0x8 0x10 : size (in quantum counts)
147 * end - 2 end - 2 : size (in quantum counts)
150 * All fields are pointer-sized, except for the size which is an unsigned short.
154 #define SHIFT_TINY_QUANTUM 4 // Required for AltiVec
155 #define TINY_QUANTUM (1 << SHIFT_TINY_QUANTUM)
157 #define FOLLOWING_TINY_PTR(ptr,msize) (((unsigned char *)(ptr)) + ((msize) << SHIFT_TINY_QUANTUM))
159 #define NUM_TINY_SLOTS 32 // number of slots for free-lists
161 #define NUM_TINY_BLOCKS 64520
162 #define SHIFT_TINY_CEIL_BLOCKS 16 // ceil(log2(NUM_TINY_BLOCKS))
163 #define NUM_TINY_CEIL_BLOCKS (1 << SHIFT_TINY_CEIL_BLOCKS)
164 #define TINY_BLOCKS_ALIGN (SHIFT_TINY_CEIL_BLOCKS + SHIFT_TINY_QUANTUM)
167 * Enough room for the data, followed by the bit arrays (2-bits per block) plus 2 words of padding
168 * as our bitmap operators overflow, plus rounding to the nearest page.
170 #define TINY_HEADER_SIZE ((NUM_TINY_BLOCKS >> 2) + 8)
171 #define TINY_REGION_SIZE ((NUM_TINY_BLOCKS * TINY_QUANTUM + TINY_HEADER_SIZE + vm_page_size - 1) & ~ (vm_page_size - 1))
174 * Beginning and end pointers for a region's heap.
176 #define TINY_REGION_ADDRESS(region) ((void *)(region))
177 #define TINY_REGION_END(region) (TINY_REGION_ADDRESS(region) + (NUM_TINY_BLOCKS * TINY_QUANTUM))
180 * Locate the heap base for a pointer known to be within a tiny region.
182 #define TINY_REGION_FOR_PTR(_p) ((void *)((uintptr_t)(_p) & ~((1 << TINY_BLOCKS_ALIGN) - 1)))
185 * Convert between byte and msize units.
187 #define TINY_BYTES_FOR_MSIZE(_m) ((_m) << SHIFT_TINY_QUANTUM)
188 #define TINY_MSIZE_FOR_BYTES(_b) ((_b) >> SHIFT_TINY_QUANTUM)
191 # define TINY_FREE_SIZE(ptr) (((msize_t *)(ptr))[8])
193 # define TINY_FREE_SIZE(ptr) (((msize_t *)(ptr))[4])
195 #define TINY_PREVIOUS_MSIZE(ptr) ((msize_t *)(ptr))[-1]
198 * Locate the block header for a pointer known to be within a tiny region.
200 #define TINY_HEADER_START (NUM_TINY_BLOCKS * TINY_QUANTUM)
201 #define TINY_BLOCK_HEADER_FOR_PTR(_p) ((void *)((uintptr_t)TINY_REGION_FOR_PTR(_p) + TINY_HEADER_START))
204 * Locate the inuse map for a given block header pointer.
206 #define TINY_INUSE_FOR_HEADER(_h) ((void *)((uintptr_t)(_h) + (NUM_TINY_BLOCKS >> 3) + 4))
209 * Compute the bitmap index for a pointer known to be within a tiny region.
211 #define TINY_INDEX_FOR_PTR(_p) (((uintptr_t)(_p) >> SHIFT_TINY_QUANTUM) & (NUM_TINY_CEIL_BLOCKS - 1))
213 #define TINY_CACHE 1 // This governs a last-free cache of 1 that bypasses the free-list
216 #warning TINY_CACHE turned off
219 /********************* DEFINITIONS for small ************************/
222 * Memory in the Small range is allocated from regions (heaps) pointed to by the szone's small_regions
225 * Each region is laid out as a heap, followed by the metadata array, all within an 8MB (2^23) block.
226 * The array is arranged as an array of shorts, one for each SMALL_QUANTUM in the heap.
227 * This means there are 16320 512-blocks and the array is 16320*2 bytes, which totals 8388480, leaving
230 * The MSB of each short is set for the first quantum in a free block. The low 15 bits encode the
231 * block size (in SMALL_QUANTUM units), or are zero if the quantum is not the first in a block.
233 * The szone maintains an array of 32 freelists, each of which is used to hold free objects
234 * of the corresponding quantum size.
236 * A free block is laid out as:
238 * Offset (32-bit mode) (64-bit mode)
241 * 0x8 0x10 : size (in quantum counts)
242 * end - 2 end - 2 : size (in quantum counts)
245 * All fields are pointer-sized, except for the size which is an unsigned short.
249 #define SMALL_IS_FREE (1 << 15)
251 #define SHIFT_SMALL_QUANTUM (SHIFT_TINY_QUANTUM + 5) // 9
252 #define SMALL_QUANTUM (1 << SHIFT_SMALL_QUANTUM) // 512 bytes
254 #define FOLLOWING_SMALL_PTR(ptr,msize) (((unsigned char *)(ptr)) + ((msize) << SHIFT_SMALL_QUANTUM))
256 #define NUM_SMALL_SLOTS 32 // number of slots for free-lists
259 * We can only represent up to 1<<15 for msize; but we choose to stay even below that to avoid the
260 * convention msize=0 => msize = (1<<15)
262 #define NUM_SMALL_BLOCKS 16320
263 #define SHIFT_SMALL_CEIL_BLOCKS 14 // ceil(log2(NUM_SMALL_BLOCKs))
264 #define NUM_SMALL_CEIL_BLOCKS (1 << SHIFT_SMALL_CEIL_BLOCKS)
265 #define SMALL_BLOCKS_ALIGN (SHIFT_SMALL_CEIL_BLOCKS + SHIFT_SMALL_QUANTUM) // 23
266 #define SMALL_ARRAY_SIZE (NUM_SMALL_BLOCKS * 2)
267 #define SMALL_REGION_SIZE ((NUM_SMALL_BLOCKS * SMALL_QUANTUM + SMALL_ARRAY_SIZE + vm_page_size - 1) & ~ (vm_page_size - 1)) // data + meta data
269 #define SMALL_PREVIOUS_MSIZE(ptr) ((msize_t *)(ptr))[-1]
272 * Convert between byte and msize units.
274 #define SMALL_BYTES_FOR_MSIZE(_m) ((_m) << SHIFT_SMALL_QUANTUM)
275 #define SMALL_MSIZE_FOR_BYTES(_b) ((_b) >> SHIFT_SMALL_QUANTUM)
278 #define SMALL_REGION_ADDRESS(region) ((unsigned char *)region)
279 #define SMALL_REGION_END(region) (SMALL_REGION_ADDRESS(region) + (NUM_SMALL_BLOCKS * SMALL_QUANTUM))
282 * Locate the heap base for a pointer known to be within a small region.
284 #define SMALL_REGION_FOR_PTR(_p) ((void *)((uintptr_t)(_p) & ~((1 << SMALL_BLOCKS_ALIGN) - 1)))
287 * Locate the metadata base for a pointer known to be within a small region.
289 #define SMALL_HEADER_START (NUM_SMALL_BLOCKS * SMALL_QUANTUM)
290 #define SMALL_META_HEADER_FOR_PTR(_p) ((msize_t *)((uintptr_t)SMALL_REGION_FOR_PTR(_p) + SMALL_HEADER_START))
293 * Compute the metadata index for a pointer known to be within a small region.
295 #define SMALL_META_INDEX_FOR_PTR(_p) (((uintptr_t)(_p) >> SHIFT_SMALL_QUANTUM) & (NUM_SMALL_CEIL_BLOCKS - 1))
298 * Find the metadata word for a pointer known to be within a small region.
300 #define SMALL_METADATA_FOR_PTR(_p) (SMALL_META_HEADER_FOR_PTR(_p) + SMALL_META_INDEX_FOR_PTR(_p))
303 * Determine whether a pointer known to be within a small region points to memory which is free.
305 #define SMALL_PTR_IS_FREE(_p) (*SMALL_METADATA_FOR_PTR(_p) & SMALL_IS_FREE)
308 * Extract the msize value for a pointer known to be within a small region.
310 #define SMALL_PTR_SIZE(_p) (*SMALL_METADATA_FOR_PTR(_p) & ~SMALL_IS_FREE)
312 #define PROTECT_SMALL 0 // Should be 0: 1 is too slow for normal use
314 #define SMALL_CACHE 1
316 #warning SMALL_CACHE turned off
319 /********************* DEFINITIONS for large and huge ***********************/
321 #define LARGE_THRESHOLD (15 * 1024) // at or above this use "large"
323 #if (LARGE_THRESHOLD > NUM_SMALL_SLOTS * SMALL_QUANTUM)
324 #error LARGE_THRESHOLD should always be less than NUM_SMALL_SLOTS * SMALL_QUANTUM
327 #define VM_COPY_THRESHOLD (40 * 1024)
328 // When all memory is touched after a copy, vm_copy() is always a lose
329 // But if the memory is only read, vm_copy() wins over memmove() at 3 or 4 pages (on a G3/300MHz)
330 // This must be larger than LARGE_THRESHOLD
333 * Given a large_entry, return the address of the allocated block.
335 #define LARGE_ENTRY_ADDRESS(entry) \
336 (void *)(((entry).address_and_num_pages >> vm_page_shift) << vm_page_shift)
339 * Given a large entry, return the number of pages or bytes in the allocated block.
341 #define LARGE_ENTRY_NUM_PAGES(entry) \
342 ((entry).address_and_num_pages & (vm_page_size - 1))
343 #define LARGE_ENTRY_SIZE(entry) \
344 (LARGE_ENTRY_NUM_PAGES(entry) << vm_page_shift)
347 * Compare a pointer with a large entry.
349 #define LARGE_ENTRY_MATCHES(entry,ptr) \
350 ((((entry).address_and_num_pages - (uintptr_t)(ptr)) >> vm_page_shift) == 0)
352 #define LARGE_ENTRY_IS_EMPTY(entry) (((entry).address_and_num_pages) == 0)
354 typedef compact_range_t large_entry_t
;
355 typedef vm_range_t huge_entry_t
;
357 /*******************************************************************************
358 * Definitions for region hash
359 ******************************************************************************/
361 typedef void * region_t
;
363 #define INITIAL_NUM_REGIONS 63 // Must be odd to hash well
365 /********************* zone itself ************************/
368 malloc_zone_t basic_zone
;
370 unsigned debug_flags
;
373 /* Regions for tiny objects */
374 size_t num_tiny_regions
;
375 size_t num_tiny_regions_allocated
;
376 region_t
*tiny_regions
; // hashed by location
377 region_t
*last_tiny_region
;
378 void *last_tiny_free
; // low SHIFT_TINY_QUANTUM indicate the msize
379 unsigned tiny_bitmap
; // cache of the 32 free lists
380 free_list_t
*tiny_free_list
[NUM_TINY_SLOTS
]; // 31 free lists for 1*TINY_QUANTUM to 31*TINY_QUANTUM plus 1 for larger than 32*SMALL_QUANTUM
381 size_t tiny_bytes_free_at_end
; // the last free region in the last block is treated as a big block in use that is not accounted for
382 unsigned num_tiny_objects
;
383 size_t num_bytes_in_tiny_objects
;
385 /* Regions for small objects */
386 size_t num_small_regions
;
387 size_t num_small_regions_allocated
;
388 region_t
*small_regions
; // hashed by location
389 region_t
*last_small_region
;
390 void *last_small_free
; // low SHIFT_SMALL_QUANTUM indicate the msize
391 unsigned small_bitmap
; // cache of the free list
392 free_list_t
*small_free_list
[NUM_SMALL_SLOTS
];
393 size_t small_bytes_free_at_end
; // the last free region in the last block is treated as a big block in use that is not accounted for
394 unsigned num_small_objects
;
395 size_t num_bytes_in_small_objects
;
397 /* large objects: vm_page_shift <= log2(size) < 2 *vm_page_shift */
398 unsigned num_large_objects_in_use
;
399 unsigned num_large_entries
;
400 large_entry_t
*large_entries
; // hashed by location; null entries don't count
401 size_t num_bytes_in_large_objects
;
403 /* huge objects: log2(size) >= 2 *vm_page_shift */
404 unsigned num_huge_entries
;
405 huge_entry_t
*huge_entries
;
406 size_t num_bytes_in_huge_objects
;
408 /* Initial region list */
409 region_t initial_tiny_regions
[INITIAL_NUM_REGIONS
];
410 region_t initial_small_regions
[INITIAL_NUM_REGIONS
];
413 #define SZONE_PAGED_SIZE ((sizeof(szone_t) + vm_page_size - 1) & ~ (vm_page_size - 1))
415 #if DEBUG_MALLOC || DEBUG_CLIENT
416 static void szone_sleep(void);
418 __private_extern__
void malloc_error_break(void);
420 // msg prints after fmt, ...
421 static void szone_error(szone_t
*szone
, const char *msg
, const void *ptr
, const char *fmt
, ...) __printflike(4, 5);
423 static void protect(void *address
, size_t size
, unsigned protection
, unsigned debug_flags
);
424 static void *allocate_pages(szone_t
*szone
, size_t size
, unsigned char align
, unsigned debug_flags
, int vm_page_label
);
425 static void deallocate_pages(szone_t
*szone
, void *addr
, size_t size
, unsigned debug_flags
);
426 static kern_return_t
_szone_default_reader(task_t task
, vm_address_t address
, vm_size_t size
, void **ptr
);
428 static INLINE
void free_list_checksum(szone_t
*szone
, free_list_t
*ptr
, const char *msg
) ALWAYSINLINE
;
429 static INLINE
void free_list_set_checksum(szone_t
*szone
, free_list_t
*ptr
) ALWAYSINLINE
;
430 static INLINE
uintptr_t free_list_checksum_ptr(void *p
) ALWAYSINLINE
;
431 static INLINE
void * free_list_unchecksum_ptr(ptr_union ptr
) ALWAYSINLINE
;
432 static unsigned free_list_count(const free_list_t
*ptr
);
434 static INLINE msize_t
get_tiny_meta_header(const void *ptr
, boolean_t
*is_free
) ALWAYSINLINE
;
435 static INLINE
void set_tiny_meta_header_in_use(const void *ptr
, msize_t msize
) ALWAYSINLINE
;
436 static INLINE
void set_tiny_meta_header_middle(const void *ptr
) ALWAYSINLINE
;
437 static INLINE
void set_tiny_meta_header_free(const void *ptr
, msize_t msize
) ALWAYSINLINE
;
438 static INLINE boolean_t
tiny_meta_header_is_free(const void *ptr
) ALWAYSINLINE
;
439 static INLINE
void *tiny_previous_preceding_free(void *ptr
, msize_t
*prev_msize
) ALWAYSINLINE
;
440 static void tiny_free_list_add_ptr(szone_t
*szone
, void *ptr
, msize_t msize
);
441 static void tiny_free_list_remove_ptr(szone_t
*szone
, void *ptr
, msize_t msize
);
442 static INLINE region_t
*tiny_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
) ALWAYSINLINE
;
443 static INLINE
void tiny_free_no_lock(szone_t
*szone
, region_t
*region
, void *ptr
, msize_t msize
) ALWAYSINLINE
;
444 static void *tiny_malloc_from_region_no_lock(szone_t
*szone
, msize_t msize
);
445 static INLINE boolean_t
try_realloc_tiny_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
) ALWAYSINLINE
;
446 static boolean_t
tiny_check_region(szone_t
*szone
, region_t region
);
447 static kern_return_t
tiny_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
, memory_reader_t reader
, vm_range_recorder_t recorder
);
448 static void *tiny_malloc_from_free_list(szone_t
*szone
, msize_t msize
);
449 static INLINE
void *tiny_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
) ALWAYSINLINE
;
450 static INLINE
void free_tiny(szone_t
*szone
, void *ptr
, region_t
*tiny_region
) ALWAYSINLINE
;
451 static void print_tiny_free_list(szone_t
*szone
);
452 static void print_tiny_region(boolean_t verbose
, region_t region
, size_t bytes_at_end
);
453 static boolean_t
tiny_free_list_check(szone_t
*szone
, grain_t slot
);
455 static INLINE
void small_meta_header_set_is_free(msize_t
*meta_headers
, unsigned index
, msize_t msize
) ALWAYSINLINE
;
456 static INLINE
void small_meta_header_set_in_use(msize_t
*meta_headers
, msize_t index
, msize_t msize
) ALWAYSINLINE
;
457 static INLINE
void small_meta_header_set_middle(msize_t
*meta_headers
, msize_t index
) ALWAYSINLINE
;
458 static void small_free_list_add_ptr(szone_t
*szone
, void *ptr
, msize_t msize
);
459 static void small_free_list_remove_ptr(szone_t
*szone
, void *ptr
, msize_t msize
);
460 static INLINE region_t
*small_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
) ALWAYSINLINE
;
461 static INLINE
void small_free_no_lock(szone_t
*szone
, region_t
*region
, void *ptr
, msize_t msize
) ALWAYSINLINE
;
462 static void *small_malloc_from_region_no_lock(szone_t
*szone
, msize_t msize
);
463 static INLINE boolean_t
try_realloc_small_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
) ALWAYSINLINE
;
464 static boolean_t
szone_check_small_region(szone_t
*szone
, region_t region
);
465 static kern_return_t
small_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
, memory_reader_t reader
, vm_range_recorder_t recorder
);
466 static void *small_malloc_from_free_list(szone_t
*szone
, msize_t msize
);
467 static INLINE
void *small_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
) ALWAYSINLINE
;
468 static INLINE
void *small_malloc_cleared_no_lock(szone_t
*szone
, msize_t msize
) ALWAYSINLINE
;
469 static INLINE
void free_small(szone_t
*szone
, void *ptr
, region_t
*small_region
) ALWAYSINLINE
;
470 static void print_small_free_list(szone_t
*szone
);
471 static void print_small_region(szone_t
*szone
, boolean_t verbose
, region_t region
, size_t bytes_at_end
);
472 static boolean_t
small_free_list_check(szone_t
*szone
, grain_t grain
);
474 static region_t
* hash_lookup_region_no_lock(region_t
*regions
, size_t num_entries
, region_t r
);
475 static void hash_region_insert_no_lock(region_t
*regions
, size_t num_entries
, region_t r
);
476 static region_t
* hash_regions_alloc_no_lock(szone_t
*szone
, size_t num_entries
);
477 static region_t
* hash_regions_grow_no_lock(szone_t
*szone
, region_t
*regions
, size_t old_size
, size_t *new_size
);
480 static void large_debug_print(szone_t
*szone
);
482 static large_entry_t
*large_entry_for_pointer_no_lock(szone_t
*szone
, const void *ptr
);
483 static void large_entry_insert_no_lock(szone_t
*szone
, large_entry_t range
);
484 static INLINE
void large_entries_rehash_after_entry_no_lock(szone_t
*szone
, large_entry_t
*entry
) ALWAYSINLINE
;
485 static INLINE large_entry_t
*large_entries_alloc_no_lock(szone_t
*szone
, unsigned num
) ALWAYSINLINE
;
486 static void large_entries_free_no_lock(szone_t
*szone
, large_entry_t
*entries
, unsigned num
, vm_range_t
*range_to_deallocate
);
487 static large_entry_t
* large_entries_grow_no_lock(szone_t
*szone
, vm_range_t
*range_to_deallocate
);
488 static vm_range_t
large_free_no_lock(szone_t
*szone
, large_entry_t
*entry
);
489 static kern_return_t
large_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t large_entries_address
, unsigned num_entries
, memory_reader_t reader
, vm_range_recorder_t recorder
);
490 static huge_entry_t
*huge_entry_for_pointer_no_lock(szone_t
*szone
, const void *ptr
);
491 static boolean_t
huge_entry_append(szone_t
*szone
, huge_entry_t huge
);
492 static kern_return_t
huge_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t huge_entries_address
, unsigned num_entries
, memory_reader_t reader
, vm_range_recorder_t recorder
);
493 static void *large_and_huge_malloc(szone_t
*szone
, size_t num_pages
);
494 static INLINE
void free_large_or_huge(szone_t
*szone
, void *ptr
) ALWAYSINLINE
;
495 static INLINE
int try_realloc_large_or_huge_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
) ALWAYSINLINE
;
497 static void szone_free(szone_t
*szone
, void *ptr
);
498 static INLINE
void *szone_malloc_should_clear(szone_t
*szone
, size_t size
, boolean_t cleared_requested
) ALWAYSINLINE
;
499 static void *szone_malloc(szone_t
*szone
, size_t size
);
500 static void *szone_calloc(szone_t
*szone
, size_t num_items
, size_t size
);
501 static void *szone_valloc(szone_t
*szone
, size_t size
);
502 static size_t szone_size(szone_t
*szone
, const void *ptr
);
503 static void *szone_realloc(szone_t
*szone
, void *ptr
, size_t new_size
);
504 static unsigned szone_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
);
505 static void szone_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
);
506 static void szone_destroy(szone_t
*szone
);
507 static size_t szone_good_size(szone_t
*szone
, size_t size
);
509 static boolean_t
szone_check_all(szone_t
*szone
, const char *function
);
510 static boolean_t
szone_check(szone_t
*szone
);
511 static kern_return_t
szone_ptr_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t zone_address
, memory_reader_t reader
, vm_range_recorder_t recorder
);
512 static void szone_print(szone_t
*szone
, boolean_t verbose
);
513 static void szone_log(malloc_zone_t
*zone
, void *log_address
);
514 static void szone_force_lock(szone_t
*szone
);
515 static void szone_force_unlock(szone_t
*szone
);
517 static void szone_statistics(szone_t
*szone
, malloc_statistics_t
*stats
);
519 static void *frozen_malloc(szone_t
*zone
, size_t new_size
);
520 static void *frozen_calloc(szone_t
*zone
, size_t num_items
, size_t size
);
521 static void *frozen_valloc(szone_t
*zone
, size_t new_size
);
522 static void *frozen_realloc(szone_t
*zone
, void *ptr
, size_t new_size
);
523 static void frozen_free(szone_t
*zone
, void *ptr
);
524 static void frozen_destroy(szone_t
*zone
);
527 # define LOG(szone,ptr) \
528 (szone->log_address && (((uintptr_t)szone->log_address == -1) || (szone->log_address == (void *)(ptr))))
530 # define LOG(szone,ptr) 0
533 #define SZONE_LOCK(szone) \
538 #define SZONE_UNLOCK(szone) \
540 UNLOCK(szone->lock); \
543 #define LOCK_AND_NOTE_LOCKED(szone,locked) \
545 CHECK(szone, __PRETTY_FUNCTION__); \
546 locked = 1; SZONE_LOCK(szone); \
549 #if DEBUG_MALLOC || DEBUG_CLIENT
550 # define CHECK(szone,fun) \
551 if ((szone)->debug_flags & CHECK_REGIONS) szone_check_all(szone, fun)
553 # define CHECK(szone,fun) do {} while (0)
556 /********************* VERY LOW LEVEL UTILITIES ************************/
558 #if DEBUG_MALLOC || DEBUG_CLIENT
563 if (getenv("MallocErrorSleep")) {
564 _malloc_printf(ASL_LEVEL_NOTICE
, "*** sleeping to help debug\n");
565 sleep(3600); // to help debug
570 // msg prints after fmt, ...
571 static __attribute__((noinline
)) void
572 szone_error(szone_t
*szone
, const char *msg
, const void *ptr
, const char *fmt
, ...)
575 _SIMPLE_STRING b
= _simple_salloc();
577 if (szone
) SZONE_UNLOCK(szone
);
581 _simple_vsprintf(b
, fmt
, ap
);
585 _simple_sprintf(b
, "*** error for object %p: %s\n", ptr
, msg
);
587 _simple_sprintf(b
, "*** error: %s\n", msg
);
589 malloc_printf("%s*** set a breakpoint in malloc_error_break to debug\n", _simple_string(b
));
593 * Should only get here if vm_allocate() can't get a single page of
594 * memory, implying _simple_asl_log() would also fail. So we just
595 * print to the file descriptor.
599 _malloc_vprintf(MALLOC_PRINTF_NOLOG
, fmt
, ap
);
603 _malloc_printf(MALLOC_PRINTF_NOLOG
, "*** error for object %p: %s\n", ptr
, msg
);
605 _malloc_printf(MALLOC_PRINTF_NOLOG
, "*** error: %s\n", msg
);
607 _malloc_printf(MALLOC_PRINTF_NOLOG
, "*** set a breakpoint in malloc_error_break to debug\n");
609 malloc_error_break();
611 szone_print(szone
, 1);
617 if (szone
->debug_flags
& SCALABLE_MALLOC_ABORT_ON_ERROR
) abort();
621 protect(void *address
, size_t size
, unsigned protection
, unsigned debug_flags
)
625 if (!(debug_flags
& SCALABLE_MALLOC_DONT_PROTECT_PRELUDE
)) {
626 err
= vm_protect(mach_task_self(), (vm_address_t
)(uintptr_t)address
- vm_page_size
, vm_page_size
, 0, protection
);
628 malloc_printf("*** can't protect(%p) region for prelude guard page at %p\n",
629 protection
,address
- (1 << vm_page_shift
));
632 if (!(debug_flags
& SCALABLE_MALLOC_DONT_PROTECT_POSTLUDE
)) {
633 err
= vm_protect(mach_task_self(), (vm_address_t
)(uintptr_t)address
+ size
, vm_page_size
, 0, protection
);
635 malloc_printf("*** can't protect(%p) region for postlude guard page at %p\n",
636 protection
, address
+ size
);
642 allocate_pages(szone_t
*szone
, size_t size
, unsigned char align
, unsigned debug_flags
, int vm_page_label
)
644 // align specifies a desired alignment (as a log) or 0 if no alignment requested
646 uintptr_t addr
, aligned_address
;
647 boolean_t add_guard_pages
= debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
;
648 size_t allocation_size
= round_page(size
);
651 if (align
) add_guard_pages
= 0; // too cumbersome to deal with that
652 if (!allocation_size
) allocation_size
= 1 << vm_page_shift
;
653 if (add_guard_pages
) allocation_size
+= 2 * (1 << vm_page_shift
);
654 if (align
) allocation_size
+= (size_t)1 << align
;
655 vm_addr
= mmap(0, allocation_size
, PROT_READ
| PROT_WRITE
, MAP_ANON
| MAP_PRIVATE
, VM_MAKE_TAG(vm_page_label
), 0);
656 if ((uintptr_t)vm_addr
== -1) {
657 szone_error(szone
, "can't allocate region", NULL
, "*** mmap(size=%lld) failed (error code=%d)\n", (long long)allocation_size
, errno
);
660 addr
= (uintptr_t)vm_addr
;
662 aligned_address
= (addr
+ ((uintptr_t)1 << align
) - 1) & ~ (((uintptr_t)1 << align
) - 1);
663 if (aligned_address
!= addr
) {
664 delta
= aligned_address
- addr
;
665 if (munmap((void *)addr
, delta
) == -1)
666 malloc_printf("*** freeing unaligned header failed with %d\n", errno
);
667 addr
= aligned_address
;
668 allocation_size
-= delta
;
670 if (allocation_size
> size
) {
671 if (munmap((void *)(addr
+ size
), allocation_size
- size
) == -1)
672 malloc_printf("*** freeing unaligned footer failed with %d\n", errno
);
675 if (add_guard_pages
) {
676 addr
+= (uintptr_t)1 << vm_page_shift
;
677 protect((void *)addr
, size
, 0, debug_flags
);
683 deallocate_pages(szone_t
*szone
, void *addr
, size_t size
, unsigned debug_flags
)
686 boolean_t add_guard_pages
= debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
;
688 if (add_guard_pages
) {
689 addr
-= 1 << vm_page_shift
;
690 size
+= 2 * (1 << vm_page_shift
);
692 err
= munmap(addr
, size
);
693 if ((err
== -1) && szone
)
694 szone_error(szone
, "Can't deallocate_pages region", addr
, NULL
);
698 _szone_default_reader(task_t task
, vm_address_t address
, vm_size_t size
, void **ptr
)
700 *ptr
= (void *)address
;
704 /********************* FREE LIST UTILITIES ************************/
706 // A free list entry is comprised of a pair of pointers, previous and next.
707 // Because the free list entries are previously freed objects, there is a
708 // non-zero chance that a misbehaved program will write to an allocated object
709 // after it has called free() on the pointer. This write would then potentially
710 // corrupt the previous and next pointers, leading to a crash. In order to
711 // detect this case, we take advantage of the fact that pointers are known to
712 // be at least 16 byte aligned, and thus have at least 4 trailing zero bits.
713 // When an entry is added to the free list, the previous and next pointers are
714 // shifted right by 2 bits, and then have the high and low 2 bits set, to act
715 // as guard bits. Before accessing a free list object, we verify that these
716 // bits are still set, and log an error if they are not.
719 free_list_checksum(szone_t
*szone
, free_list_t
*ptr
, const char *msg
)
721 uintptr_t ptrs
= ptr
->previous
.u
& ptr
->next
.u
;
724 ptrs
= (ptrs
<< 2) | (ptrs
>> (64-2));
726 ptrs
= (ptrs
<< 2) | (ptrs
>> (32-2));
729 if ((ptrs
& 15) != 15)
730 szone_error(szone
, "incorrect checksum for freed object "
731 "- object was probably modified after being freed.", ptr
, NULL
);
734 static INLINE
uintptr_t
735 free_list_checksum_ptr(void *p
)
741 return (ptr
.u
>> 2) | 0xC000000000000003ULL
;
743 return (ptr
.u
>> 2) | 0xC0000003U
;
748 free_list_unchecksum_ptr(ptr_union ptr
)
750 uintptr_t u
= (ptr
.u
>> 2) << 4;
755 free_list_set_checksum(szone_t
*szone
, free_list_t
*ptr
)
757 ptr
->previous
.u
= free_list_checksum_ptr(ptr
->previous
.p
);
758 ptr
->next
.u
= free_list_checksum_ptr(ptr
->next
.p
);
762 free_list_count(const free_list_t
*ptr
)
768 ptr
= free_list_unchecksum_ptr(ptr
->next
);
773 /* XXX inconsistent use of BITMAP32 and BITARRAY operations could be cleaned up */
775 #define BITMAP32_SET(bitmap,bit) (bitmap |= 1 << (bit))
776 #define BITMAP32_CLR(bitmap,bit) (bitmap &= ~ (1 << (bit)))
777 #define BITMAP32_BIT(bitmap,bit) ((bitmap >> (bit)) & 1)
779 /* returns bit # of least-significant one bit, starting at 0 (undefined if !bitmap) */
780 #define BITMAP32_CTZ(bitmap) (__builtin_ctz(bitmap))
782 /********************* TINY FREE LIST UTILITIES ************************/
784 // We encode the meta-headers as follows:
785 // Each quantum has an associated set of 2 bits:
786 // block_header when 1 says this block is the beginning of a block
787 // in_use when 1 says this block is in use
788 // so a block in use of size 3 is 1-1 0-X 0-X
789 // for a free block TINY_FREE_SIZE(ptr) carries the size and the bits are 1-0 X-X X-X
790 // for a block middle the bits are 0-0
792 // Attention double evaluation for these
793 #define BITARRAY_SET(bits,index) (bits[index>>3] |= (1 << (index & 7)))
794 #define BITARRAY_CLR(bits,index) (bits[index>>3] &= ~(1 << (index & 7)))
795 #define BITARRAY_BIT(bits,index) (((bits[index>>3]) >> (index & 7)) & 1)
797 // Following is for start<8 and end<=start+32
798 static void ALWAYSINLINE
799 bitarray_mclr(void *bits
, unsigned start
, unsigned end
) {
800 unsigned word
= OSReadLittleInt32(bits
, 0);
801 unsigned mask
= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
804 unsigned char *bytes
= (unsigned char *)bits
;
805 bytes
[4] &= ~((1 << (end
- 32)) - 1);
807 mask
|= (0xFFFFFFFF << end
);
809 OSWriteLittleInt32(bits
, 0, word
& mask
);
813 * Obtain the size of a free tiny block (in msize_t units).
816 get_tiny_free_size(const void *ptr
)
818 void *next_block
= (void *)((uintptr_t)ptr
+ TINY_QUANTUM
);
819 void *region_end
= TINY_REGION_END(TINY_REGION_FOR_PTR(ptr
));
821 // check whether the next block is outside the tiny region or a block header
822 // if so, then the size of this block is one, and there is no stored size.
823 if (next_block
< region_end
)
825 unsigned char *next_header
= TINY_BLOCK_HEADER_FOR_PTR(next_block
);
826 msize_t next_index
= TINY_INDEX_FOR_PTR(next_block
);
828 if (!BITARRAY_BIT(next_header
, next_index
))
829 return TINY_FREE_SIZE(ptr
);
835 * Get the size of the previous free block, which is stored in the last two
836 * bytes of the block. If the previous block is not free, then the result is
840 get_tiny_previous_free_msize(const void *ptr
)
842 // check whether the previous block is in the tiny region and a block header
843 // if so, then the size of the previous block is one, and there is no stored
845 if (ptr
!= TINY_REGION_FOR_PTR(ptr
))
847 void *prev_block
= (void *)((uintptr_t)ptr
- TINY_QUANTUM
);
848 unsigned char *prev_header
= TINY_BLOCK_HEADER_FOR_PTR(prev_block
);
849 msize_t prev_index
= TINY_INDEX_FOR_PTR(prev_block
);
850 if (BITARRAY_BIT(prev_header
, prev_index
))
852 return TINY_PREVIOUS_MSIZE(ptr
);
854 // don't read possibly unmapped memory before the beginning of the region
858 static INLINE msize_t
859 get_tiny_meta_header(const void *ptr
, boolean_t
*is_free
)
861 // returns msize and is_free
862 // may return 0 for the msize component (meaning 65536)
863 unsigned char *block_header
;
864 unsigned char *in_use
;
868 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
869 index
= TINY_INDEX_FOR_PTR(ptr
);
870 byte_index
= index
>> 3;
872 block_header
+= byte_index
;
875 if (!BITMAP32_BIT(*block_header
, index
))
877 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
878 if (!BITMAP32_BIT(*in_use
, index
)) {
880 return get_tiny_free_size(ptr
);
882 uint32_t *addr
= (uint32_t *)((uintptr_t)block_header
& ~3);
883 uint32_t word0
= OSReadLittleInt32(addr
, 0) >> index
;
884 uint32_t word1
= OSReadLittleInt32(addr
, 4) << (8 - index
);
885 uint32_t bits
= (((uintptr_t)block_header
& 3) * 8); // precision loss on LP64 OK here
886 uint32_t word
= (word0
>> bits
) | (word1
<< (24 - bits
));
887 uint32_t result
= ffs(word
>> 1);
892 set_tiny_meta_header_in_use(const void *ptr
, msize_t msize
)
894 unsigned char *block_header
;
895 unsigned char *in_use
;
901 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
902 index
= TINY_INDEX_FOR_PTR(ptr
);
903 byte_index
= index
>> 3;
907 malloc_printf("set_tiny_meta_header_in_use() invariant broken %p %d\n", ptr
, msize
);
908 if ((unsigned)index
+ (unsigned)msize
> 0x10000)
909 malloc_printf("set_tiny_meta_header_in_use() invariant broken (2) %p %d\n", ptr
, msize
);
911 block_header
+= byte_index
;
913 BITMAP32_SET(*block_header
, index
);
914 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
915 BITMAP32_SET(*in_use
, index
);
919 byte_index
= index
>> 3;
920 block_header
+= byte_index
; in_use
+= byte_index
;
922 end_bit
= index
+ clr_msize
;
923 bitarray_mclr(block_header
, index
, end_bit
);
924 bitarray_mclr(in_use
, index
, end_bit
);
926 BITARRAY_SET(block_header
, index
+clr_msize
); // we set the block_header bit for the following block to reaffirm next block is a block
932 mf
= get_tiny_meta_header(ptr
, &ff
);
934 malloc_printf("setting header for tiny in_use %p : %d\n", ptr
, msize
);
935 malloc_printf("reading header for tiny %p : %d %d\n", ptr
, mf
, ff
);
942 set_tiny_meta_header_middle(const void *ptr
)
944 // indicates this block is in the middle of an in use block
945 unsigned char *block_header
;
946 unsigned char *in_use
;
949 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
950 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
951 index
= TINY_INDEX_FOR_PTR(ptr
);
953 BITARRAY_CLR(block_header
, index
);
954 BITARRAY_CLR(in_use
, index
);
958 set_tiny_meta_header_free(const void *ptr
, msize_t msize
)
960 // !msize is acceptable and means 65536
961 unsigned char *block_header
;
962 unsigned char *in_use
;
965 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
966 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
967 index
= TINY_INDEX_FOR_PTR(ptr
);
970 if ((unsigned)index
+ (unsigned)msize
> 0x10000) {
971 malloc_printf("setting header for tiny free %p msize too large: %d\n", ptr
, msize
);
974 BITARRAY_SET(block_header
, index
);
975 BITARRAY_CLR(in_use
, index
);
976 // mark the end of this block if msize is > 1. For msize == 0, the whole
977 // region is free, so there is no following block. For msize == 1, there is
978 // no space to write the size on 64 bit systems. The size for 1 quantum
979 // blocks is computed from the metadata bitmaps.
981 void *follower
= FOLLOWING_TINY_PTR(ptr
, msize
);
982 TINY_PREVIOUS_MSIZE(follower
) = msize
;
983 TINY_FREE_SIZE(ptr
) = msize
;
986 TINY_FREE_SIZE(ptr
) = msize
;
990 msize_t mf
= get_tiny_meta_header(ptr
, &ff
);
991 if ((msize
!= mf
) || !ff
) {
992 malloc_printf("setting header for tiny free %p : %u\n", ptr
, msize
);
993 malloc_printf("reading header for tiny %p : %u %u\n", ptr
, mf
, ff
);
998 static INLINE boolean_t
999 tiny_meta_header_is_free(const void *ptr
)
1001 unsigned char *block_header
;
1002 unsigned char *in_use
;
1005 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1006 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
1007 index
= TINY_INDEX_FOR_PTR(ptr
);
1008 if (!BITARRAY_BIT(block_header
, index
))
1010 return !BITARRAY_BIT(in_use
, index
);
1013 static INLINE
void *
1014 tiny_previous_preceding_free(void *ptr
, msize_t
*prev_msize
)
1016 // returns the previous block, assuming and verifying it's free
1017 unsigned char *block_header
;
1018 unsigned char *in_use
;
1020 msize_t previous_msize
;
1021 msize_t previous_index
;
1024 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1025 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
1026 index
= TINY_INDEX_FOR_PTR(ptr
);
1030 if ((previous_msize
= get_tiny_previous_free_msize(ptr
)) > index
)
1033 previous_index
= index
- previous_msize
;
1034 previous_ptr
= (void *)(TINY_REGION_FOR_PTR(ptr
) + TINY_BYTES_FOR_MSIZE(previous_index
));
1035 if (!BITARRAY_BIT(block_header
, previous_index
))
1037 if (BITARRAY_BIT(in_use
, previous_index
))
1039 if (get_tiny_free_size(previous_ptr
) != previous_msize
)
1042 // conservative check did match true check
1043 *prev_msize
= previous_msize
;
1044 return previous_ptr
;
1048 * Adds an item to the proper free list, and also marks the meta-header of the
1050 * Assumes szone has been locked
1053 tiny_free_list_add_ptr(szone_t
*szone
, void *ptr
, msize_t msize
)
1055 grain_t slot
= (!msize
|| (msize
>= NUM_TINY_SLOTS
)) ? NUM_TINY_SLOTS
- 1 : msize
- 1;
1056 free_list_t
*free_ptr
= ptr
;
1057 free_list_t
*free_head
= szone
->tiny_free_list
[slot
];
1060 if (LOG(szone
,ptr
)) {
1061 malloc_printf("in %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
1063 if (((uintptr_t)ptr
) & (TINY_QUANTUM
- 1)) {
1064 szone_error(szone
, "tiny_free_list_add_ptr: Unaligned ptr", ptr
, NULL
);
1067 set_tiny_meta_header_free(ptr
, msize
);
1069 free_list_checksum(szone
, free_head
, __PRETTY_FUNCTION__
);
1071 if (free_list_unchecksum_ptr(free_head
->previous
)) {
1072 szone_error(szone
, "tiny_free_list_add_ptr: Internal invariant broken (free_head->previous)", ptr
,
1073 "ptr=%p slot=%d free_head=%p previous=%p\n", ptr
, slot
, free_head
, free_head
->previous
.p
);
1075 if (! tiny_meta_header_is_free(free_head
)) {
1076 szone_error(szone
, "tiny_free_list_add_ptr: Internal invariant broken (free_head is not a free pointer)", ptr
,
1077 "ptr=%p slot=%d free_head=%p\n", ptr
, slot
, free_head
);
1080 free_head
->previous
.u
= free_list_checksum_ptr(free_ptr
);
1082 BITMAP32_SET(szone
->tiny_bitmap
, slot
);
1084 free_ptr
->previous
.p
= NULL
;
1085 free_ptr
->next
.p
= free_head
;
1086 free_list_set_checksum(szone
, free_ptr
);
1087 szone
->tiny_free_list
[slot
] = free_ptr
;
1091 * Removes the item pointed to by ptr in the proper free list.
1092 * Assumes szone has been locked
1095 tiny_free_list_remove_ptr(szone_t
*szone
, void *ptr
, msize_t msize
)
1097 grain_t slot
= (!msize
|| (msize
>= NUM_TINY_SLOTS
)) ? NUM_TINY_SLOTS
- 1 : msize
- 1;
1098 free_list_t
*free_ptr
= ptr
, *next
, *previous
;
1099 free_list_checksum(szone
, free_ptr
, __PRETTY_FUNCTION__
);
1101 next
= free_list_unchecksum_ptr(free_ptr
->next
);
1102 previous
= free_list_unchecksum_ptr(free_ptr
->previous
);
1105 if (LOG(szone
,ptr
)) {
1106 malloc_printf("In %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
1110 // The block to remove is the head of the free list
1112 if (szone
->tiny_free_list
[slot
] != ptr
) {
1113 szone_error(szone
, "tiny_free_list_remove_ptr: Internal invariant broken (szone->tiny_free_list[slot])", ptr
,
1114 "ptr=%p slot=%d msize=%d szone->tiny_free_list[slot]=%p\n",
1115 ptr
, slot
, msize
, szone
->tiny_free_list
[slot
]);
1119 szone
->tiny_free_list
[slot
] = next
;
1120 if (!next
) BITMAP32_CLR(szone
->tiny_bitmap
, slot
);
1122 // We know free_ptr is already checksummed, so we don't need to do it
1124 previous
->next
= free_ptr
->next
;
1127 // We know free_ptr is already checksummed, so we don't need to do it
1129 next
->previous
= free_ptr
->previous
;
1134 * tiny_region_for_ptr_no_lock - Returns the tiny region containing the pointer,
1135 * or NULL if not found.
1137 static INLINE region_t
*
1138 tiny_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
)
1140 return hash_lookup_region_no_lock(szone
->tiny_regions
,
1141 szone
->num_tiny_regions_allocated
,
1142 TINY_REGION_FOR_PTR(ptr
));
1146 tiny_free_no_lock(szone_t
*szone
, region_t
*region
, void *ptr
, msize_t msize
)
1148 size_t original_size
= TINY_BYTES_FOR_MSIZE(msize
);
1149 void *next_block
= ((char *)ptr
+ original_size
);
1150 msize_t previous_msize
;
1153 free_list_t
*big_free_block
;
1154 free_list_t
*after_next_block
;
1155 free_list_t
*before_next_block
;
1158 if (LOG(szone
,ptr
)) {
1159 malloc_printf("in tiny_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
1162 szone_error(szone
, "trying to free tiny block that is too small", ptr
,
1163 "in tiny_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
1166 // We try to coalesce this block with the preceeding one
1167 previous
= tiny_previous_preceding_free(ptr
, &previous_msize
);
1170 if (LOG(szone
, ptr
) || LOG(szone
,previous
)) {
1171 malloc_printf("in tiny_free_no_lock(), coalesced backwards for %p previous=%p\n", ptr
, previous
);
1174 // clear the meta_header since this is no longer the start of a block
1175 set_tiny_meta_header_middle(ptr
);
1176 tiny_free_list_remove_ptr(szone
, previous
, previous_msize
);
1178 msize
+= previous_msize
;
1180 // We try to coalesce with the next block
1181 if ((next_block
< TINY_REGION_END(*region
)) && tiny_meta_header_is_free(next_block
)) {
1182 next_msize
= get_tiny_free_size(next_block
);
1184 if (LOG(szone
, ptr
) || LOG(szone
, next_block
)) {
1185 malloc_printf("in tiny_free_no_lock(), for ptr=%p, msize=%d coalesced forward=%p next_msize=%d\n",
1186 ptr
, msize
, next_block
, next_msize
);
1189 // If we are coalescing with the next block, and the next block is in
1190 // the last slot of the free list, then we optimize this case here to
1191 // avoid removing next_block from the slot 31 and then adding ptr back
1193 if (next_msize
>= NUM_TINY_SLOTS
) {
1194 msize
+= next_msize
;
1195 big_free_block
= (free_list_t
*)next_block
;
1196 free_list_checksum(szone
, big_free_block
, __PRETTY_FUNCTION__
);
1197 after_next_block
= free_list_unchecksum_ptr(big_free_block
->next
);
1198 before_next_block
= free_list_unchecksum_ptr(big_free_block
->previous
);
1199 if (!before_next_block
) {
1200 szone
->tiny_free_list
[NUM_TINY_SLOTS
-1] = ptr
;
1202 before_next_block
->next
.u
= free_list_checksum_ptr(ptr
);
1204 if (after_next_block
) {
1205 after_next_block
->previous
.u
= free_list_checksum_ptr(ptr
);
1207 // we don't need to checksum these since they are already checksummed
1208 ((free_list_t
*)ptr
)->previous
= big_free_block
->previous
;
1209 ((free_list_t
*)ptr
)->next
= big_free_block
->next
;
1211 // clear the meta_header to enable coalescing backwards
1212 set_tiny_meta_header_middle(big_free_block
);
1213 set_tiny_meta_header_free(ptr
, msize
);
1214 goto tiny_free_ending
;
1216 tiny_free_list_remove_ptr(szone
, next_block
, next_msize
);
1217 set_tiny_meta_header_middle(next_block
); // clear the meta_header to enable coalescing backwards
1218 msize
+= next_msize
;
1221 // The tiny cache already scribbles free blocks as they go through the
1222 // cache, so we do not need to do it here.
1223 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && msize
) {
1224 memset(ptr
, 0x55, TINY_BYTES_FOR_MSIZE(msize
));
1227 tiny_free_list_add_ptr(szone
, ptr
, msize
);
1229 // When in proper debug mode we write on the memory to help debug memory smashers
1230 szone
->num_tiny_objects
--;
1231 szone
->num_bytes_in_tiny_objects
-= original_size
; // we use original_size and not msize to avoid double counting the coalesced blocks
1234 // Allocates from the last region or a freshly allocated region
1236 tiny_malloc_from_region_no_lock(szone_t
*szone
, msize_t msize
)
1238 void *last_block
, *ptr
, *aligned_address
;
1239 unsigned char *last_header
;
1240 msize_t last_msize
, last_index
;
1242 // Before anything we transform any remaining tiny_bytes_free_at_end into a
1243 // regular free block. We take special care here to update the bitfield
1244 // information, since we are bypassing the normal free codepath. If there
1245 // is more than one quanta worth of memory in tiny_bytes_free_at_end, then
1246 // there will be two block headers:
1247 // 1) header for the free space at end, msize = 1
1248 // 2) header inserted by set_tiny_meta_header_in_use after block
1249 // We must clear the second one so that when the free block's size is
1250 // queried, we do not think the block is only 1 quantum in size because
1251 // of the second set header bit.
1252 if (szone
->tiny_bytes_free_at_end
) {
1253 last_block
= TINY_REGION_END(szone
->last_tiny_region
) - szone
->tiny_bytes_free_at_end
;
1254 last_msize
= TINY_MSIZE_FOR_BYTES(szone
->tiny_bytes_free_at_end
);
1255 last_header
= TINY_BLOCK_HEADER_FOR_PTR(last_block
);
1256 last_index
= TINY_INDEX_FOR_PTR(last_block
);
1258 if (last_index
!= (NUM_TINY_BLOCKS
- 1))
1259 BITARRAY_CLR(last_header
, last_index
+ 1);
1261 tiny_free_list_add_ptr(szone
, last_block
, last_msize
);
1262 szone
->tiny_bytes_free_at_end
= 0;
1264 // time to create a new region
1265 aligned_address
= allocate_pages(szone
, TINY_REGION_SIZE
, TINY_BLOCKS_ALIGN
, 0, VM_MEMORY_MALLOC_TINY
);
1266 if (!aligned_address
) // out of memory!
1268 // We set the padding after block_header to be all 1
1269 ((uint32_t *)(aligned_address
+ TINY_HEADER_START
+ (NUM_TINY_BLOCKS
>> 3)))[0] = ~0;
1271 // Check to see if the hash ring of tiny regions needs to grow. Try to
1272 // avoid the hash ring becoming too dense.
1273 if (szone
->num_tiny_regions_allocated
< (2 * szone
->num_tiny_regions
)) {
1274 region_t
*new_regions
;
1276 new_regions
= hash_regions_grow_no_lock(szone
, szone
->tiny_regions
,
1277 szone
->num_tiny_regions_allocated
,
1279 // Do not deallocate the current tiny_regions allocation since someone may
1280 // be iterating it. Instead, just leak it.
1281 szone
->tiny_regions
= new_regions
;
1282 szone
->num_tiny_regions_allocated
= new_size
;
1284 // Insert the new region into the hash ring, and update malloc statistics
1285 hash_region_insert_no_lock(szone
->tiny_regions
,
1286 szone
->num_tiny_regions_allocated
,
1288 szone
->last_tiny_region
= aligned_address
;
1290 szone
->num_tiny_regions
++;
1291 ptr
= aligned_address
;
1292 set_tiny_meta_header_in_use(ptr
, msize
);
1293 szone
->num_tiny_objects
++;
1294 szone
->num_bytes_in_tiny_objects
+= TINY_BYTES_FOR_MSIZE(msize
);
1296 // We put a header on the last block so that it appears in use (for coalescing, etc...)
1297 set_tiny_meta_header_in_use(ptr
+ TINY_BYTES_FOR_MSIZE(msize
), 1);
1298 szone
->tiny_bytes_free_at_end
= TINY_BYTES_FOR_MSIZE(NUM_TINY_BLOCKS
- msize
);
1300 if (LOG(szone
,ptr
)) {
1301 malloc_printf("in tiny_malloc_from_region_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
1307 static INLINE boolean_t
1308 try_realloc_tiny_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
)
1310 // returns 1 on success
1313 unsigned next_index
;
1316 msize_t next_msize
, coalesced_msize
, leftover_msize
;
1319 index
= TINY_INDEX_FOR_PTR(ptr
);
1320 old_msize
= TINY_MSIZE_FOR_BYTES(old_size
);
1321 next_index
= index
+ old_msize
;
1323 if (next_index
>= NUM_TINY_BLOCKS
) {
1326 next_block
= (char *)ptr
+ old_size
;
1328 is_free
= tiny_meta_header_is_free(next_block
);
1330 SZONE_UNLOCK(szone
);
1331 return 0; // next_block is in use;
1333 next_msize
= get_tiny_free_size(next_block
);
1334 if (old_size
+ TINY_MSIZE_FOR_BYTES(next_msize
) < new_size
) {
1335 SZONE_UNLOCK(szone
);
1336 return 0; // even with next block, not enough
1338 tiny_free_list_remove_ptr(szone
, next_block
, next_msize
);
1339 set_tiny_meta_header_middle(next_block
); // clear the meta_header to enable coalescing backwards
1340 coalesced_msize
= TINY_MSIZE_FOR_BYTES(new_size
- old_size
+ TINY_QUANTUM
- 1);
1341 leftover_msize
= next_msize
- coalesced_msize
;
1342 if (leftover_msize
) {
1343 leftover
= next_block
+ TINY_BYTES_FOR_MSIZE(coalesced_msize
);
1344 tiny_free_list_add_ptr(szone
, leftover
, leftover_msize
);
1346 set_tiny_meta_header_in_use(ptr
, old_msize
+ coalesced_msize
);
1348 if (LOG(szone
,ptr
)) {
1349 malloc_printf("in try_realloc_tiny_in_place(), ptr=%p, msize=%d\n", ptr
, old_msize
+ coalesced_msize
);
1352 szone
->num_bytes_in_tiny_objects
+= TINY_BYTES_FOR_MSIZE(coalesced_msize
);
1353 SZONE_UNLOCK(szone
);
1354 CHECK(szone
, __PRETTY_FUNCTION__
);
1359 tiny_check_region(szone_t
*szone
, region_t region
)
1361 uintptr_t start
, ptr
, region_end
;
1362 boolean_t prev_free
= 0;
1365 free_list_t
*free_head
;
1366 void *follower
, *previous
, *next
;
1368 /* establish region limits */
1369 start
= (uintptr_t)TINY_REGION_ADDRESS(region
);
1371 region_end
= (uintptr_t)TINY_REGION_END(region
);
1374 * The last region may have a trailing chunk which has not been converted into inuse/freelist
1377 if (region
== szone
->last_tiny_region
)
1378 region_end
-= szone
->tiny_bytes_free_at_end
;
1382 * Scan blocks within the region.
1384 while (ptr
< region_end
) {
1386 * If the first block is free, and its size is 65536 (msize = 0) then the entire region is
1389 msize
= get_tiny_meta_header((void *)ptr
, &is_free
);
1390 if (is_free
&& !msize
&& (ptr
== start
)) {
1395 * If the block's size is 65536 (msize = 0) then since we're not the first entry the size is
1399 malloc_printf("*** invariant broken for tiny block %p this msize=%d - size is too small\n",
1406 * In use blocks cannot be more than 31 quanta large.
1409 if (msize
> 31 * TINY_QUANTUM
) {
1410 malloc_printf("*** invariant broken for %p this tiny msize=%d[%p] - size is too large\n",
1414 /* move to next block */
1415 ptr
+= TINY_BYTES_FOR_MSIZE(msize
);
1418 * Free blocks must have been coalesced, we cannot have a free block following another
1422 malloc_printf("*** invariant broken for free block %p this tiny msize=%d: two free blocks in a row\n",
1428 * Check the integrity of this block's entry in its freelist.
1430 free_head
= (free_list_t
*)ptr
;
1431 free_list_checksum(szone
, free_head
, __PRETTY_FUNCTION__
);
1432 previous
= free_list_unchecksum_ptr(free_head
->previous
);
1433 next
= free_list_unchecksum_ptr(free_head
->next
);
1434 if (previous
&& !tiny_meta_header_is_free(previous
)) {
1435 malloc_printf("*** invariant broken for %p (previous %p is not a free pointer)\n",
1439 if (next
&& !tiny_meta_header_is_free(next
)) {
1440 malloc_printf("*** invariant broken for %p (next in free list %p is not a free pointer)\n",
1445 * Check the free block's trailing size value.
1447 follower
= FOLLOWING_TINY_PTR(ptr
, msize
);
1448 if (((uintptr_t)follower
!= region_end
) && (get_tiny_previous_free_msize(follower
) != msize
)) {
1449 malloc_printf("*** invariant broken for tiny free %p followed by %p in region [%p-%p] "
1450 "(end marker incorrect) should be %d; in fact %d\n",
1451 ptr
, follower
, TINY_REGION_ADDRESS(region
), region_end
, msize
, get_tiny_previous_free_msize(follower
));
1454 /* move to next block */
1455 ptr
= (uintptr_t)follower
;
1459 * Ensure that we scanned the entire region
1461 if (ptr
!= region_end
) {
1462 malloc_printf("*** invariant broken for region end %p - %p\n", ptr
, region_end
);
1466 * Check the trailing block's integrity.
1468 if (region
== szone
->last_tiny_region
) {
1469 if (szone
->tiny_bytes_free_at_end
) {
1470 msize
= get_tiny_meta_header((void *)ptr
, &is_free
);
1471 if (is_free
|| (msize
!= 1)) {
1472 malloc_printf("*** invariant broken for blocker block %p - %d %d\n", ptr
, msize
, is_free
);
1479 static kern_return_t
1480 tiny_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
, memory_reader_t reader
, vm_range_recorder_t recorder
)
1482 size_t num_regions
= szone
->num_tiny_regions_allocated
;
1483 void *last_tiny_free
= szone
->last_tiny_free
;
1486 vm_range_t buffer
[MAX_RECORDER_BUFFER
];
1491 vm_range_t admin_range
;
1492 vm_range_t ptr_range
;
1493 unsigned char *mapped_region
;
1494 unsigned char *block_header
;
1495 unsigned char *in_use
;
1496 unsigned block_index
;
1497 unsigned block_limit
;
1502 vm_address_t last_tiny_free_ptr
= 0;
1503 msize_t last_tiny_free_msize
= 0;
1505 if (last_tiny_free
) {
1506 last_tiny_free_ptr
= (uintptr_t) last_tiny_free
& ~(TINY_QUANTUM
- 1);
1507 last_tiny_free_msize
= (uintptr_t) last_tiny_free
& (TINY_QUANTUM
- 1);
1510 err
= reader(task
, (vm_address_t
)szone
->tiny_regions
, sizeof(region_t
) * num_regions
, (void **)®ions
);
1511 if (err
) return err
;
1512 for (index
= 0; index
< num_regions
; ++index
) {
1513 region
= regions
[index
];
1515 range
.address
= (vm_address_t
)TINY_REGION_ADDRESS(region
);
1516 range
.size
= (vm_size_t
)TINY_REGION_SIZE
;
1517 if (type_mask
& MALLOC_ADMIN_REGION_RANGE_TYPE
) {
1518 admin_range
.address
= range
.address
+ TINY_HEADER_START
;
1519 admin_range
.size
= TINY_HEADER_SIZE
;
1520 recorder(task
, context
, MALLOC_ADMIN_REGION_RANGE_TYPE
, &admin_range
, 1);
1522 if (type_mask
& (MALLOC_PTR_REGION_RANGE_TYPE
| MALLOC_ADMIN_REGION_RANGE_TYPE
)) {
1523 ptr_range
.address
= range
.address
;
1524 ptr_range
.size
= NUM_TINY_BLOCKS
* TINY_QUANTUM
;
1525 recorder(task
, context
, MALLOC_PTR_REGION_RANGE_TYPE
, &ptr_range
, 1);
1527 if (type_mask
& MALLOC_PTR_IN_USE_RANGE_TYPE
) {
1528 err
= reader(task
, range
.address
, range
.size
, (void **)&mapped_region
);
1532 block_header
= (unsigned char *)(mapped_region
+ TINY_HEADER_START
);
1533 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
1535 block_limit
= NUM_TINY_BLOCKS
;
1536 if (region
== szone
->last_tiny_region
)
1537 block_limit
-= TINY_MSIZE_FOR_BYTES(szone
->tiny_bytes_free_at_end
);
1539 while (block_index
< block_limit
) {
1540 vm_size_t block_offset
= TINY_BYTES_FOR_MSIZE(block_index
);
1541 is_free
= !BITARRAY_BIT(in_use
, block_index
);
1543 mapped_ptr
= mapped_region
+ block_offset
;
1545 // mapped_region, the address at which 'range' in 'task' has been
1546 // mapped into our process, is not necessarily aligned to
1547 // TINY_BLOCKS_ALIGN.
1549 // Since the code in get_tiny_free_size() assumes the pointer came
1550 // from a properly aligned tiny region, and mapped_region is not
1551 // necessarily aligned, then do the size calculation directly.
1552 // If the next bit is set in the header bitmap, then the size is one
1553 // quantum. Otherwise, read the size field.
1554 if (!BITARRAY_BIT(block_header
, block_index
+1))
1555 msize
= TINY_FREE_SIZE(mapped_ptr
);
1561 } else if (range
.address
+ block_offset
!= last_tiny_free_ptr
) {
1563 bit
= block_index
+ 1;
1564 while (! BITARRAY_BIT(block_header
, bit
)) {
1568 buffer
[count
].address
= range
.address
+ block_offset
;
1569 buffer
[count
].size
= TINY_BYTES_FOR_MSIZE(msize
);
1571 if (count
>= MAX_RECORDER_BUFFER
) {
1572 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
1576 // Block is not free but it matches last_tiny_free_ptr so even
1577 // though it is not marked free in the bitmap, we treat it as if
1578 // it is and move on
1579 msize
= last_tiny_free_msize
;
1581 block_index
+= msize
;
1587 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
1593 tiny_malloc_from_free_list(szone_t
*szone
, msize_t msize
)
1595 // Assumes we've locked the region
1598 grain_t slot
= msize
- 1;
1599 free_list_t
**free_list
= szone
->tiny_free_list
;
1600 free_list_t
**the_slot
= free_list
+ slot
;
1602 free_list_t
**limit
;
1604 msize_t leftover_msize
;
1605 free_list_t
*leftover_ptr
;
1608 CHECK_LOCKED(szone
, __PRETTY_FUNCTION__
);
1610 // Look for an exact match by checking the freelist for this msize.
1614 next
= free_list_unchecksum_ptr(ptr
->next
);
1616 next
->previous
= ptr
->previous
;
1618 BITMAP32_CLR(szone
->tiny_bitmap
, slot
);
1623 if (LOG(szone
, ptr
)) {
1624 malloc_printf("in tiny_malloc_from_free_list(), exact match ptr=%p, this_msize=%d\n", ptr
, this_msize
);
1627 goto return_tiny_alloc
;
1630 // Mask off the bits representing slots holding free blocks smaller than the
1631 // size we need. If there are no larger free blocks, try allocating from
1632 // the free space at the end of the tiny region.
1633 bitmap
= szone
->tiny_bitmap
& ~ ((1 << slot
) - 1);
1635 goto try_tiny_malloc_from_end
;
1637 slot
= BITMAP32_CTZ(bitmap
);
1638 limit
= free_list
+ NUM_TINY_SLOTS
- 1;
1641 // Iterate over freelists looking for free blocks, starting at first list
1642 // which is not empty, and contains blocks which are large enough to satisfy
1644 while (free_list
< limit
) {
1647 next
= free_list_unchecksum_ptr(ptr
->next
);
1649 this_msize
= get_tiny_free_size(ptr
);
1651 next
->previous
= ptr
->previous
;
1653 BITMAP32_CLR(szone
->tiny_bitmap
, this_msize
- 1);
1655 goto add_leftover_and_proceed
;
1660 // We are now looking at the last slot, which contains blocks equal to, or
1661 // due to coalescing of free blocks, larger than 31 * tiny quantum size.
1662 // If the last freelist is not empty, and the head contains a block that is
1663 // larger than our request, then the remainder is put back on the free list.
1666 free_list_checksum(szone
, ptr
, __PRETTY_FUNCTION__
);
1667 this_msize
= get_tiny_free_size(ptr
);
1668 next
= free_list_unchecksum_ptr(ptr
->next
);
1669 if (this_msize
- msize
>= NUM_TINY_SLOTS
) {
1670 // the leftover will go back to the free list, so we optimize by
1671 // modifying the free list rather than a pop and push of the head
1672 leftover_msize
= this_msize
- msize
;
1673 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ TINY_BYTES_FOR_MSIZE(msize
));
1674 *limit
= leftover_ptr
;
1676 next
->previous
.u
= free_list_checksum_ptr(leftover_ptr
);
1678 leftover_ptr
->previous
= ptr
->previous
;
1679 leftover_ptr
->next
= ptr
->next
;
1680 set_tiny_meta_header_free(leftover_ptr
, leftover_msize
);
1682 if (LOG(szone
,ptr
)) {
1683 malloc_printf("in tiny_malloc_from_free_list(), last slot ptr=%p, msize=%d this_msize=%d\n", ptr
, msize
, this_msize
);
1687 goto return_tiny_alloc
;
1690 next
->previous
= ptr
->previous
;
1693 goto add_leftover_and_proceed
;
1696 try_tiny_malloc_from_end
:
1697 // Let's see if we can use szone->tiny_bytes_free_at_end
1698 if (szone
->tiny_bytes_free_at_end
>= TINY_BYTES_FOR_MSIZE(msize
)) {
1699 ptr
= (free_list_t
*)(TINY_REGION_END(szone
->last_tiny_region
) - szone
->tiny_bytes_free_at_end
);
1700 szone
->tiny_bytes_free_at_end
-= TINY_BYTES_FOR_MSIZE(msize
);
1701 if (szone
->tiny_bytes_free_at_end
) {
1702 // let's add an in use block after ptr to serve as boundary
1703 set_tiny_meta_header_in_use((unsigned char *)ptr
+ TINY_BYTES_FOR_MSIZE(msize
), 1);
1707 if (LOG(szone
, ptr
)) {
1708 malloc_printf("in tiny_malloc_from_free_list(), from end ptr=%p, msize=%d\n", ptr
, msize
);
1711 goto return_tiny_alloc
;
1715 add_leftover_and_proceed
:
1716 if (!this_msize
|| (this_msize
> msize
)) {
1717 leftover_msize
= this_msize
- msize
;
1718 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ TINY_BYTES_FOR_MSIZE(msize
));
1720 if (LOG(szone
,ptr
)) {
1721 malloc_printf("in tiny_malloc_from_free_list(), adding leftover ptr=%p, this_msize=%d\n", ptr
, this_msize
);
1724 tiny_free_list_add_ptr(szone
, leftover_ptr
, leftover_msize
);
1729 szone
->num_tiny_objects
++;
1730 szone
->num_bytes_in_tiny_objects
+= TINY_BYTES_FOR_MSIZE(this_msize
);
1732 if (LOG(szone
,ptr
)) {
1733 malloc_printf("in tiny_malloc_from_free_list(), ptr=%p, this_msize=%d, msize=%d\n", ptr
, this_msize
, msize
);
1736 set_tiny_meta_header_in_use(ptr
, this_msize
);
1740 static INLINE
void *
1741 tiny_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
)
1743 boolean_t locked
= 0;
1748 szone_error(szone
, "invariant broken (!msize) in allocation (region)", NULL
, NULL
);
1753 ptr
= szone
->last_tiny_free
;
1754 if ((((uintptr_t)ptr
) & (TINY_QUANTUM
- 1)) == msize
) {
1755 // we have a candidate - let's lock to make sure
1756 LOCK_AND_NOTE_LOCKED(szone
, locked
);
1757 if (ptr
== szone
->last_tiny_free
) {
1758 szone
->last_tiny_free
= NULL
;
1759 SZONE_UNLOCK(szone
);
1760 CHECK(szone
, __PRETTY_FUNCTION__
);
1761 ptr
= (void *)((uintptr_t)ptr
& ~ (TINY_QUANTUM
- 1));
1762 if (cleared_requested
) {
1763 memset(ptr
, 0, TINY_BYTES_FOR_MSIZE(msize
));
1766 if (LOG(szone
,ptr
)) {
1767 malloc_printf("in tiny_malloc_should_clear(), tiny cache ptr=%p, msize=%d\n", ptr
, msize
);
1774 // Except in rare occasions where we need to add a new region, we are going to end up locking, so we might as well lock right away to avoid doing unnecessary optimistic probes
1775 if (!locked
) LOCK_AND_NOTE_LOCKED(szone
, locked
);
1776 ptr
= tiny_malloc_from_free_list(szone
, msize
);
1778 SZONE_UNLOCK(szone
);
1779 CHECK(szone
, __PRETTY_FUNCTION__
);
1780 if (cleared_requested
) {
1781 memset(ptr
, 0, TINY_BYTES_FOR_MSIZE(msize
));
1785 ptr
= tiny_malloc_from_region_no_lock(szone
, msize
);
1786 // we don't clear because this freshly allocated space is pristine
1787 SZONE_UNLOCK(szone
);
1788 CHECK(szone
, __PRETTY_FUNCTION__
);
1793 free_tiny(szone_t
*szone
, void *ptr
, region_t
*tiny_region
)
1801 // ptr is known to be in tiny_region
1804 ptr2
= szone
->last_tiny_free
;
1805 /* check that we don't already have this pointer in the cache */
1806 if (ptr
== (void *)((uintptr_t)ptr2
& ~ (TINY_QUANTUM
- 1))) {
1807 szone_error(szone
, "double free", ptr
, NULL
);
1810 #endif /* TINY_CACHE */
1811 msize
= get_tiny_meta_header(ptr
, &is_free
);
1813 szone_error(szone
, "double free", ptr
, NULL
);
1818 malloc_printf("*** szone_free() block in use is too large: %p\n", ptr
);
1823 if (msize
< TINY_QUANTUM
) { // to see if the bits fit in the last 4 bits
1824 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && msize
)
1825 memset(ptr
, 0x55, TINY_BYTES_FOR_MSIZE(msize
));
1826 szone
->last_tiny_free
= (void *)(((uintptr_t)ptr
) | msize
);
1828 SZONE_UNLOCK(szone
);
1829 CHECK(szone
, __PRETTY_FUNCTION__
);
1832 msize
= (uintptr_t)ptr2
& (TINY_QUANTUM
- 1);
1833 ptr
= (void *)(((uintptr_t)ptr2
) & ~(TINY_QUANTUM
- 1));
1834 tiny_region
= tiny_region_for_ptr_no_lock(szone
, ptr
);
1836 szone_error(szone
, "double free (tiny cache)", ptr
, NULL
);
1840 tiny_free_no_lock(szone
, tiny_region
, ptr
, msize
);
1841 SZONE_UNLOCK(szone
);
1842 CHECK(szone
, __PRETTY_FUNCTION__
);
1846 print_tiny_free_list(szone_t
*szone
)
1850 _SIMPLE_STRING b
= _simple_salloc();
1853 _simple_sappend(b
, "tiny free sizes: ");
1854 while (slot
< NUM_TINY_SLOTS
) {
1855 ptr
= szone
->tiny_free_list
[slot
];
1857 _simple_sprintf(b
, "%s%y[%d]; ", (slot
== NUM_TINY_SLOTS
-1) ? ">=" : "", (slot
+1)*TINY_QUANTUM
, free_list_count(ptr
));
1861 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
1867 print_tiny_region(boolean_t verbose
, region_t region
, size_t bytes_at_end
)
1869 unsigned counts
[1024];
1870 unsigned in_use
= 0;
1871 uintptr_t start
= (uintptr_t)TINY_REGION_ADDRESS(region
);
1872 uintptr_t current
= start
;
1873 uintptr_t limit
= (uintptr_t)TINY_REGION_END(region
) - bytes_at_end
;
1879 memset(counts
, 0, 1024 * sizeof(unsigned));
1880 while (current
< limit
) {
1881 msize
= get_tiny_meta_header((void *)current
, &is_free
);
1882 if (is_free
& !msize
&& (current
== start
)) {
1883 // first block is all free
1887 malloc_printf("*** error with %p: msize=%d\n", (void *)current
, (unsigned)msize
);
1893 malloc_printf("*** error at %p msize for in_use is %d\n", (void *)current
, msize
);
1898 current
+= TINY_BYTES_FOR_MSIZE(msize
);
1900 if ((b
= _simple_salloc()) != NULL
) {
1901 _simple_sprintf(b
, "Tiny region [%p-%p, %y]\t", (void *)start
, TINY_REGION_END(region
), (int)TINY_REGION_SIZE
);
1902 _simple_sprintf(b
, "In_use=%d ", in_use
);
1903 if (bytes_at_end
) _simple_sprintf(b
, "untouched=%ly", bytes_at_end
);
1904 if (verbose
&& in_use
) {
1905 _simple_sappend(b
, "\n\tSizes in use: ");
1906 for (ci
= 0; ci
< 1024; ci
++)
1908 _simple_sprintf(b
, "%d[%d]", TINY_BYTES_FOR_MSIZE(ci
), counts
[ci
]);
1910 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
1916 tiny_free_list_check(szone_t
*szone
, grain_t slot
)
1919 free_list_t
*ptr
= szone
->tiny_free_list
[slot
];
1920 free_list_t
*previous
= NULL
;
1923 CHECK_LOCKED(szone
, __PRETTY_FUNCTION__
);
1925 free_list_checksum(szone
, ptr
, __PRETTY_FUNCTION__
);
1926 is_free
= tiny_meta_header_is_free(ptr
);
1928 malloc_printf("*** in-use ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
1931 if (((uintptr_t)ptr
) & (TINY_QUANTUM
- 1)) {
1932 malloc_printf("*** unaligned ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
1935 if (!tiny_region_for_ptr_no_lock(szone
, ptr
)) {
1936 malloc_printf("*** ptr not in szone slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
1939 if (free_list_unchecksum_ptr(ptr
->previous
) != previous
) {
1940 malloc_printf("*** previous incorrectly set slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
1944 ptr
= free_list_unchecksum_ptr(ptr
->next
);
1950 /********************* SMALL FREE LIST UTILITIES ************************/
1953 * Mark a block as free. Only the first quantum of a block is marked thusly,
1954 * the remainder are marked "middle".
1957 small_meta_header_set_is_free(msize_t
*meta_headers
, unsigned index
, msize_t msize
)
1959 meta_headers
[index
] = msize
| SMALL_IS_FREE
;
1963 * Mark a block as in use. Only the first quantum of a block is marked thusly,
1964 * the remainder are marked "middle".
1967 small_meta_header_set_in_use(msize_t
*meta_headers
, msize_t index
, msize_t msize
)
1969 meta_headers
[index
] = msize
;
1973 * Mark a quantum as being the second or later in a block.
1976 small_meta_header_set_middle(msize_t
*meta_headers
, msize_t index
)
1978 meta_headers
[index
] = 0;
1981 // Adds an item to the proper free list
1982 // Also marks the header of the block properly
1983 // Assumes szone has been locked
1985 small_free_list_add_ptr(szone_t
*szone
, void *ptr
, msize_t msize
)
1987 grain_t slot
= (msize
<= NUM_SMALL_SLOTS
) ? msize
- 1 : NUM_SMALL_SLOTS
- 1;
1988 free_list_t
*free_ptr
= ptr
;
1989 free_list_t
*free_head
= szone
->small_free_list
[slot
];
1993 if (LOG(szone
,ptr
)) {
1994 malloc_printf("in %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
1996 if (((uintptr_t)ptr
) & (SMALL_QUANTUM
- 1)) {
1997 szone_error(szone
, "small_free_list_add_ptr: Unaligned ptr", ptr
, NULL
);
2001 free_list_checksum(szone
, free_head
, __PRETTY_FUNCTION__
);
2003 if (free_list_unchecksum_ptr(free_head
->previous
)) {
2004 szone_error(szone
, "small_free_list_add_ptr: Internal invariant broken (free_head->previous)", ptr
,
2005 "ptr=%p slot=%d free_head=%p previous=%p\n", ptr
, slot
, free_head
, free_head
->previous
.p
);
2007 if (!SMALL_PTR_IS_FREE(free_head
)) {
2008 szone_error(szone
, "small_free_list_add_ptr: Internal invariant broken (free_head is not a free pointer)", ptr
,
2009 "ptr=%p slot=%d free_head=%p\n", ptr
, slot
, free_head
);
2012 free_head
->previous
.u
= free_list_checksum_ptr(free_ptr
);
2014 BITMAP32_SET(szone
->small_bitmap
, slot
);
2016 free_ptr
->previous
.p
= NULL
;
2017 free_ptr
->next
.p
= free_head
;
2018 free_list_set_checksum(szone
, free_ptr
);
2019 szone
->small_free_list
[slot
] = free_ptr
;
2020 follower
= ptr
+ SMALL_BYTES_FOR_MSIZE(msize
);
2021 SMALL_PREVIOUS_MSIZE(follower
) = msize
;
2024 // Removes item in the proper free list
2025 // msize could be read, but all callers have it so we pass it in
2026 // Assumes szone has been locked
2028 small_free_list_remove_ptr(szone_t
*szone
, void *ptr
, msize_t msize
)
2030 grain_t slot
= (msize
<= NUM_SMALL_SLOTS
) ? msize
- 1 : NUM_SMALL_SLOTS
- 1;
2031 free_list_t
*free_ptr
= ptr
, *next
, *previous
;
2032 free_list_checksum(szone
, free_ptr
, __PRETTY_FUNCTION__
);
2034 next
= free_list_unchecksum_ptr(free_ptr
->next
);
2035 previous
= free_list_unchecksum_ptr(free_ptr
->previous
);
2038 if (LOG(szone
,ptr
)) {
2039 malloc_printf("In %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
2043 // The block to remove is the head of the free list
2045 if (szone
->small_free_list
[slot
] != ptr
) {
2046 szone_error(szone
, "small_free_list_remove_ptr: Internal invariant broken (szone->small_free_list[grain])", ptr
,
2047 "ptr=%p slot=%d msize=%d szone->small_free_list[slot]=%p\n",
2048 ptr
, slot
, msize
, szone
->small_free_list
[slot
]);
2052 szone
->small_free_list
[slot
] = next
;
2053 if (!next
) BITMAP32_CLR(szone
->small_bitmap
, slot
);
2055 // We know free_ptr is already checksummed, so we don't need to do it
2057 previous
->next
= free_ptr
->next
;
2060 // We know free_ptr is already checksummed, so we don't need to do it
2062 next
->previous
= free_ptr
->previous
;
2066 static INLINE region_t
*
2067 small_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
)
2069 return hash_lookup_region_no_lock(szone
->small_regions
,
2070 szone
->num_small_regions_allocated
,
2071 SMALL_REGION_FOR_PTR(ptr
));
2075 small_free_no_lock(szone_t
*szone
, region_t
*region
, void *ptr
, msize_t msize
)
2077 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
2078 unsigned index
= SMALL_META_INDEX_FOR_PTR(ptr
);
2079 size_t original_size
= SMALL_BYTES_FOR_MSIZE(msize
);
2080 unsigned char *next_block
= ((unsigned char *)ptr
+ original_size
);
2081 msize_t next_index
= index
+ msize
;
2082 msize_t previous_msize
, next_msize
;
2086 CHECK_LOCKED(szone
, __PRETTY_FUNCTION__
);
2088 if (LOG(szone
,ptr
)) {
2089 malloc_printf("in small_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
2092 szone_error(szone
, "trying to free small block that is too small", ptr
,
2093 "in small_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
2096 // We try to coalesce this block with the preceeding one
2097 if (index
&& (SMALL_PREVIOUS_MSIZE(ptr
) <= index
)) {
2098 previous_msize
= SMALL_PREVIOUS_MSIZE(ptr
);
2099 if (meta_headers
[index
- previous_msize
] == (previous_msize
| SMALL_IS_FREE
)) {
2100 previous
= ptr
- SMALL_BYTES_FOR_MSIZE(previous_msize
);
2101 // previous is really to be coalesced
2103 if (LOG(szone
, ptr
) || LOG(szone
,previous
)) {
2104 malloc_printf("in small_free_no_lock(), coalesced backwards for %p previous=%p\n", ptr
, previous
);
2107 small_free_list_remove_ptr(szone
, previous
, previous_msize
);
2108 small_meta_header_set_middle(meta_headers
, index
);
2110 msize
+= previous_msize
;
2111 index
-= previous_msize
;
2114 // We try to coalesce with the next block
2115 if ((next_block
< SMALL_REGION_END(*region
)) && (meta_headers
[next_index
] & SMALL_IS_FREE
)) {
2116 // next block is free, we coalesce
2117 next_msize
= meta_headers
[next_index
] & ~ SMALL_IS_FREE
;
2119 if (LOG(szone
,ptr
)) malloc_printf("In small_free_no_lock(), for ptr=%p, msize=%d coalesced next block=%p next_msize=%d\n", ptr
, msize
, next_block
, next_msize
);
2121 small_free_list_remove_ptr(szone
, next_block
, next_msize
);
2122 small_meta_header_set_middle(meta_headers
, next_index
);
2123 msize
+= next_msize
;
2125 if (szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) {
2127 szone_error(szone
, "incorrect size information - block header was damaged", ptr
, NULL
);
2129 memset(ptr
, 0x55, SMALL_BYTES_FOR_MSIZE(msize
));
2132 small_free_list_add_ptr(szone
, ptr
, msize
);
2133 small_meta_header_set_is_free(meta_headers
, index
, msize
);
2134 szone
->num_small_objects
--;
2135 szone
->num_bytes_in_small_objects
-= original_size
; // we use original_size and not msize to avoid double counting the coalesced blocks
2139 small_malloc_from_region_no_lock(szone_t
*szone
, msize_t msize
)
2144 msize_t
*meta_headers
;
2148 // Allocates from the last region or a freshly allocated region
2149 CHECK_LOCKED(szone
, __PRETTY_FUNCTION__
);
2150 // Before anything we transform the small_bytes_free_at_end - if any - to a regular free block
2151 if (szone
->small_bytes_free_at_end
) {
2152 last_block
= (void *)(SMALL_REGION_END(szone
->last_small_region
) - szone
->small_bytes_free_at_end
);
2153 small_free_list_add_ptr(szone
, last_block
, SMALL_MSIZE_FOR_BYTES(szone
->small_bytes_free_at_end
));
2154 *SMALL_METADATA_FOR_PTR(last_block
) = SMALL_MSIZE_FOR_BYTES(szone
->small_bytes_free_at_end
) | SMALL_IS_FREE
;
2155 szone
->small_bytes_free_at_end
= 0;
2157 // time to create a new region
2158 new_address
= allocate_pages(szone
, SMALL_REGION_SIZE
, SMALL_BLOCKS_ALIGN
,
2159 0, VM_MEMORY_MALLOC_SMALL
);
2164 meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
2167 // Check to see if the hash ring of small regions needs to grow. Try to
2168 // avoid the hash ring becoming too dense.
2169 if (szone
->num_small_regions_allocated
< (2 * szone
->num_small_regions
)) {
2170 region_t
*new_regions
;
2172 new_regions
= hash_regions_grow_no_lock(szone
, szone
->small_regions
,
2173 szone
->num_small_regions_allocated
,
2175 // Do not deallocate the current small_regions allocation since someone
2176 // may be iterating it. Instead, just leak it.
2177 szone
->small_regions
= new_regions
;
2178 szone
->num_small_regions_allocated
= new_size
;
2180 // Insert the new region into the hash ring, and update malloc statistics
2181 hash_region_insert_no_lock(szone
->small_regions
,
2182 szone
->num_small_regions_allocated
,
2184 szone
->last_small_region
= new_address
;
2186 // we bump the number of regions AFTER we have changes the regions pointer
2187 // to enable finding a small region without taking the lock
2189 // FIXME: naive assumption assumes memory ordering coherence between this
2190 // and other CPUs. This also applies to the near-identical code in
2191 // tiny_malloc_from_region_no_lock.
2192 szone
->num_small_regions
++;
2193 small_meta_header_set_in_use(meta_headers
, index
, msize
);
2194 msize_left
= NUM_SMALL_BLOCKS
- index
;
2195 szone
->num_small_objects
++;
2196 szone
->num_bytes_in_small_objects
+= SMALL_BYTES_FOR_MSIZE(msize
);
2197 // add a big free block
2198 index
+= msize
; msize_left
-= msize
;
2199 meta_headers
[index
] = msize_left
;
2200 szone
->small_bytes_free_at_end
= SMALL_BYTES_FOR_MSIZE(msize_left
);
2204 static INLINE boolean_t
2205 try_realloc_small_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
)
2207 // returns 1 on success
2208 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
2209 unsigned index
= SMALL_META_INDEX_FOR_PTR(ptr
);
2210 msize_t old_msize
= SMALL_MSIZE_FOR_BYTES(old_size
);
2211 msize_t new_msize
= SMALL_MSIZE_FOR_BYTES(new_size
+ SMALL_QUANTUM
- 1);
2212 void *next_block
= (char *)ptr
+ old_size
;
2213 unsigned next_index
= index
+ old_msize
;
2214 msize_t next_msize_and_free
;
2216 msize_t leftover_msize
;
2218 unsigned leftover_index
;
2220 if (next_index
>= NUM_SMALL_BLOCKS
) {
2224 if ((uintptr_t)next_block
& (SMALL_QUANTUM
- 1)) {
2225 szone_error(szone
, "internal invariant broken in realloc(next_block)", next_block
, NULL
);
2227 if (meta_headers
[index
] != old_msize
)
2228 malloc_printf("*** try_realloc_small_in_place incorrect old %d %d\n",
2229 meta_headers
[index
], old_msize
);
2233 * Look for a free block immediately afterwards. If it's large enough, we can consume (part of)
2236 next_msize_and_free
= meta_headers
[next_index
];
2237 next_msize
= next_msize_and_free
& ~ SMALL_IS_FREE
;
2238 if (!(next_msize_and_free
& SMALL_IS_FREE
) || (old_msize
+ next_msize
< new_msize
)) {
2239 SZONE_UNLOCK(szone
);
2243 * The following block is big enough; pull it from its freelist and chop off enough to satisfy
2246 small_free_list_remove_ptr(szone
, next_block
, next_msize
);
2247 small_meta_header_set_middle(meta_headers
, next_index
);
2248 leftover_msize
= old_msize
+ next_msize
- new_msize
;
2249 if (leftover_msize
) {
2250 /* there's some left, so put the remainder back */
2251 leftover
= (unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(new_msize
);
2252 small_free_list_add_ptr(szone
, leftover
, leftover_msize
);
2253 leftover_index
= index
+ new_msize
;
2254 small_meta_header_set_is_free(meta_headers
, leftover_index
, leftover_msize
);
2257 if (SMALL_BYTES_FOR_MSIZE(new_msize
) >= LARGE_THRESHOLD
) {
2258 malloc_printf("*** realloc in place for %p exceeded msize=%d\n", new_msize
);
2261 small_meta_header_set_in_use(meta_headers
, index
, new_msize
);
2263 if (LOG(szone
,ptr
)) {
2264 malloc_printf("in szone_realloc(), ptr=%p, msize=%d\n", ptr
, *SMALL_METADATA_FOR_PTR(ptr
));
2267 szone
->num_bytes_in_small_objects
+= SMALL_BYTES_FOR_MSIZE(new_msize
- old_msize
);
2268 SZONE_UNLOCK(szone
);
2269 CHECK(szone
, __PRETTY_FUNCTION__
);
2274 szone_check_small_region(szone_t
*szone
, region_t region
)
2276 unsigned char *ptr
= SMALL_REGION_ADDRESS(region
);
2277 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
2278 unsigned char *region_end
= SMALL_REGION_END(region
);
2279 msize_t prev_free
= 0;
2281 msize_t msize_and_free
;
2283 free_list_t
*free_head
;
2284 void *previous
, *next
;
2287 CHECK_LOCKED(szone
, __PRETTY_FUNCTION__
);
2288 if (region
== szone
->last_small_region
) region_end
-= szone
->small_bytes_free_at_end
;
2289 while (ptr
< region_end
) {
2290 index
= SMALL_META_INDEX_FOR_PTR(ptr
);
2291 msize_and_free
= meta_headers
[index
];
2292 if (!(msize_and_free
& SMALL_IS_FREE
)) {
2294 msize
= msize_and_free
;
2296 malloc_printf("*** invariant broken: null msize ptr=%p num_small_regions=%d end=%p\n",
2297 ptr
, szone
->num_small_regions
, region_end
);
2300 if (msize
> (LARGE_THRESHOLD
/ SMALL_QUANTUM
)) {
2301 malloc_printf("*** invariant broken for %p this small msize=%d - size is too large\n",
2302 ptr
, msize_and_free
);
2305 ptr
+= SMALL_BYTES_FOR_MSIZE(msize
);
2309 msize
= msize_and_free
& ~ SMALL_IS_FREE
;
2310 free_head
= (free_list_t
*)ptr
;
2311 follower
= (msize_t
*)FOLLOWING_SMALL_PTR(ptr
, msize
);
2313 malloc_printf("*** invariant broken for free block %p this msize=%d\n", ptr
, msize
);
2317 malloc_printf("*** invariant broken for %p (2 free in a row)\n", ptr
);
2320 free_list_checksum(szone
, free_head
, __PRETTY_FUNCTION__
);
2321 previous
= free_list_unchecksum_ptr(free_head
->previous
);
2322 next
= free_list_unchecksum_ptr(free_head
->next
);
2323 if (previous
&& !SMALL_PTR_IS_FREE(previous
)) {
2324 malloc_printf("*** invariant broken for %p (previous %p is not a free pointer)\n",
2325 ptr
, free_head
->previous
);
2328 if (next
&& !SMALL_PTR_IS_FREE(next
)) {
2329 malloc_printf("*** invariant broken for %p (next is not a free pointer)\n", ptr
);
2332 if (SMALL_PREVIOUS_MSIZE(follower
) != msize
) {
2333 malloc_printf("*** invariant broken for small free %p followed by %p in region [%p-%p] "
2334 "(end marker incorrect) should be %d; in fact %d\n",
2335 ptr
, follower
, SMALL_REGION_ADDRESS(region
), region_end
, msize
, SMALL_PREVIOUS_MSIZE(follower
));
2338 ptr
= (unsigned char *)follower
;
2339 prev_free
= SMALL_IS_FREE
;
2345 static kern_return_t
2346 small_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
, memory_reader_t reader
, vm_range_recorder_t recorder
)
2348 size_t num_regions
= szone
->num_small_regions_allocated
;
2349 void *last_small_free
= szone
->last_small_free
;
2352 vm_range_t buffer
[MAX_RECORDER_BUFFER
];
2357 vm_range_t admin_range
;
2358 vm_range_t ptr_range
;
2359 unsigned char *mapped_region
;
2360 msize_t
*block_header
;
2361 unsigned block_index
;
2362 unsigned block_limit
;
2363 msize_t msize_and_free
;
2365 vm_address_t last_small_free_ptr
= 0;
2366 msize_t last_small_free_msize
= 0;
2368 if (last_small_free
) {
2369 last_small_free_ptr
= (uintptr_t)last_small_free
& ~(SMALL_QUANTUM
- 1);
2370 last_small_free_msize
= (uintptr_t)last_small_free
& (SMALL_QUANTUM
- 1);
2373 err
= reader(task
, (vm_address_t
)szone
->small_regions
, sizeof(region_t
) * num_regions
, (void **)®ions
);
2374 if (err
) return err
;
2375 for (index
= 0; index
< num_regions
; ++index
) {
2376 region
= regions
[index
];
2378 range
.address
= (vm_address_t
)SMALL_REGION_ADDRESS(region
);
2379 range
.size
= SMALL_REGION_SIZE
;
2380 if (type_mask
& MALLOC_ADMIN_REGION_RANGE_TYPE
) {
2381 admin_range
.address
= range
.address
+ SMALL_HEADER_START
;
2382 admin_range
.size
= SMALL_ARRAY_SIZE
;
2383 recorder(task
, context
, MALLOC_ADMIN_REGION_RANGE_TYPE
, &admin_range
, 1);
2385 if (type_mask
& (MALLOC_PTR_REGION_RANGE_TYPE
| MALLOC_ADMIN_REGION_RANGE_TYPE
)) {
2386 ptr_range
.address
= range
.address
;
2387 ptr_range
.size
= NUM_SMALL_BLOCKS
* SMALL_QUANTUM
;
2388 recorder(task
, context
, MALLOC_PTR_REGION_RANGE_TYPE
, &ptr_range
, 1);
2390 if (type_mask
& MALLOC_PTR_IN_USE_RANGE_TYPE
) {
2391 err
= reader(task
, range
.address
, range
.size
, (void **)&mapped_region
);
2392 if (err
) return err
;
2393 block_header
= (msize_t
*)(mapped_region
+ SMALL_HEADER_START
);
2395 block_limit
= NUM_SMALL_BLOCKS
;
2396 if (region
== szone
->last_small_region
)
2397 block_limit
-= SMALL_MSIZE_FOR_BYTES(szone
->small_bytes_free_at_end
);
2398 while (block_index
< block_limit
) {
2399 msize_and_free
= block_header
[block_index
];
2400 msize
= msize_and_free
& ~ SMALL_IS_FREE
;
2401 if (! (msize_and_free
& SMALL_IS_FREE
) &&
2402 range
.address
+ SMALL_BYTES_FOR_MSIZE(block_index
) != last_small_free_ptr
) {
2404 buffer
[count
].address
= range
.address
+ SMALL_BYTES_FOR_MSIZE(block_index
);
2405 buffer
[count
].size
= SMALL_BYTES_FOR_MSIZE(msize
);
2407 if (count
>= MAX_RECORDER_BUFFER
) {
2408 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
2412 block_index
+= msize
;
2418 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
2424 small_malloc_from_free_list(szone_t
*szone
, msize_t msize
)
2428 grain_t slot
= (msize
<= NUM_SMALL_SLOTS
) ? msize
- 1 : NUM_SMALL_SLOTS
- 1;
2429 free_list_t
**free_list
= szone
->small_free_list
;
2431 free_list_t
**limit
;
2432 unsigned bitmap
= szone
->small_bitmap
& ~ ((1 << slot
) - 1);
2433 msize_t leftover_msize
;
2434 free_list_t
*leftover_ptr
;
2435 msize_t
*meta_headers
;
2436 unsigned leftover_index
;
2439 CHECK_LOCKED(szone
, __PRETTY_FUNCTION__
);
2441 // Mask off the bits representing slots holding free blocks smaller than the
2442 // size we need. If there are no larger free blocks, try allocating from
2443 // the free space at the end of the tiny region.
2445 goto try_small_from_end
;
2447 slot
= BITMAP32_CTZ(bitmap
);
2448 limit
= free_list
+ NUM_SMALL_SLOTS
- 1;
2451 // Iterate over freelists looking for free blocks, starting at first list
2452 // which is not empty, and contains blocks which are large enough to satisfy
2454 while (free_list
< limit
) {
2457 next
= free_list_unchecksum_ptr(ptr
->next
);
2459 this_msize
= SMALL_PTR_SIZE(ptr
);
2461 next
->previous
= ptr
->previous
;
2463 BITMAP32_CLR(szone
->small_bitmap
, this_msize
- 1);
2465 goto add_leftover_and_proceed
;
2470 // We are now looking at the last slot, which contains blocks equal to, or
2471 // due to coalescing of free blocks, larger than 31 * small quantum size.
2472 // If the last freelist is not empty, and the head contains a block that is
2473 // larger than our request, then the remainder is put back on the free list.
2475 // FIXME: This code doesn't have the optimization from the 'tiny' codepath
2476 // that optimizes for the this_msize >= 2 * num slots
2477 // FIXME: this code also seems somewhat bogus. There's a check for
2478 // this_msize >= msize, but by definition we can't ask for a small
2479 // block larger than 31 small quanta, and every free block in this
2480 // slot has to be at least that large.
2483 free_list_checksum(szone
, ptr
, __PRETTY_FUNCTION__
);
2484 next
= free_list_unchecksum_ptr(ptr
->next
);
2485 this_msize
= SMALL_PTR_SIZE(ptr
);
2486 if (this_msize
>= msize
) {
2487 small_free_list_remove_ptr(szone
, ptr
, this_msize
);
2488 goto add_leftover_and_proceed
;
2494 // Let's see if we can use szone->small_bytes_free_at_end
2495 if (szone
->small_bytes_free_at_end
>= SMALL_BYTES_FOR_MSIZE(msize
)) {
2496 ptr
= (free_list_t
*)(SMALL_REGION_END(szone
->last_small_region
) - szone
->small_bytes_free_at_end
);
2497 szone
->small_bytes_free_at_end
-= SMALL_BYTES_FOR_MSIZE(msize
);
2498 if (szone
->small_bytes_free_at_end
) {
2499 // let's mark this block as in use to serve as boundary
2500 *SMALL_METADATA_FOR_PTR((unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
)) = SMALL_MSIZE_FOR_BYTES(szone
->small_bytes_free_at_end
);
2503 goto return_small_alloc
;
2507 add_leftover_and_proceed
:
2508 if (this_msize
> msize
) {
2509 leftover_msize
= this_msize
- msize
;
2510 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
));
2512 if (LOG(szone
,ptr
)) {
2513 malloc_printf("in small_malloc_from_free_list(), adding leftover ptr=%p, this_msize=%d\n", ptr
, this_msize
);
2516 small_free_list_add_ptr(szone
, leftover_ptr
, leftover_msize
);
2517 meta_headers
= SMALL_META_HEADER_FOR_PTR(leftover_ptr
);
2518 leftover_index
= SMALL_META_INDEX_FOR_PTR(leftover_ptr
);
2519 small_meta_header_set_is_free(meta_headers
, leftover_index
, leftover_msize
);
2524 szone
->num_small_objects
++;
2525 szone
->num_bytes_in_small_objects
+= SMALL_BYTES_FOR_MSIZE(this_msize
);
2527 if (LOG(szone
,ptr
)) {
2528 malloc_printf("in small_malloc_from_free_list(), ptr=%p, this_msize=%d, msize=%d\n", ptr
, this_msize
, msize
);
2531 *SMALL_METADATA_FOR_PTR(ptr
) = this_msize
;
2535 static INLINE
void *
2536 small_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
)
2538 boolean_t locked
= 0;
2544 ptr
= (void *)szone
->last_small_free
;
2545 if ((((uintptr_t)ptr
) & (SMALL_QUANTUM
- 1)) == msize
) {
2546 // we have a candidate - let's lock to make sure
2547 LOCK_AND_NOTE_LOCKED(szone
, locked
);
2548 if (ptr
== (void *)szone
->last_small_free
) {
2549 szone
->last_small_free
= NULL
;
2550 SZONE_UNLOCK(szone
);
2551 CHECK(szone
, __PRETTY_FUNCTION__
);
2552 ptr
= (void *)((uintptr_t)ptr
& ~ (SMALL_QUANTUM
- 1));
2553 if (cleared_requested
) {
2554 memset(ptr
, 0, SMALL_BYTES_FOR_MSIZE(msize
));
2560 // Except in rare occasions where we need to add a new region, we are going to end up locking,
2561 // so we might as well lock right away to avoid doing unnecessary optimistic probes
2562 if (!locked
) LOCK_AND_NOTE_LOCKED(szone
, locked
);
2563 ptr
= small_malloc_from_free_list(szone
, msize
);
2565 SZONE_UNLOCK(szone
);
2566 CHECK(szone
, __PRETTY_FUNCTION__
);
2567 if (cleared_requested
) {
2568 memset(ptr
, 0, SMALL_BYTES_FOR_MSIZE(msize
));
2572 ptr
= small_malloc_from_region_no_lock(szone
, msize
);
2573 // we don't clear because this freshly allocated space is pristine
2574 SZONE_UNLOCK(szone
);
2575 CHECK(szone
, __PRETTY_FUNCTION__
);
2579 // tries to allocate a small, cleared block
2580 static INLINE
void *
2581 small_malloc_cleared_no_lock(szone_t
*szone
, msize_t msize
)
2585 // Assumes already locked
2586 CHECK_LOCKED(szone
, __PRETTY_FUNCTION__
);
2587 ptr
= small_malloc_from_free_list(szone
, msize
);
2589 memset(ptr
, 0, SMALL_BYTES_FOR_MSIZE(msize
));
2592 ptr
= small_malloc_from_region_no_lock(szone
, msize
);
2593 // we don't clear because this freshly allocated space is pristine
2599 free_small(szone_t
*szone
, void *ptr
, region_t
*small_region
)
2601 msize_t msize
= SMALL_PTR_SIZE(ptr
);
2606 // ptr is known to be in small_region
2609 ptr2
= szone
->last_small_free
;
2610 /* check that we don't already have this pointer in the cache */
2611 if (ptr
== (void *)((uintptr_t)ptr2
& ~ (SMALL_QUANTUM
- 1))) {
2612 szone_error(szone
, "double free", ptr
, NULL
);
2616 if (SMALL_PTR_IS_FREE(ptr
)) {
2617 szone_error(szone
, "double free", ptr
, NULL
);
2621 szone
->last_small_free
= (void *)(((uintptr_t)ptr
) | msize
);
2623 SZONE_UNLOCK(szone
);
2624 CHECK(szone
, __PRETTY_FUNCTION__
);
2627 msize
= (uintptr_t)ptr2
& (SMALL_QUANTUM
- 1);
2628 ptr
= (void *)(((uintptr_t)ptr2
) & ~ (SMALL_QUANTUM
- 1));
2629 small_region
= small_region_for_ptr_no_lock(szone
, ptr
);
2630 if (!small_region
) {
2631 szone_error(szone
, "double free (small cache)", ptr
, NULL
);
2635 small_free_no_lock(szone
, small_region
, ptr
, msize
);
2636 SZONE_UNLOCK(szone
);
2637 CHECK(szone
, __PRETTY_FUNCTION__
);
2641 print_small_free_list(szone_t
*szone
)
2645 _SIMPLE_STRING b
= _simple_salloc();
2648 _simple_sappend(b
, "small free sizes: ");
2649 while (grain
< NUM_SMALL_SLOTS
) {
2650 ptr
= szone
->small_free_list
[grain
];
2652 _simple_sprintf(b
, "%s%y[%d]; ", (grain
== NUM_SMALL_SLOTS
-1) ? ">=" : "", (grain
+ 1) * SMALL_QUANTUM
, free_list_count(ptr
));
2656 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
2662 print_small_region(szone_t
*szone
, boolean_t verbose
, region_t region
, size_t bytes_at_end
)
2664 unsigned counts
[1024];
2665 unsigned in_use
= 0;
2666 void *start
= SMALL_REGION_ADDRESS(region
);
2667 void *limit
= SMALL_REGION_END(region
) - bytes_at_end
;
2668 msize_t msize_and_free
;
2673 memset(counts
, 0, 1024 * sizeof(unsigned));
2674 while (start
< limit
) {
2675 msize_and_free
= *SMALL_METADATA_FOR_PTR(start
);
2676 msize
= msize_and_free
& ~ SMALL_IS_FREE
;
2677 if (!(msize_and_free
& SMALL_IS_FREE
)) {
2683 start
+= SMALL_BYTES_FOR_MSIZE(msize
);
2685 if ((b
= _simple_salloc()) != NULL
) {
2686 _simple_sprintf(b
, "Small region [%p-%p, %y]\tIn_use=%d ",
2687 SMALL_REGION_ADDRESS(region
), SMALL_REGION_END(region
), (int)SMALL_REGION_SIZE
, in_use
);
2689 _simple_sprintf(b
, "Untouched=%ly", bytes_at_end
);
2690 if (verbose
&& in_use
) {
2691 _simple_sappend(b
, "\n\tSizes in use: ");
2692 for (ci
= 0; ci
< 1024; ci
++)
2694 _simple_sprintf(b
, "%d[%d] ", SMALL_BYTES_FOR_MSIZE(ci
), counts
[ci
]);
2696 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
2702 small_free_list_check(szone_t
*szone
, grain_t grain
)
2705 free_list_t
*ptr
= szone
->small_free_list
[grain
];
2706 free_list_t
*previous
= NULL
;
2707 msize_t msize_and_free
;
2709 CHECK_LOCKED(szone
, __PRETTY_FUNCTION__
);
2711 msize_and_free
= *SMALL_METADATA_FOR_PTR(ptr
);
2713 if (!(msize_and_free
& SMALL_IS_FREE
)) {
2714 malloc_printf("*** in-use ptr in free list grain=%d count=%d ptr=%p\n", grain
, count
, ptr
);
2717 if (((uintptr_t)ptr
) & (SMALL_QUANTUM
- 1)) {
2718 malloc_printf("*** unaligned ptr in free list grain=%d count=%d ptr=%p\n", grain
, count
, ptr
);
2721 if (!small_region_for_ptr_no_lock(szone
, ptr
)) {
2722 malloc_printf("*** ptr not in szone grain=%d count=%d ptr=%p\n", grain
, count
, ptr
);
2725 free_list_checksum(szone
, ptr
, __PRETTY_FUNCTION__
);
2726 if (free_list_unchecksum_ptr(ptr
->previous
) != previous
) {
2727 malloc_printf("*** previous incorrectly set grain=%d count=%d ptr=%p\n", grain
, count
, ptr
);
2731 ptr
= free_list_unchecksum_ptr(ptr
->next
);
2736 /*******************************************************************************
2737 * Region hash implementation
2739 * This is essentially a duplicate of the existing Large allocator hash, minus
2740 * the ability to remove entries. The two should be combined eventually.
2741 ******************************************************************************/
2742 #pragma mark region hash
2745 * hash_lookup_region_no_lock - Scan a hash ring looking for an entry for a
2748 * FIXME: If consecutive queries of the same region are likely, a one-entry
2749 * cache would likely be a significant performance win here.
2752 hash_lookup_region_no_lock(region_t
*regions
, size_t num_entries
, region_t r
) {
2753 size_t index
, hash_index
;
2759 index
= hash_index
= ((uintptr_t)r
>> 20) % num_entries
;
2761 entry
= regions
+ index
;
2766 if (++index
== num_entries
)
2768 } while (index
!= hash_index
);
2773 * hash_region_insert_no_lock - Insert a region into the hash ring.
2776 hash_region_insert_no_lock(region_t
*regions
, size_t num_entries
, region_t r
) {
2777 size_t index
, hash_index
;
2780 index
= hash_index
= ((uintptr_t)r
>> 20) % num_entries
;
2782 entry
= regions
+ index
;
2787 if (++index
== num_entries
)
2789 } while (index
!= hash_index
);
2793 * hash_regions_alloc_no_lock - Allocate space for a number of entries. This
2794 * must be a VM allocation as to avoid recursing between allocating a new small
2795 * region, and asking the small region to allocate space for the new list of
2799 hash_regions_alloc_no_lock(szone_t
*szone
, size_t num_entries
)
2801 size_t size
= num_entries
* sizeof(region_t
);
2802 return allocate_pages(szone
, round_page(size
), 0, 0, VM_MEMORY_MALLOC
);
2806 * hash_regions_grow_no_lock - Grow the hash ring, and rehash the entries.
2807 * Return the new region and new size to update the szone. Do not deallocate
2808 * the old entries since someone may still be allocating them.
2811 hash_regions_grow_no_lock(szone_t
*szone
, region_t
*regions
, size_t old_size
,
2814 // double in size and allocate memory for the regions
2815 *new_size
= old_size
* 2 + 1;
2816 region_t
*new_regions
= hash_regions_alloc_no_lock(szone
, *new_size
);
2818 // rehash the entries into the new list
2820 for (index
= 0; index
< old_size
; ++index
) {
2821 region_t r
= regions
[index
];
2823 hash_region_insert_no_lock(new_regions
, *new_size
, r
);
2828 /*******************************************************************************
2829 * Large allocator implementation
2830 ******************************************************************************/
2831 #pragma mark large allocator
2836 large_debug_print(szone_t
*szone
)
2838 unsigned num_large_entries
= szone
->num_large_entries
;
2839 unsigned index
= num_large_entries
;
2840 large_entry_t
*range
;
2841 _SIMPLE_STRING b
= _simple_salloc();
2844 for (index
= 0, range
= szone
->large_entries
; index
< szone
->num_large_entries
; index
++, range
++)
2845 if (!LARGE_ENTRY_IS_EMPTY(*range
))
2846 _simple_sprintf(b
, "%d: %p(%y); ", index
, LARGE_ENTRY_ADDRESS(*range
), LARGE_ENTRY_SIZE(*range
));
2848 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
2855 * Scan the hash ring looking for an entry for the given pointer.
2857 static large_entry_t
*
2858 large_entry_for_pointer_no_lock(szone_t
*szone
, const void *ptr
)
2860 // result only valid with lock held
2861 unsigned num_large_entries
= szone
->num_large_entries
;
2862 unsigned hash_index
;
2864 large_entry_t
*range
;
2866 if (!num_large_entries
)
2868 hash_index
= ((uintptr_t)ptr
>> vm_page_shift
) % num_large_entries
;
2871 range
= szone
->large_entries
+ index
;
2872 if (LARGE_ENTRY_MATCHES(*range
, ptr
))
2874 if (LARGE_ENTRY_IS_EMPTY(*range
))
2875 return NULL
; // end of chain
2877 if (index
== num_large_entries
)
2879 } while (index
!= hash_index
);
2884 large_entry_insert_no_lock(szone_t
*szone
, large_entry_t range
)
2886 unsigned num_large_entries
= szone
->num_large_entries
;
2887 unsigned hash_index
= (range
.address_and_num_pages
>> vm_page_shift
) % num_large_entries
;
2888 unsigned index
= hash_index
;
2889 large_entry_t
*entry
;
2892 entry
= szone
->large_entries
+ index
;
2893 if (LARGE_ENTRY_IS_EMPTY(*entry
)) {
2895 return; // end of chain
2898 if (index
== num_large_entries
)
2900 } while (index
!= hash_index
);
2904 large_entries_rehash_after_entry_no_lock(szone_t
*szone
, large_entry_t
*entry
)
2906 unsigned num_large_entries
= szone
->num_large_entries
;
2907 unsigned hash_index
= entry
- szone
->large_entries
;
2908 unsigned index
= hash_index
;
2909 large_entry_t range
;
2913 if (index
== num_large_entries
)
2915 range
= szone
->large_entries
[index
];
2916 if (LARGE_ENTRY_IS_EMPTY(range
))
2918 szone
->large_entries
[index
].address_and_num_pages
= 0;
2919 large_entry_insert_no_lock(szone
, range
); // this will reinsert in the
2921 } while (index
!= hash_index
);
2924 static INLINE large_entry_t
*
2925 large_entries_alloc_no_lock(szone_t
*szone
, unsigned num
)
2927 size_t size
= num
* sizeof(large_entry_t
);
2928 boolean_t is_vm_allocation
= size
>= LARGE_THRESHOLD
;
2930 if (is_vm_allocation
) {
2931 // Note that we allocate memory (via a system call) under a spin lock
2932 // That is certainly evil, however it's very rare in the lifetime of a process
2933 // The alternative would slow down the normal case
2934 return allocate_pages(szone
, round_page(size
), 0, 0, VM_MEMORY_MALLOC_LARGE
);
2936 return small_malloc_cleared_no_lock(szone
, SMALL_MSIZE_FOR_BYTES(size
+ SMALL_QUANTUM
- 1));
2941 large_entries_free_no_lock(szone_t
*szone
, large_entry_t
*entries
, unsigned num
, vm_range_t
*range_to_deallocate
)
2943 // returns range to deallocate
2944 size_t size
= num
* sizeof(large_entry_t
);
2945 boolean_t is_vm_allocation
= size
>= LARGE_THRESHOLD
;
2947 msize_t msize_and_free
;
2949 if (is_vm_allocation
) {
2950 range_to_deallocate
->address
= (vm_address_t
)entries
;
2951 range_to_deallocate
->size
= round_page(size
);
2953 range_to_deallocate
->size
= 0;
2954 region
= small_region_for_ptr_no_lock(szone
, entries
);
2955 msize_and_free
= *SMALL_METADATA_FOR_PTR(entries
);
2956 if (msize_and_free
& SMALL_IS_FREE
) {
2957 szone_error(szone
, "object already freed being freed", entries
, NULL
);
2960 small_free_no_lock(szone
, region
, entries
, msize_and_free
);
2964 static large_entry_t
*
2965 large_entries_grow_no_lock(szone_t
*szone
, vm_range_t
*range_to_deallocate
)
2967 // sets range_to_deallocate
2968 unsigned old_num_entries
= szone
->num_large_entries
;
2969 large_entry_t
*old_entries
= szone
->large_entries
;
2970 unsigned new_num_entries
= (old_num_entries
) ? old_num_entries
* 2 + 1 : 63; // always an odd number for good hashing
2971 large_entry_t
*new_entries
= large_entries_alloc_no_lock(szone
, new_num_entries
);
2972 unsigned index
= old_num_entries
;
2973 large_entry_t oldRange
;
2975 // if the allocation of new entries failed, bail
2976 if (new_entries
== NULL
)
2979 szone
->num_large_entries
= new_num_entries
;
2980 szone
->large_entries
= new_entries
;
2982 /* rehash entries into the new list */
2984 oldRange
= old_entries
[index
];
2985 if (!LARGE_ENTRY_IS_EMPTY(oldRange
)) {
2986 large_entry_insert_no_lock(szone
, oldRange
);
2990 large_entries_free_no_lock(szone
, old_entries
, old_num_entries
, range_to_deallocate
);
2992 range_to_deallocate
->size
= 0;
2997 // frees the specific entry in the size table
2998 // returns a range to truly deallocate
3000 large_free_no_lock(szone_t
*szone
, large_entry_t
*entry
)
3004 range
.address
= (vm_address_t
)LARGE_ENTRY_ADDRESS(*entry
);
3005 range
.size
= (vm_size_t
)LARGE_ENTRY_SIZE(*entry
);
3006 szone
->num_large_objects_in_use
--;
3007 szone
->num_bytes_in_large_objects
-= range
.size
;
3008 if (szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) {
3009 protect((void *)range
.address
, range
.size
, VM_PROT_READ
| VM_PROT_WRITE
, szone
->debug_flags
);
3010 range
.address
-= vm_page_size
;
3011 range
.size
+= 2 * vm_page_size
;
3013 entry
->address_and_num_pages
= 0;
3014 large_entries_rehash_after_entry_no_lock(szone
, entry
);
3016 if (large_entry_for_pointer_no_lock(szone
, (void *)range
.address
)) {
3017 malloc_printf("*** freed entry %p still in use; num_large_entries=%d\n",
3018 range
.address
, szone
->num_large_entries
);
3019 large_debug_print(szone
);
3026 static kern_return_t
3027 large_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t large_entries_address
, unsigned num_entries
, memory_reader_t reader
, vm_range_recorder_t recorder
)
3030 vm_range_t buffer
[MAX_RECORDER_BUFFER
];
3032 large_entry_t
*entries
;
3035 large_entry_t entry
;
3037 err
= reader(task
, large_entries_address
, sizeof(large_entry_t
) * num_entries
, (void **)&entries
);
3040 index
= num_entries
;
3041 if ((type_mask
& MALLOC_ADMIN_REGION_RANGE_TYPE
) &&
3042 (num_entries
* sizeof(large_entry_t
) >= LARGE_THRESHOLD
)) {
3043 range
.address
= large_entries_address
;
3044 range
.size
= round_page(num_entries
* sizeof(large_entry_t
));
3045 recorder(task
, context
, MALLOC_ADMIN_REGION_RANGE_TYPE
, &range
, 1);
3047 if (type_mask
& (MALLOC_PTR_IN_USE_RANGE_TYPE
| MALLOC_PTR_REGION_RANGE_TYPE
))
3049 entry
= entries
[index
];
3050 if (!LARGE_ENTRY_IS_EMPTY(entry
)) {
3051 range
.address
= (vm_address_t
)LARGE_ENTRY_ADDRESS(entry
);
3052 range
.size
= (vm_size_t
)LARGE_ENTRY_SIZE(entry
);
3053 buffer
[count
++] = range
;
3054 if (count
>= MAX_RECORDER_BUFFER
) {
3055 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
| MALLOC_PTR_REGION_RANGE_TYPE
, buffer
, count
);
3061 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
3062 | MALLOC_PTR_REGION_RANGE_TYPE
, buffer
, count
);
3067 /********************* HUGE ENTRY UTILITIES ************************/
3069 static huge_entry_t
*
3070 huge_entry_for_pointer_no_lock(szone_t
*szone
, const void *ptr
)
3075 for (index
= szone
->num_huge_entries
, huge
= szone
->huge_entries
;
3079 if ((void *)huge
->address
== ptr
)
3086 huge_entry_append(szone_t
*szone
, huge_entry_t huge
)
3088 huge_entry_t
*new_huge_entries
= NULL
, *old_huge_entries
;
3089 unsigned num_huge_entries
;
3091 // We do a little dance with locking because doing allocation (even in the
3092 // default szone) may cause something to get freed in this szone, with a
3094 // Returns 1 on success
3097 num_huge_entries
= szone
->num_huge_entries
;
3098 SZONE_UNLOCK(szone
);
3099 /* check for counter wrap */
3100 if ((num_huge_entries
+ 1) < num_huge_entries
)
3102 /* stale allocation from last time around the loop? */
3103 if (new_huge_entries
)
3104 szone_free(szone
, new_huge_entries
);
3105 new_huge_entries
= szone_malloc(szone
, (num_huge_entries
+ 1) * sizeof(huge_entry_t
));
3106 if (new_huge_entries
== NULL
)
3109 if (num_huge_entries
== szone
->num_huge_entries
) {
3110 // No change - our malloc still applies
3111 old_huge_entries
= szone
->huge_entries
;
3112 if (num_huge_entries
) {
3113 memcpy(new_huge_entries
, old_huge_entries
, num_huge_entries
* sizeof(huge_entry_t
));
3115 new_huge_entries
[szone
->num_huge_entries
++] = huge
;
3116 szone
->huge_entries
= new_huge_entries
;
3117 SZONE_UNLOCK(szone
);
3118 szone_free(szone
, old_huge_entries
);
3125 static kern_return_t
3126 huge_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t huge_entries_address
, unsigned num_entries
, memory_reader_t reader
, vm_range_recorder_t recorder
)
3128 huge_entry_t
*entries
;
3131 err
= reader(task
, huge_entries_address
, sizeof(huge_entry_t
) * num_entries
, (void **)&entries
);
3135 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
| MALLOC_PTR_REGION_RANGE_TYPE
, entries
, num_entries
);
3141 large_and_huge_malloc(szone_t
*szone
, size_t num_pages
)
3144 vm_range_t range_to_deallocate
;
3145 huge_entry_t huge_entry
;
3147 large_entry_t large_entry
;
3150 num_pages
= 1; // minimal allocation size for this szone
3151 size
= (size_t)num_pages
<< vm_page_shift
;
3152 range_to_deallocate
.size
= 0;
3153 if (num_pages
>= (1 << vm_page_shift
)) {
3154 addr
= allocate_pages(szone
, size
, 0, szone
->debug_flags
, VM_MEMORY_MALLOC_HUGE
);
3157 huge_entry
.size
= size
;
3158 huge_entry
.address
= (vm_address_t
)addr
;
3159 if (!huge_entry_append(szone
, huge_entry
))
3160 return NULL
; // we are leaking the allocation here
3162 szone
->num_bytes_in_huge_objects
+= size
;
3165 addr
= allocate_pages(szone
, size
, 0, szone
->debug_flags
, VM_MEMORY_MALLOC_LARGE
);
3167 if (LOG(szone
, addr
))
3168 malloc_printf("in szone_malloc true large allocation at %p for %ly\n", (void *)addr
, size
);
3172 SZONE_UNLOCK(szone
);
3176 if (large_entry_for_pointer_no_lock(szone
, addr
)) {
3177 malloc_printf("freshly allocated is already in use: %p\n", addr
);
3178 large_debug_print(szone
);
3182 if ((szone
->num_large_objects_in_use
+ 1) * 4 > szone
->num_large_entries
) {
3183 // density of hash table too high; grow table
3184 // we do that under lock to avoid a race
3185 large_entry_t
*entries
= large_entries_grow_no_lock(szone
, &range_to_deallocate
);
3186 if (entries
== NULL
) {
3187 SZONE_UNLOCK(szone
);
3191 large_entry
.address_and_num_pages
= (uintptr_t)addr
| num_pages
;
3193 if (large_entry_for_pointer_no_lock(szone
, addr
)) {
3194 malloc_printf("entry about to be added already in use: %p\n", addr
);
3195 large_debug_print(szone
);
3199 large_entry_insert_no_lock(szone
, large_entry
);
3201 if (!large_entry_for_pointer_no_lock(szone
, (void *)addr
)) {
3202 malloc_printf("can't find entry just added\n");
3203 large_debug_print(szone
);
3207 szone
->num_large_objects_in_use
++;
3208 szone
->num_bytes_in_large_objects
+= size
;
3210 SZONE_UNLOCK(szone
);
3211 if (range_to_deallocate
.size
) {
3212 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, range_to_deallocate
.size
, 0); // we deallocate outside the lock
3214 return (void *)addr
;
3218 free_large_or_huge(szone_t
*szone
, void *ptr
)
3220 // We have established ptr is page-aligned and not tiny nor small
3221 large_entry_t
*entry
;
3222 vm_range_t vm_range_to_deallocate
;
3226 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
3228 vm_range_to_deallocate
= large_free_no_lock(szone
, entry
);
3230 if (large_entry_for_pointer_no_lock(szone
, ptr
)) {
3231 malloc_printf("*** just after freeing %p still in use num_large_entries=%d\n", ptr
, szone
->num_large_entries
);
3232 large_debug_print(szone
);
3236 } else if ((huge
= huge_entry_for_pointer_no_lock(szone
, ptr
))) {
3237 vm_range_to_deallocate
= *huge
;
3238 *huge
= szone
->huge_entries
[--szone
->num_huge_entries
]; // last entry fills that spot
3239 szone
->num_bytes_in_huge_objects
-= (size_t)vm_range_to_deallocate
.size
;
3242 large_debug_print(szone
);
3244 szone_error(szone
, "pointer being freed was not allocated", ptr
, NULL
);
3247 SZONE_UNLOCK(szone
); // we release the lock asap
3248 CHECK(szone
, __PRETTY_FUNCTION__
);
3249 // we deallocate_pages, including guard pages
3250 if (vm_range_to_deallocate
.address
) {
3252 if (large_entry_for_pointer_no_lock(szone
, (void *)vm_range_to_deallocate
.address
)) {
3253 malloc_printf("*** invariant broken: %p still in use num_large_entries=%d\n", vm_range_to_deallocate
.address
, szone
->num_large_entries
);
3254 large_debug_print(szone
);
3258 deallocate_pages(szone
, (void *)vm_range_to_deallocate
.address
, (size_t)vm_range_to_deallocate
.size
, 0);
3263 try_realloc_large_or_huge_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
)
3265 vm_address_t addr
= (vm_address_t
)ptr
+ old_size
;
3266 large_entry_t
*large_entry
, saved_entry
;
3267 huge_entry_t
*huge_entry
, huge
;
3271 if (old_size
!= ((old_size
>> vm_page_shift
) << vm_page_shift
)) {
3272 malloc_printf("*** old_size is %d\n", old_size
);
3276 large_entry
= large_entry_for_pointer_no_lock(szone
, (void *)addr
);
3277 SZONE_UNLOCK(szone
);
3279 return 0; // large pointer already exists in table - extension is not going to work
3281 new_size
= round_page(new_size
);
3283 * Ask for allocation at a specific address, and mark as realloc
3284 * to request coalescing with previous realloc'ed extensions.
3286 err
= vm_allocate(mach_task_self(), &addr
, new_size
- old_size
, VM_MAKE_TAG(VM_MEMORY_REALLOC
));
3287 if (err
!= KERN_SUCCESS
) {
3292 * If the new size is still under the large/huge threshold, we can just
3293 * extend the existing large block.
3295 * Note: this logic is predicated on the understanding that an allocated
3296 * block can never really shrink, so that the new size will always be
3297 * larger than the old size.
3299 * Note: the use of 1 << vm_page_shift here has to do with the subdivision
3300 * of the bits in the large_entry_t, and not the size of a page (directly).
3302 if ((new_size
>> vm_page_shift
) < (1 << vm_page_shift
)) {
3303 /* extend existing large entry */
3304 large_entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
3306 szone_error(szone
, "large entry reallocated is not properly in table", ptr
, NULL
);
3307 /* XXX will cause fault on next reference to entry */
3309 large_entry
->address_and_num_pages
= (uintptr_t)ptr
| (new_size
>> vm_page_shift
);
3310 szone
->num_bytes_in_large_objects
+= new_size
- old_size
;
3311 } else if ((old_size
>> vm_page_shift
) >= (1 << vm_page_shift
)) {
3312 /* extend existing huge entry */
3313 huge_entry
= huge_entry_for_pointer_no_lock(szone
, ptr
);
3315 szone_error(szone
, "huge entry reallocated is not properly in table", ptr
, NULL
);
3316 /* XXX will cause fault on next reference to huge_entry */
3318 huge_entry
->size
= new_size
;
3319 szone
->num_bytes_in_huge_objects
+= new_size
- old_size
;
3321 /* need to convert large entry to huge entry */
3323 /* release large entry, note we still have the VM allocation */
3324 large_entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
3325 saved_entry
= *large_entry
; // in case we need to put it back
3326 large_free_no_lock(szone
, large_entry
);
3328 /* and get a huge entry */
3329 huge
.address
= (vm_address_t
)ptr
;
3330 huge
.size
= new_size
; /* fix up size */
3331 SZONE_UNLOCK(szone
);
3332 if (huge_entry_append(szone
, huge
)) {
3333 szone
->num_bytes_in_huge_objects
+= new_size
;
3334 return 1; // success!
3337 // we leak memory (the extra space appended) but data structures are correct
3338 large_entry_insert_no_lock(szone
, saved_entry
); // this will reinsert the large entry
3340 SZONE_UNLOCK(szone
); // we release the lock asap
3344 /********************* Zone call backs ************************/
3347 szone_free(szone_t
*szone
, void *ptr
)
3349 region_t
*tiny_region
;
3350 region_t
*small_region
;
3353 if (LOG(szone
, ptr
))
3354 malloc_printf("in szone_free with %p\n", ptr
);
3359 * Try to free to a tiny region.
3361 if ((uintptr_t)ptr
& (TINY_QUANTUM
- 1)) {
3362 szone_error(szone
, "Non-aligned pointer being freed", ptr
, NULL
);
3365 if ((tiny_region
= tiny_region_for_ptr_no_lock(szone
, ptr
)) != NULL
) {
3366 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
) {
3367 szone_error(szone
, "Pointer to metadata being freed", ptr
, NULL
);
3370 free_tiny(szone
, ptr
, tiny_region
);
3375 * Try to free to a small region.
3377 if ((uintptr_t)ptr
& (SMALL_QUANTUM
- 1)) {
3378 szone_error(szone
, "Non-aligned pointer being freed (2)", ptr
, NULL
);
3381 if ((small_region
= small_region_for_ptr_no_lock(szone
, ptr
)) != NULL
) {
3382 if (SMALL_META_INDEX_FOR_PTR(ptr
) >= NUM_SMALL_BLOCKS
) {
3383 szone_error(szone
, "Pointer to metadata being freed (2)", ptr
, NULL
);
3386 free_small(szone
, ptr
, small_region
);
3390 /* check that it's a legal large/huge allocation */
3391 if ((uintptr_t)ptr
& (vm_page_size
- 1)) {
3392 szone_error(szone
, "non-page-aligned, non-allocated pointer being freed", ptr
, NULL
);
3395 free_large_or_huge(szone
, ptr
);
3398 static INLINE
void *
3399 szone_malloc_should_clear(szone_t
*szone
, size_t size
, boolean_t cleared_requested
)
3404 if (size
<= 31*TINY_QUANTUM
) {
3406 msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
3409 ptr
= tiny_malloc_should_clear(szone
, msize
, cleared_requested
);
3410 } else if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) && (size
< LARGE_THRESHOLD
)) {
3412 msize
= SMALL_MSIZE_FOR_BYTES(size
+ SMALL_QUANTUM
- 1);
3413 if (! msize
) msize
= 1;
3414 ptr
= small_malloc_should_clear(szone
, msize
, cleared_requested
);
3417 size_t num_pages
= round_page(size
) >> vm_page_shift
;
3418 if (num_pages
== 0) /* Overflowed */
3421 ptr
= large_and_huge_malloc(szone
, num_pages
);
3424 if (LOG(szone
, ptr
))
3425 malloc_printf("szone_malloc returned %p\n", ptr
);
3428 * If requested, scribble on allocated memory.
3430 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && ptr
&& !cleared_requested
&& size
)
3431 memset(ptr
, 0xaa, size
);
3437 szone_malloc(szone_t
*szone
, size_t size
) {
3438 return szone_malloc_should_clear(szone
, size
, 0);
3442 szone_calloc(szone_t
*szone
, size_t num_items
, size_t size
)
3444 size_t total_bytes
= num_items
* size
;
3445 if ((num_items
> 1) && (size
!= 0) && ((total_bytes
/ size
) != num_items
))
3447 return szone_malloc_should_clear(szone
, total_bytes
, 1);
3451 szone_valloc(szone_t
*szone
, size_t size
)
3456 num_pages
= round_page(size
) >> vm_page_shift
;
3457 ptr
= large_and_huge_malloc(szone
, num_pages
);
3459 if (LOG(szone
, ptr
))
3460 malloc_printf("szone_valloc returned %p\n", ptr
);
3466 szone_size(szone_t
*szone
, const void *ptr
)
3470 msize_t msize
, msize_and_free
;
3471 large_entry_t
*entry
;
3477 if (LOG(szone
, ptr
)) {
3478 malloc_printf("in szone_size for %p (szone=%p)\n", ptr
, szone
);
3483 * Look for it in a tiny region.
3485 if ((uintptr_t)ptr
& (TINY_QUANTUM
- 1))
3487 if (tiny_region_for_ptr_no_lock(szone
, ptr
)) {
3488 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
)
3490 msize
= get_tiny_meta_header(ptr
, &is_free
);
3491 return (is_free
) ? 0 : TINY_BYTES_FOR_MSIZE(msize
);
3495 * Look for it in a small region.
3497 if ((uintptr_t)ptr
& (SMALL_QUANTUM
- 1))
3499 if (small_region_for_ptr_no_lock(szone
, ptr
)) {
3500 if (SMALL_META_INDEX_FOR_PTR(ptr
) >= NUM_SMALL_BLOCKS
)
3502 msize_and_free
= *SMALL_METADATA_FOR_PTR(ptr
);
3503 return (msize_and_free
& SMALL_IS_FREE
) ? 0 : SMALL_BYTES_FOR_MSIZE(msize_and_free
);
3507 * If not page-aligned, it cannot have come from a large or huge allocation.
3509 if ((uintptr_t)ptr
& (vm_page_size
- 1))
3513 * Look for it in a large or huge entry.
3516 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
3518 size
= LARGE_ENTRY_SIZE(*entry
);
3519 } else if ((huge
= huge_entry_for_pointer_no_lock(szone
, ptr
))) {
3522 SZONE_UNLOCK(szone
);
3524 if (LOG(szone
, ptr
)) {
3525 malloc_printf("szone_size for %p returned %d\n", ptr
, (unsigned)size
);
3532 szone_realloc(szone_t
*szone
, void *ptr
, size_t new_size
)
3538 if (LOG(szone
, ptr
)) {
3539 malloc_printf("in szone_realloc for %p, %d\n", ptr
, (unsigned)new_size
);
3543 ptr
= szone_malloc(szone
, new_size
);
3546 old_size
= szone_size(szone
, ptr
);
3548 szone_error(szone
, "pointer being reallocated was not allocated", ptr
, NULL
);
3551 /* we never shrink an allocation */
3552 if (old_size
>= new_size
)
3556 * If the old and new sizes both suit the tiny allocator, try to reallocate in-place.
3558 if ((new_size
+ TINY_QUANTUM
- 1) <= 31 * TINY_QUANTUM
) {
3559 if (try_realloc_tiny_in_place(szone
, ptr
, old_size
, new_size
)) {
3564 * If the old and new sizes both suit the small allocator, and we're not protecting the
3565 * small allocations, try to reallocate in-place.
3567 } else if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) &&
3568 ((new_size
+ SMALL_QUANTUM
- 1) < LARGE_THRESHOLD
) &&
3569 (old_size
> 31 * TINY_QUANTUM
)) {
3570 if (try_realloc_small_in_place(szone
, ptr
, old_size
, new_size
)) {
3575 * If the allocation's a large or huge allocation, try to reallocate in-place there.
3577 } else if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) && (old_size
> LARGE_THRESHOLD
)) {
3578 if (try_realloc_large_or_huge_in_place(szone
, ptr
, old_size
, new_size
)) {
3584 * Can't reallocate in place for whatever reason; allocate a new buffer and copy.
3586 new_ptr
= szone_malloc(szone
, new_size
);
3587 if (new_ptr
== NULL
)
3591 * If the allocation's large enough, try to copy using VM. If that fails, or
3592 * if it's too small, just copy by hand.
3594 if ((old_size
< VM_COPY_THRESHOLD
) ||
3595 vm_copy(mach_task_self(), (vm_address_t
)ptr
, old_size
, (vm_address_t
)new_ptr
))
3596 memcpy(new_ptr
, ptr
, old_size
);
3597 szone_free(szone
, ptr
);
3600 if (LOG(szone
, ptr
)) {
3601 malloc_printf("szone_realloc returned %p for %d\n", new_ptr
, (unsigned)new_size
);
3607 // given a size, returns the number of pointers allocated capable of holding
3608 // that size, up to the limit specified by the 'count' argument. These pointers
3609 // are stored in the 'results' array, which must be allocated by the caller.
3610 // may return zero, since this function is only a best attempt at allocating
3611 // the pointers. clients should be prepared to call malloc for any additional
3612 // blocks they need.
3614 szone_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
)
3616 msize_t msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
3619 // only bother implementing this for tiny
3620 if (size
> 31*TINY_QUANTUM
)
3622 // make sure to return objects at least one quantum in size
3626 CHECK(szone
, __PRETTY_FUNCTION__
);
3628 // We must lock the zone now, since tiny_malloc_from_free_list assumes that
3629 // the caller has done so.
3632 // with the zone locked, allocate objects from the free list until all
3633 // sufficiently large objects have been exhausted, or we have met our quota
3634 // of objects to allocate.
3635 while (found
< count
) {
3636 void *ptr
= tiny_malloc_from_free_list(szone
, msize
);
3643 SZONE_UNLOCK(szone
);
3648 szone_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
)
3652 region_t
*tiny_region
;
3656 // frees all the pointers in to_be_freed
3657 // note that to_be_freed may be overwritten during the process
3660 CHECK(szone
, __PRETTY_FUNCTION__
);
3662 while (cc
< count
) {
3663 ptr
= to_be_freed
[cc
];
3665 /* XXX this really slows us down */
3666 tiny_region
= tiny_region_for_ptr_no_lock(szone
, ptr
);
3668 // this is a tiny pointer
3669 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
)
3670 break; // pointer to metadata; let the standard free deal with it
3671 msize
= get_tiny_meta_header(ptr
, &is_free
);
3673 break; // a double free; let the standard free deal with it
3674 tiny_free_no_lock(szone
, tiny_region
, ptr
, msize
);
3675 to_be_freed
[cc
] = NULL
;
3680 SZONE_UNLOCK(szone
);
3681 CHECK(szone
, __PRETTY_FUNCTION__
);
3683 ptr
= to_be_freed
[count
];
3685 szone_free(szone
, ptr
);
3690 szone_destroy(szone_t
*szone
)
3693 large_entry_t
*large
;
3694 vm_range_t range_to_deallocate
;
3697 /* destroy large entries */
3698 index
= szone
->num_large_entries
;
3700 large
= szone
->large_entries
+ index
;
3701 if (!LARGE_ENTRY_IS_EMPTY(*large
)) {
3702 // we deallocate_pages, including guard pages
3703 deallocate_pages(szone
, (void *)LARGE_ENTRY_ADDRESS(*large
), LARGE_ENTRY_SIZE(*large
), szone
->debug_flags
);
3706 if (szone
->num_large_entries
* sizeof(large_entry_t
) >= LARGE_THRESHOLD
) {
3707 // we do not free in the small chunk case
3708 large_entries_free_no_lock(szone
, szone
->large_entries
, szone
->num_large_entries
, &range_to_deallocate
);
3709 if (range_to_deallocate
.size
)
3710 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, (size_t)range_to_deallocate
.size
, 0);
3713 /* destroy huge entries */
3714 index
= szone
->num_huge_entries
;
3716 huge
= szone
->huge_entries
+ index
;
3717 deallocate_pages(szone
, (void *)huge
->address
, huge
->size
, szone
->debug_flags
);
3720 /* destroy tiny regions */
3721 for (index
= 0; index
< szone
->num_tiny_regions_allocated
; ++index
)
3722 if (szone
->tiny_regions
[index
])
3723 deallocate_pages(szone
, szone
->tiny_regions
[index
], TINY_REGION_SIZE
, 0);
3725 /* destroy small regions */
3726 for (index
= 0; index
< szone
->num_small_regions_allocated
; ++index
)
3727 if (szone
->small_regions
[index
])
3728 deallocate_pages(szone
, szone
->small_regions
[index
], SMALL_REGION_SIZE
, 0);
3730 /* destroy region hash rings, if any */
3731 if (szone
->tiny_regions
!= szone
->initial_tiny_regions
) {
3732 size_t size
= round_page(szone
->num_tiny_regions_allocated
* sizeof(region_t
));
3733 deallocate_pages(szone
, szone
->tiny_regions
, size
, 0);
3735 if (szone
->small_regions
!= szone
->initial_small_regions
) {
3736 size_t size
= round_page(szone
->num_small_regions_allocated
* sizeof(region_t
));
3737 deallocate_pages(szone
, szone
->small_regions
, size
, 0);
3739 /* Now destroy the separate szone region */
3740 deallocate_pages(szone
, (void *)szone
, SZONE_PAGED_SIZE
, SCALABLE_MALLOC_ADD_GUARD_PAGES
);
3744 szone_good_size(szone_t
*szone
, size_t size
)
3749 if (size
<= 31 * TINY_QUANTUM
) {
3751 msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
3752 if (! msize
) msize
= 1;
3753 return TINY_BYTES_FOR_MSIZE(msize
);
3755 if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) && (size
< LARGE_THRESHOLD
)) {
3757 msize
= SMALL_MSIZE_FOR_BYTES(size
+ SMALL_QUANTUM
- 1);
3758 if (! msize
) msize
= 1;
3759 return SMALL_BYTES_FOR_MSIZE(msize
);
3761 num_pages
= round_page(size
) >> vm_page_shift
;
3763 num_pages
= 1; // minimal allocation size for this
3764 return num_pages
<< vm_page_shift
;
3768 unsigned szone_check_counter
= 0;
3769 unsigned szone_check_start
= 0;
3770 unsigned szone_check_modulo
= 1;
3773 szone_check_all(szone_t
*szone
, const char *function
)
3778 CHECK_LOCKED(szone
, __PRETTY_FUNCTION__
);
3780 /* check tiny regions - chould check region count */
3781 for (index
= 0; index
< szone
->num_tiny_regions_allocated
; ++index
) {
3782 region_t tiny
= szone
->tiny_regions
[index
];
3783 if (tiny
&& !tiny_check_region(szone
, tiny
)) {
3784 SZONE_UNLOCK(szone
);
3785 szone
->debug_flags
&= ~ CHECK_REGIONS
;
3786 szone_error(szone
, "check: tiny region incorrect", NULL
,
3787 "*** tiny region %d incorrect szone_check_all(%s) counter=%d\n",
3788 index
, function
, szone_check_counter
);
3792 /* check tiny free lists */
3793 for (index
= 0; index
< NUM_TINY_SLOTS
; ++index
) {
3794 if (!tiny_free_list_check(szone
, index
)) {
3795 SZONE_UNLOCK(szone
);
3796 szone
->debug_flags
&= ~ CHECK_REGIONS
;
3797 szone_error(szone
, "check: tiny free list incorrect", NULL
,
3798 "*** tiny free list incorrect (slot=%d) szone_check_all(%s) counter=%d\n",
3799 index
, function
, szone_check_counter
);
3803 /* check small regions - could check region count */
3804 for (index
= 0; index
< szone
->num_small_regions_allocated
; ++index
) {
3805 region_t small
= szone
->small_regions
[index
];
3806 if (small
&& !szone_check_small_region(szone
, small
)) {
3807 SZONE_UNLOCK(szone
);
3808 szone
->debug_flags
&= ~ CHECK_REGIONS
;
3809 szone_error(szone
, "check: small region incorrect", NULL
,
3810 "*** small region %d incorrect szone_check_all(%s) counter=%d\n",
3811 index
, function
, szone_check_counter
);
3815 /* check small free lists */
3816 for (index
= 0; index
< NUM_SMALL_SLOTS
; ++index
) {
3817 if (!small_free_list_check(szone
, index
)) {
3818 SZONE_UNLOCK(szone
);
3819 szone
->debug_flags
&= ~ CHECK_REGIONS
;
3820 szone_error(szone
, "check: small free list incorrect", NULL
,
3821 "*** small free list incorrect (grain=%d) szone_check_all(%s) counter=%d\n",
3822 index
, function
, szone_check_counter
);
3826 SZONE_UNLOCK(szone
);
3831 szone_check(szone_t
*szone
)
3833 if ((++szone_check_counter
% 10000) == 0)
3834 _malloc_printf(ASL_LEVEL_NOTICE
, "at szone_check counter=%d\n", szone_check_counter
);
3835 if (szone_check_counter
< szone_check_start
)
3837 if (szone_check_counter
% szone_check_modulo
)
3839 return szone_check_all(szone
, "");
3842 static kern_return_t
3843 szone_ptr_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t zone_address
, memory_reader_t reader
, vm_range_recorder_t recorder
)
3848 if (!reader
) reader
= _szone_default_reader
;
3849 err
= reader(task
, zone_address
, sizeof(szone_t
), (void **)&szone
);
3850 if (err
) return err
;
3851 err
= tiny_in_use_enumerator(task
, context
, type_mask
, szone
, reader
, recorder
);
3852 if (err
) return err
;
3853 err
= small_in_use_enumerator(task
, context
, type_mask
, szone
, reader
, recorder
);
3854 if (err
) return err
;
3855 err
= large_in_use_enumerator(task
, context
, type_mask
,
3856 (vm_address_t
)szone
->large_entries
, szone
->num_large_entries
, reader
,
3858 if (err
) return err
;
3859 err
= huge_in_use_enumerator(task
, context
, type_mask
,
3860 (vm_address_t
)szone
->huge_entries
, szone
->num_huge_entries
, reader
,
3865 // Following method is deprecated: use scalable_zone_statistics instead
3867 scalable_zone_info(malloc_zone_t
*zone
, unsigned *info_to_fill
, unsigned count
)
3869 szone_t
*szone
= (void *)zone
;
3872 // We do not lock to facilitate debug
3873 info
[4] = szone
->num_tiny_objects
;
3874 info
[5] = szone
->num_bytes_in_tiny_objects
;
3875 info
[6] = szone
->num_small_objects
;
3876 info
[7] = szone
->num_bytes_in_small_objects
;
3877 info
[8] = szone
->num_large_objects_in_use
;
3878 info
[9] = szone
->num_bytes_in_large_objects
;
3879 info
[10] = szone
->num_huge_entries
;
3880 info
[11] = szone
->num_bytes_in_huge_objects
;
3881 info
[12] = szone
->debug_flags
;
3882 info
[0] = info
[4] + info
[6] + info
[8] + info
[10];
3883 info
[1] = info
[5] + info
[7] + info
[9] + info
[11];
3884 info
[3] = szone
->num_tiny_regions
* TINY_REGION_SIZE
+ szone
->num_small_regions
* SMALL_REGION_SIZE
+ info
[9] + info
[11];
3885 info
[2] = info
[3] - szone
->tiny_bytes_free_at_end
- szone
->small_bytes_free_at_end
;
3886 memcpy(info_to_fill
, info
, sizeof(unsigned)*count
);
3890 szone_print(szone_t
*szone
, boolean_t verbose
)
3897 scalable_zone_info((void *)szone
, info
, 13);
3898 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
3899 "Scalable zone %p: inUse=%d(%y) touched=%y allocated=%y flags=%d\n",
3900 szone
, info
[0], info
[1], info
[2], info
[3], info
[12]);
3901 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
3902 "\ttiny=%d(%y) small=%d(%y) large=%d(%y) huge=%d(%y)\n",
3903 info
[4], info
[5], info
[6], info
[7], info
[8], info
[9], info
[10], info
[11]);
3905 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
3906 "%d tiny regions:\n", szone
->num_tiny_regions
);
3907 for (index
= 0; index
< szone
->num_tiny_regions_allocated
; ++index
) {
3908 region
= szone
->tiny_regions
[index
];
3910 print_tiny_region(verbose
, region
, (region
== szone
->last_tiny_region
) ?
3911 szone
->tiny_bytes_free_at_end
: 0);
3914 print_tiny_free_list(szone
);
3916 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
3917 "%d small regions:\n", szone
->num_small_regions
);
3918 for (index
= 0; index
< szone
->num_small_regions_allocated
; ++index
) {
3919 region
= szone
->small_regions
[index
];
3921 print_small_region(szone
, verbose
, region
,
3922 (region
== szone
->last_small_region
) ?
3923 szone
->small_bytes_free_at_end
: 0);
3926 print_small_free_list(szone
);
3927 SZONE_UNLOCK(szone
);
3931 szone_log(malloc_zone_t
*zone
, void *log_address
)
3933 szone_t
*szone
= (szone_t
*)zone
;
3935 szone
->log_address
= log_address
;
3939 szone_force_lock(szone_t
*szone
)
3945 szone_force_unlock(szone_t
*szone
)
3947 SZONE_UNLOCK(szone
);
3951 scalable_zone_statistics(malloc_zone_t
*zone
, malloc_statistics_t
*stats
, unsigned subzone
)
3953 szone_t
*szone
= (szone_t
*)zone
;
3957 stats
->blocks_in_use
= szone
->num_tiny_objects
;
3958 stats
->size_in_use
= szone
->num_bytes_in_tiny_objects
;
3959 stats
->size_allocated
= szone
->num_tiny_regions
* TINY_REGION_SIZE
;
3960 stats
->max_size_in_use
= stats
->size_allocated
- szone
->tiny_bytes_free_at_end
;
3963 stats
->blocks_in_use
= szone
->num_small_objects
;
3964 stats
->size_in_use
= szone
->num_bytes_in_small_objects
;
3965 stats
->size_allocated
= szone
->num_small_regions
* SMALL_REGION_SIZE
;
3966 stats
->max_size_in_use
= stats
->size_allocated
- szone
->small_bytes_free_at_end
;
3969 stats
->blocks_in_use
= szone
->num_large_objects_in_use
;
3970 stats
->size_in_use
= szone
->num_bytes_in_large_objects
;
3971 stats
->max_size_in_use
= stats
->size_allocated
= stats
->size_in_use
;
3974 stats
->blocks_in_use
= szone
->num_huge_entries
;
3975 stats
->size_in_use
= szone
->num_bytes_in_huge_objects
;
3976 stats
->max_size_in_use
= stats
->size_allocated
= stats
->size_in_use
;
3983 szone_statistics(szone_t
*szone
, malloc_statistics_t
*stats
)
3985 size_t big_and_huge
;
3987 stats
->blocks_in_use
=
3988 szone
->num_tiny_objects
+
3989 szone
->num_small_objects
+
3990 szone
->num_large_objects_in_use
+
3991 szone
->num_huge_entries
;
3992 big_and_huge
= szone
->num_bytes_in_large_objects
+ szone
->num_bytes_in_huge_objects
;
3993 stats
->size_in_use
= szone
->num_bytes_in_tiny_objects
+ szone
->num_bytes_in_small_objects
+ big_and_huge
;
3994 stats
->max_size_in_use
= stats
->size_allocated
=
3995 szone
->num_tiny_regions
* TINY_REGION_SIZE
+
3996 szone
->num_small_regions
* SMALL_REGION_SIZE
+
3999 // Now we account for the untouched areas
4000 stats
->max_size_in_use
-= szone
->tiny_bytes_free_at_end
;
4001 stats
->max_size_in_use
-= szone
->small_bytes_free_at_end
;
4004 static const struct malloc_introspection_t szone_introspect
= {
4005 (void *)szone_ptr_in_use_enumerator
,
4006 (void *)szone_good_size
,
4007 (void *)szone_check
,
4008 (void *)szone_print
,
4010 (void *)szone_force_lock
,
4011 (void *)szone_force_unlock
,
4012 (void *)szone_statistics
4013 }; // marked as const to spare the DATA section
4016 create_scalable_zone(size_t initial_size
, unsigned debug_flags
)
4021 * Sanity-check our build-time assumptions about the size of a page.
4022 * Since we have sized various things assuming the default page size,
4023 * attempting to determine it dynamically is not useful.
4025 if ((vm_page_size
!= _vm_page_size
) || (vm_page_shift
!= _vm_page_shift
)) {
4026 malloc_printf("*** FATAL ERROR - machine page size does not match our assumptions.\n");
4030 /* get memory for the zone, which is now separate from any region.
4031 add guard pages to prevent walking from any other vm allocations
4032 to here and overwriting the function pointers in basic_zone. */
4033 szone
= allocate_pages(NULL
, SZONE_PAGED_SIZE
, 0,
4034 SCALABLE_MALLOC_ADD_GUARD_PAGES
,
4038 /* set up the szone structure */
4039 szone
->tiny_regions
= szone
->initial_tiny_regions
;
4040 szone
->small_regions
= szone
->initial_small_regions
;
4041 szone
->num_tiny_regions_allocated
= INITIAL_NUM_REGIONS
;
4042 szone
->num_small_regions_allocated
= INITIAL_NUM_REGIONS
;
4043 szone
->basic_zone
.version
= 3;
4044 szone
->basic_zone
.size
= (void *)szone_size
;
4045 szone
->basic_zone
.malloc
= (void *)szone_malloc
;
4046 szone
->basic_zone
.calloc
= (void *)szone_calloc
;
4047 szone
->basic_zone
.valloc
= (void *)szone_valloc
;
4048 szone
->basic_zone
.free
= (void *)szone_free
;
4049 szone
->basic_zone
.realloc
= (void *)szone_realloc
;
4050 szone
->basic_zone
.destroy
= (void *)szone_destroy
;
4051 szone
->basic_zone
.batch_malloc
= (void *)szone_batch_malloc
;
4052 szone
->basic_zone
.batch_free
= (void *)szone_batch_free
;
4053 szone
->basic_zone
.introspect
= (struct malloc_introspection_t
*)&szone_introspect
;
4054 szone
->debug_flags
= debug_flags
;
4055 LOCK_INIT(szone
->lock
);
4058 #warning CHECK_REGIONS enabled
4059 debug_flags
|= CHECK_REGIONS
;
4062 #warning LOG enabled
4063 szone
->log_address
= ~0;
4065 CHECK(szone
, __PRETTY_FUNCTION__
);
4066 return (malloc_zone_t
*)szone
;
4069 /********* Support code for emacs unexec ************/
4071 /* History of freezedry version numbers:
4073 * 1) Old malloc (before the scalable malloc implementation in this file
4075 * 2) Original freezedrying code for scalable malloc. This code was apparently
4076 * based on the old freezedrying code and was fundamentally flawed in its
4077 * assumption that tracking allocated memory regions was adequate to fake
4078 * operations on freezedried memory. This doesn't work, since scalable
4079 * malloc does not store flags in front of large page-aligned allocations.
4080 * 3) Original szone-based freezedrying code.
4081 * 4) Fresher malloc with tiny zone
4082 * 5) 32/64bit compatible malloc
4083 * 6) Metadata within 1MB and 8MB region for tiny and small
4085 * No version backward compatibility is provided, but the version number does
4086 * make it possible for malloc_jumpstart() to return an error if the application
4087 * was freezedried with an older version of malloc.
4089 #define MALLOC_FREEZEDRY_VERSION 6
4098 frozen_malloc(szone_t
*zone
, size_t new_size
)
4100 return malloc(new_size
);
4104 frozen_calloc(szone_t
*zone
, size_t num_items
, size_t size
)
4106 return calloc(num_items
, size
);
4110 frozen_valloc(szone_t
*zone
, size_t new_size
)
4112 return valloc(new_size
);
4116 frozen_realloc(szone_t
*zone
, void *ptr
, size_t new_size
)
4118 size_t old_size
= szone_size(zone
, ptr
);
4121 if (new_size
<= old_size
) {
4124 new_ptr
= malloc(new_size
);
4126 memcpy(new_ptr
, ptr
, old_size
);
4132 frozen_free(szone_t
*zone
, void *ptr
)
4137 frozen_destroy(szone_t
*zone
)
4141 /********* Pseudo-private API for emacs unexec ************/
4144 * malloc_freezedry() records all of the szones in use, so that they can be
4145 * partially reconstituted by malloc_jumpstart(). Due to the differences
4146 * between reconstituted memory regions and those created by the szone code,
4147 * care is taken not to reallocate from the freezedried memory, except in the
4148 * case of a non-growing realloc().
4150 * Due to the flexibility provided by the zone registration mechanism, it is
4151 * impossible to implement generic freezedrying for any zone type. This code
4152 * only handles applications that use the szone allocator, so malloc_freezedry()
4153 * returns 0 (error) if any non-szone zones are encountered.
4157 malloc_freezedry(void)
4159 extern unsigned malloc_num_zones
;
4160 extern malloc_zone_t
**malloc_zones
;
4161 malloc_frozen
*data
;
4164 /* Allocate space in which to store the freezedry state. */
4165 data
= (malloc_frozen
*) malloc(sizeof(malloc_frozen
));
4167 /* Set freezedry version number so that malloc_jumpstart() can check for compatibility. */
4168 data
->version
= MALLOC_FREEZEDRY_VERSION
;
4170 /* Allocate the array of szone pointers. */
4171 data
->nszones
= malloc_num_zones
;
4172 data
->szones
= (szone_t
*) calloc(malloc_num_zones
, sizeof(szone_t
));
4175 * Fill in the array of szone structures. They are copied rather than
4176 * referenced, since the originals are likely to be clobbered during malloc
4179 for (i
= 0; i
< malloc_num_zones
; i
++) {
4180 if (strcmp(malloc_zones
[i
]->zone_name
, "DefaultMallocZone")) {
4181 /* Unknown zone type. */
4186 memcpy(&data
->szones
[i
], malloc_zones
[i
], sizeof(szone_t
));
4189 return((uintptr_t)data
);
4193 malloc_jumpstart(uintptr_t cookie
)
4195 malloc_frozen
*data
= (malloc_frozen
*)cookie
;
4198 if (data
->version
!= MALLOC_FREEZEDRY_VERSION
) {
4199 /* Unsupported freezedry version. */
4203 for (i
= 0; i
< data
->nszones
; i
++) {
4204 /* Set function pointers. Even the functions that stay the same must be
4205 * set, since there are no guarantees that they will be mapped to the
4206 * same addresses. */
4207 data
->szones
[i
].basic_zone
.size
= (void *) szone_size
;
4208 data
->szones
[i
].basic_zone
.malloc
= (void *) frozen_malloc
;
4209 data
->szones
[i
].basic_zone
.calloc
= (void *) frozen_calloc
;
4210 data
->szones
[i
].basic_zone
.valloc
= (void *) frozen_valloc
;
4211 data
->szones
[i
].basic_zone
.free
= (void *) frozen_free
;
4212 data
->szones
[i
].basic_zone
.realloc
= (void *) frozen_realloc
;
4213 data
->szones
[i
].basic_zone
.destroy
= (void *) frozen_destroy
;
4214 data
->szones
[i
].basic_zone
.introspect
= (struct malloc_introspection_t
*)&szone_introspect
;
4216 /* Register the freezedried zone. */
4217 malloc_zone_register(&data
->szones
[i
].basic_zone
);