-
Notifications
You must be signed in to change notification settings - Fork 23
Expand file tree
/
Copy pathmagazine_malloc.c
More file actions
7182 lines (6215 loc) · 259 KB
/
Copy pathmagazine_malloc.c
File metadata and controls
7182 lines (6215 loc) · 259 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright (c) 1999, 2006, 2008 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/* Author: Bertrand Serlet, August 1999 */
/*
Multithread enhancements for "tiny" allocations introduced February 2008.
These are in the spirit of "Hoard". See:
Berger, E.D.; McKinley, K.S.; Blumofe, R.D.; Wilson, P.R. (2000).
"Hoard: a scalable memory allocator for multithreaded applications".
ACM SIGPLAN Notices 35 (11): 117-128. Berger2000.
<http://portal.acm.org/citation.cfm?id=356989.357000>
Retrieved on 2008-02-22.
*/
/* gcc -g -O3 magazine_malloc.c malloc.c -o libmagmalloc.dylib -I. \
-I/System/Library/Frameworks/System.framework/PrivateHeaders/ -funit-at-a-time \
-dynamiclib -Wall -arch x86_64 -arch i386 -arch ppc */
#include "scalable_malloc.h"
#include "malloc_printf.h"
#include "_simple.h"
#include "magmallocProvider.h"
#include <pthread_internals.h> /* for pthread_lock_t SPI */
#include <pthread.h> /* for pthread API */
#include <stdint.h>
#include <unistd.h>
#include <mach/vm_statistics.h>
#include <mach/mach_init.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/sysctl.h>
#include <libkern/OSAtomic.h>
#include <mach-o/dyld.h> /* for NSVersionOfLinkTimeLibrary() */
/********************* DEFINITIONS ************************/
#define DEBUG_MALLOC 0 // set to one to debug malloc itself
#define DEBUG_CLIENT 0 // set to one to debug malloc client
#if DEBUG_MALLOC
#warning DEBUG_MALLOC ENABLED
# define INLINE
# define ALWAYSINLINE
# define CHECK_MAGAZINE_PTR_LOCKED(szone, mag_ptr, fun) \
do { \
if (__is_threaded && TRY_LOCK(mag_ptr->magazine_lock)) { \
malloc_printf("*** magazine_lock was not set %p in %s\n", \
mag_ptr->magazine_lock, fun); \
} \
} while (0)
#else
# define INLINE __inline__
# define ALWAYSINLINE __attribute__((always_inline))
# define CHECK_MAGAZINE_PTR_LOCKED(szone, mag_ptr, fun) {}
#endif
# define NOINLINE __attribute__((noinline))
#if defined(__i386__) || defined(__x86_64__)
#define CACHE_ALIGN __attribute__ ((aligned (128) )) /* Future-proofing at 128B */
#elif defined(__ppc__) || defined(__ppc64__)
#define CACHE_ALIGN __attribute__ ((aligned (128) ))
#else
#define CACHE_ALIGN
#endif
/*
* Access to global variables is slow, so optimise our handling of vm_page_size
* and vm_page_shift.
*/
#define _vm_page_size vm_page_size /* to get to the originals */
#define _vm_page_shift vm_page_shift
#define vm_page_size 4096 /* our normal working sizes */
#define vm_page_shift 12
/*
* msize - a type to refer to the number of quanta of a tiny or small
* allocation. A tiny block with an msize of 3 would be 3 << SHIFT_TINY_QUANTUM
* bytes in size.
*/
typedef unsigned short msize_t;
typedef union {
void *p;
uintptr_t u;
} ptr_union;
typedef struct {
ptr_union previous;
ptr_union next;
} free_list_t;
typedef unsigned int grain_t; // N.B. wide enough to index all free slots
typedef int mag_index_t;
#define CHECK_REGIONS (1 << 31)
#define MAX_RECORDER_BUFFER 256
/********************* DEFINITIONS for tiny ************************/
/*
* Memory in the Tiny range is allocated from regions (heaps) pointed to by the
* szone's hashed_regions pointer.
*
* Each region is laid out as a heap, followed by a header block, all within
* a 1MB (2^20) block. This means there are 64520 16-byte blocks and the header
* is 16138 bytes, making the total 1048458 bytes, leaving 118 bytes unused.
*
* The header block is arranged as in struct tiny_region defined just below, and
* consists of two bitfields (or bit arrays) interleaved 32 bits by 32 bits.
*
* Each bitfield comprises NUM_TINY_BLOCKS bits, and refers to the corresponding
* TINY_QUANTUM block within the heap.
*
* The bitfields are used to encode the state of memory within the heap. The header bit indicates
* that the corresponding quantum is the first quantum in a block (either in use or free). The
* in-use bit is set for the header if the block has been handed out (allocated). If the header
* bit is not set, the in-use bit is invalid.
*
* The szone maintains an array of NUM_TINY_SLOTS freelists, each of which is used to hold
* free objects of the corresponding quantum size.
*
* A free block is laid out depending on its size, in order to fit all free
* blocks in 16 bytes, on both 32 and 64 bit platforms. One quantum blocks do
* not store their size in the block, instead relying on the header information
* to determine their size. Blocks of two or more quanta have room to store
* their size in the block, and store it both after the 'next' pointer, and in
* the last 2 bytes of the block.
*
* 1-quantum block
* Offset (32-bit mode) (64-bit mode)
* 0x0 0x0 : previous
* 0x4 0x08 : next
* end end
*
* >1-quantum block
* Offset (32-bit mode) (64-bit mode)
* 0x0 0x0 : previous
* 0x4 0x08 : next
* 0x8 0x10 : size (in quantum counts)
* end - 2 end - 2 : size (in quantum counts)
* end end
*
* All fields are pointer-sized, except for the size which is an unsigned short.
*
*/
#define SHIFT_TINY_QUANTUM 4 // Required for AltiVec
#define TINY_QUANTUM (1 << SHIFT_TINY_QUANTUM)
#define FOLLOWING_TINY_PTR(ptr,msize) (((unsigned char *)(ptr)) + ((msize) << SHIFT_TINY_QUANTUM))
#ifdef __LP64__
#define NUM_TINY_SLOTS 64 // number of slots for free-lists
#else
#define NUM_TINY_SLOTS 32 // number of slots for free-lists
#endif
#define NUM_TINY_BLOCKS 64520
#define SHIFT_TINY_CEIL_BLOCKS 16 // ceil(log2(NUM_TINY_BLOCKS))
#define NUM_TINY_CEIL_BLOCKS (1 << SHIFT_TINY_CEIL_BLOCKS)
#define TINY_BLOCKS_ALIGN (SHIFT_TINY_CEIL_BLOCKS + SHIFT_TINY_QUANTUM) // 20
/*
* Enough room for the data, followed by the bit arrays (2-bits per block)
* plus rounding to the nearest page.
*/
#define CEIL_NUM_TINY_BLOCKS_WORDS (((NUM_TINY_BLOCKS + 31) & ~31) >> 5)
#define TINY_METADATA_SIZE (sizeof(region_trailer_t) + sizeof(tiny_header_inuse_pair_t) * CEIL_NUM_TINY_BLOCKS_WORDS)
#define TINY_REGION_SIZE \
((NUM_TINY_BLOCKS * TINY_QUANTUM + TINY_METADATA_SIZE + vm_page_size - 1) & ~ (vm_page_size - 1))
#define TINY_METADATA_START (NUM_TINY_BLOCKS * TINY_QUANTUM)
/*
* Beginning and end pointers for a region's heap.
*/
#define TINY_REGION_ADDRESS(region) ((void *)(region))
#define TINY_REGION_END(region) ((void *)(((uintptr_t)(region)) + (NUM_TINY_BLOCKS * TINY_QUANTUM)))
/*
* Locate the heap base for a pointer known to be within a tiny region.
*/
#define TINY_REGION_FOR_PTR(_p) ((void *)((uintptr_t)(_p) & ~((1 << TINY_BLOCKS_ALIGN) - 1)))
/*
* Convert between byte and msize units.
*/
#define TINY_BYTES_FOR_MSIZE(_m) ((_m) << SHIFT_TINY_QUANTUM)
#define TINY_MSIZE_FOR_BYTES(_b) ((_b) >> SHIFT_TINY_QUANTUM)
#ifdef __LP64__
# define TINY_FREE_SIZE(ptr) (((msize_t *)(ptr))[8])
#else
# define TINY_FREE_SIZE(ptr) (((msize_t *)(ptr))[4])
#endif
#define TINY_PREVIOUS_MSIZE(ptr) ((msize_t *)(ptr))[-1]
/*
* Layout of a tiny region
*/
typedef uint32_t tiny_block_t[4]; // assert(TINY_QUANTUM == sizeof(tiny_block_t))
typedef struct tiny_header_inuse_pair
{
uint32_t header;
uint32_t inuse;
} tiny_header_inuse_pair_t;
typedef struct region_trailer
{
struct region_trailer *prev;
struct region_trailer *next;
boolean_t recirc_suitable;
unsigned bytes_used;
mag_index_t mag_index;
} region_trailer_t;
typedef struct tiny_region
{
tiny_block_t blocks[NUM_TINY_BLOCKS];
region_trailer_t trailer;
// The interleaved bit arrays comprising the header and inuse bitfields.
// The unused bits of each component in the last pair will be initialized to sentinel values.
tiny_header_inuse_pair_t pairs[CEIL_NUM_TINY_BLOCKS_WORDS];
uint8_t pad[TINY_REGION_SIZE - (NUM_TINY_BLOCKS * sizeof(tiny_block_t)) - TINY_METADATA_SIZE];
} *tiny_region_t;
/*
* Per-region meta data for tiny allocator
*/
#define REGION_TRAILER_FOR_TINY_REGION(r) (&(((tiny_region_t)(r))->trailer))
#define MAGAZINE_INDEX_FOR_TINY_REGION(r) (REGION_TRAILER_FOR_TINY_REGION(r)->mag_index)
#define BYTES_USED_FOR_TINY_REGION(r) (REGION_TRAILER_FOR_TINY_REGION(r)->bytes_used)
/*
* Locate the block header for a pointer known to be within a tiny region.
*/
#define TINY_BLOCK_HEADER_FOR_PTR(_p) ((void *)&(((tiny_region_t)TINY_REGION_FOR_PTR(_p))->pairs))
/*
* Locate the inuse map for a given block header pointer.
*/
#define TINY_INUSE_FOR_HEADER(_h) ((void *)&(((tiny_header_inuse_pair_t *)(_h))->inuse))
/*
* Compute the bitmap index for a pointer known to be within a tiny region.
*/
#define TINY_INDEX_FOR_PTR(_p) (((uintptr_t)(_p) >> SHIFT_TINY_QUANTUM) & (NUM_TINY_CEIL_BLOCKS - 1))
#define TINY_CACHE 1 // This governs a last-free cache of 1 that bypasses the free-list
#if ! TINY_CACHE
#warning TINY_CACHE turned off
#endif
#define TINY_REGION_PAYLOAD_BYTES (NUM_TINY_BLOCKS * TINY_QUANTUM)
/********************* DEFINITIONS for small ************************/
/*
* Memory in the Small range is allocated from regions (heaps) pointed to by the szone's hashed_regions
* pointer.
*
* Each region is laid out as a heap, followed by the metadata array, all within an 8MB (2^23) block.
* The array is arranged as an array of shorts, one for each SMALL_QUANTUM in the heap.
* This means there are 16320 512-blocks and the array is 16320*2 bytes, which totals 8388480, leaving
* 128 bytes unused.
*
* The MSB of each short is set for the first quantum in a free block. The low 15 bits encode the
* block size (in SMALL_QUANTUM units), or are zero if the quantum is not the first in a block.
*
* The szone maintains an array of 32 freelists, each of which is used to hold free objects
* of the corresponding quantum size.
*
* A free block is laid out as:
*
* Offset (32-bit mode) (64-bit mode)
* 0x0 0x0 : previous
* 0x4 0x08 : next
* 0x8 0x10 : size (in quantum counts)
* end - 2 end - 2 : size (in quantum counts)
* end end
*
* All fields are pointer-sized, except for the size which is an unsigned short.
*
*/
#define SMALL_IS_FREE (1 << 15)
#define SHIFT_SMALL_QUANTUM (SHIFT_TINY_QUANTUM + 5) // 9
#define SMALL_QUANTUM (1 << SHIFT_SMALL_QUANTUM) // 512 bytes
#define FOLLOWING_SMALL_PTR(ptr,msize) (((unsigned char *)(ptr)) + ((msize) << SHIFT_SMALL_QUANTUM))
/*
* The number of slots in the free-list for small blocks. To avoid going to
* vm system as often on large memory machines, increase the number of free list
* spots above some amount of RAM installed in the system.
*/
#define NUM_SMALL_SLOTS 32
#define NUM_SMALL_SLOTS_LARGEMEM 256
#define SMALL_BITMAP_WORDS 8
/*
* We can only represent up to 1<<15 for msize; but we choose to stay even below that to avoid the
* convention msize=0 => msize = (1<<15)
*/
#define NUM_SMALL_BLOCKS 16320
#define SHIFT_SMALL_CEIL_BLOCKS 14 // ceil(log2(NUM_SMALL_BLOCKs))
#define NUM_SMALL_CEIL_BLOCKS (1 << SHIFT_SMALL_CEIL_BLOCKS)
#define SMALL_BLOCKS_ALIGN (SHIFT_SMALL_CEIL_BLOCKS + SHIFT_SMALL_QUANTUM) // 23
#define SMALL_METADATA_SIZE (sizeof(region_trailer_t) + NUM_SMALL_BLOCKS * sizeof(msize_t))
#define SMALL_REGION_SIZE \
((NUM_SMALL_BLOCKS * SMALL_QUANTUM + SMALL_METADATA_SIZE + vm_page_size - 1) & ~ (vm_page_size - 1))
#define SMALL_METADATA_START (NUM_SMALL_BLOCKS * SMALL_QUANTUM)
/*
* Beginning and end pointers for a region's heap.
*/
#define SMALL_REGION_ADDRESS(region) ((unsigned char *)region)
#define SMALL_REGION_END(region) (SMALL_REGION_ADDRESS(region) + (NUM_SMALL_BLOCKS * SMALL_QUANTUM))
/*
* Locate the heap base for a pointer known to be within a small region.
*/
#define SMALL_REGION_FOR_PTR(_p) ((void *)((uintptr_t)(_p) & ~((1 << SMALL_BLOCKS_ALIGN) - 1)))
/*
* Convert between byte and msize units.
*/
#define SMALL_BYTES_FOR_MSIZE(_m) ((_m) << SHIFT_SMALL_QUANTUM)
#define SMALL_MSIZE_FOR_BYTES(_b) ((_b) >> SHIFT_SMALL_QUANTUM)
#define SMALL_PREVIOUS_MSIZE(ptr) ((msize_t *)(ptr))[-1]
/*
* Layout of a small region
*/
typedef uint32_t small_block_t[SMALL_QUANTUM/sizeof(uint32_t)];
typedef struct small_region
{
small_block_t blocks[NUM_SMALL_BLOCKS];
region_trailer_t trailer;
msize_t small_meta_words[NUM_SMALL_BLOCKS];
uint8_t pad[SMALL_REGION_SIZE - (NUM_SMALL_BLOCKS * sizeof(small_block_t)) - SMALL_METADATA_SIZE];
} *small_region_t;
/*
* Per-region meta data for small allocator
*/
#define REGION_TRAILER_FOR_SMALL_REGION(r) (&(((small_region_t)(r))->trailer))
#define MAGAZINE_INDEX_FOR_SMALL_REGION(r) (REGION_TRAILER_FOR_SMALL_REGION(r)->mag_index)
#define BYTES_USED_FOR_SMALL_REGION(r) (REGION_TRAILER_FOR_SMALL_REGION(r)->bytes_used)
/*
* Locate the metadata base for a pointer known to be within a small region.
*/
#define SMALL_META_HEADER_FOR_PTR(_p) (((small_region_t)SMALL_REGION_FOR_PTR(_p))->small_meta_words)
/*
* Compute the metadata index for a pointer known to be within a small region.
*/
#define SMALL_META_INDEX_FOR_PTR(_p) (((uintptr_t)(_p) >> SHIFT_SMALL_QUANTUM) & (NUM_SMALL_CEIL_BLOCKS - 1))
/*
* Find the metadata word for a pointer known to be within a small region.
*/
#define SMALL_METADATA_FOR_PTR(_p) (SMALL_META_HEADER_FOR_PTR(_p) + SMALL_META_INDEX_FOR_PTR(_p))
/*
* Determine whether a pointer known to be within a small region points to memory which is free.
*/
#define SMALL_PTR_IS_FREE(_p) (*SMALL_METADATA_FOR_PTR(_p) & SMALL_IS_FREE)
/*
* Extract the msize value for a pointer known to be within a small region.
*/
#define SMALL_PTR_SIZE(_p) (*SMALL_METADATA_FOR_PTR(_p) & ~SMALL_IS_FREE)
#define PROTECT_SMALL 0 // Should be 0: 1 is too slow for normal use
#define SMALL_CACHE 1
#if !SMALL_CACHE
#warning SMALL_CACHE turned off
#endif
#define SMALL_REGION_PAYLOAD_BYTES (NUM_SMALL_BLOCKS * SMALL_QUANTUM)
/************************* DEFINITIONS for large ****************************/
#define LARGE_THRESHOLD (15 * 1024) // strictly above this use "large"
#define LARGE_THRESHOLD_LARGEMEM (127 * 1024)
#if (LARGE_THRESHOLD > NUM_SMALL_SLOTS * SMALL_QUANTUM)
#error LARGE_THRESHOLD should always be less than NUM_SMALL_SLOTS * SMALL_QUANTUM
#endif
#if (LARGE_THRESHOLD_LARGEMEM > NUM_SMALL_SLOTS_LARGEMEM * SMALL_QUANTUM)
#error LARGE_THRESHOLD_LARGEMEM should always be less than NUM_SMALL_SLOTS * SMALL_QUANTUM
#endif
/*
* When all memory is touched after a copy, vm_copy() is always a lose
* But if the memory is only read, vm_copy() wins over memmove() at 3 or 4 pages
* (on a G3/300MHz)
*
* This must be larger than LARGE_THRESHOLD
*/
#define VM_COPY_THRESHOLD (40 * 1024)
#define VM_COPY_THRESHOLD_LARGEMEM (128 * 1024)
typedef struct {
vm_address_t address;
vm_size_t size;
boolean_t did_madvise_reusable;
} large_entry_t;
#define LARGE_CACHE 1
#if !LARGE_CACHE
#warning LARGE_CACHE turned off
#endif
#if defined(__LP64__)
#define LARGE_ENTRY_CACHE_SIZE 16
#define LARGE_CACHE_SIZE_LIMIT ((vm_size_t)0x80000000) /* 2Gb */
#else
#define LARGE_ENTRY_CACHE_SIZE 8
#define LARGE_CACHE_SIZE_LIMIT ((vm_size_t)0x02000000) /* 32Mb */
#endif
#define LARGE_CACHE_SIZE_ENTRY_LIMIT (LARGE_CACHE_SIZE_LIMIT/LARGE_ENTRY_CACHE_SIZE)
/*******************************************************************************
* Definitions for region hash
******************************************************************************/
typedef void * region_t;
typedef region_t * rgnhdl_t; /* A pointer into hashed_regions array. */
#define INITIAL_NUM_REGIONS_SHIFT 6 // log2(INITIAL_NUM_REGIONS)
#define INITIAL_NUM_REGIONS (1 << INITIAL_NUM_REGIONS_SHIFT) // Must be a power of 2!
#define HASHRING_OPEN_ENTRY ((region_t) 0) // Initial value and sentinel marking end of collision chain
#define HASHRING_REGION_DEALLOCATED ((region_t)-1) // Region at this slot reclaimed by OS
#define HASH_BLOCKS_ALIGN TINY_BLOCKS_ALIGN // MIN( TINY_BLOCKS_ALIGN, SMALL_BLOCKS_ALIGN, ... )
typedef struct region_hash_generation {
size_t num_regions_allocated;
size_t num_regions_allocated_shift; // log2(num_regions_allocated)
region_t *hashed_regions; // hashed by location
struct region_hash_generation *nextgen;
} region_hash_generation_t;
/*******************************************************************************
* Per-processor magazine for tiny and small allocators
******************************************************************************/
typedef struct { // vm_allocate()'d, so the array of magazines is page-aligned to begin with.
// Take magazine_lock first, Depot lock when needed for recirc, then szone->{tiny,small}_regions_lock when needed for alloc
pthread_lock_t magazine_lock CACHE_ALIGN;
// One element deep "death row", optimizes malloc/free/malloc for identical size.
void *mag_last_free; // low SHIFT_{TINY,SMALL}_QUANTUM bits indicate the msize
region_t mag_last_free_rgn; // holds the region for mag_last_free
free_list_t *mag_free_list[256]; // assert( 256 >= MAX( NUM_TINY_SLOTS, NUM_SMALL_SLOTS_LARGEMEM ))
unsigned mag_bitmap[8]; // assert( sizeof(mag_bitmap) << 3 >= sizeof(mag_free_list)/sizeof(free_list_t) )
// the last free region in the last block is treated as a big block in use that is not accounted for
size_t mag_bytes_free_at_end;
region_t mag_last_region; // Valid iff mag_bytes_free_at_end > 0
// bean counting ...
unsigned mag_num_objects;
size_t mag_num_bytes_in_objects;
size_t num_bytes_in_magazine;
// recirculation list -- invariant: all regions owned by this magazine that meet the emptiness criteria
// are located nearer to the head of the list than any region that doesn't satisfy that criteria.
// Doubly linked list for efficient extraction.
unsigned recirculation_entries;
region_trailer_t *firstNode;
region_trailer_t *lastNode;
#if __LP64__
uint64_t pad[49]; // So sizeof(magazine_t) is 2560 bytes. FIXME: assert this at compile time
#else
uint32_t pad[45]; // So sizeof(magazine_t) is 1280 bytes. FIXME: assert this at compile time
#endif
} magazine_t;
#define TINY_MAX_MAGAZINES 16 /* MUST BE A POWER OF 2! */
#define TINY_MAGAZINE_PAGED_SIZE \
(((sizeof(magazine_t) * (TINY_MAX_MAGAZINES + 1)) + vm_page_size - 1) &\
~ (vm_page_size - 1)) /* + 1 for the Depot */
#define SMALL_MAX_MAGAZINES 16 /* MUST BE A POWER OF 2! */
#define SMALL_MAGAZINE_PAGED_SIZE \
(((sizeof(magazine_t) * (SMALL_MAX_MAGAZINES + 1)) + vm_page_size - 1) &\
~ (vm_page_size - 1)) /* + 1 for the Depot */
#define DEPOT_MAGAZINE_INDEX -1
/****************************** zone itself ***********************************/
/*
* Note that objects whose adddress are held in pointers here must be pursued
* individually in the {tiny,small}_in_use_enumeration() routines. See for
* example the treatment of region_hash_generation and tiny_magazines below.
*/
typedef struct szone_s { // vm_allocate()'d, so page-aligned to begin with.
malloc_zone_t basic_zone;
pthread_key_t cpu_id_key;
unsigned debug_flags;
void *log_address;
/* Regions for tiny objects */
pthread_lock_t tiny_regions_lock CACHE_ALIGN;
size_t num_tiny_regions;
size_t num_tiny_regions_dealloc;
region_hash_generation_t *tiny_region_generation;
region_hash_generation_t trg[2];
int num_tiny_magazines;
unsigned num_tiny_magazines_mask;
int num_tiny_magazines_mask_shift;
magazine_t *tiny_magazines; // array of per-processor magazines
/* Regions for small objects */
pthread_lock_t small_regions_lock CACHE_ALIGN;
size_t num_small_regions;
size_t num_small_regions_dealloc;
region_hash_generation_t *small_region_generation;
region_hash_generation_t srg[2];
unsigned num_small_slots; // determined by physmem size
int num_small_magazines;
unsigned num_small_magazines_mask;
int num_small_magazines_mask_shift;
magazine_t *small_magazines; // array of per-processor magazines
/* large objects: all the rest */
pthread_lock_t large_szone_lock CACHE_ALIGN; // One customer at a time for large
unsigned num_large_objects_in_use;
unsigned num_large_entries;
large_entry_t *large_entries; // hashed by location; null entries don't count
size_t num_bytes_in_large_objects;
#if LARGE_CACHE
int large_entry_cache_oldest;
int large_entry_cache_newest;
large_entry_t large_entry_cache[LARGE_ENTRY_CACHE_SIZE]; // "death row" for large malloc/free
boolean_t large_legacy_reset_mprotect;
size_t large_entry_cache_hoard_bytes;
size_t large_entry_cache_hoard_lmit;
#endif
/* flag and limits pertaining to altered malloc behavior for systems with
large amounts of physical memory */
unsigned is_largemem;
unsigned large_threshold;
unsigned vm_copy_threshold;
/* security cookie */
uintptr_t cookie;
/* Initial region list */
region_t initial_tiny_regions[INITIAL_NUM_REGIONS];
region_t initial_small_regions[INITIAL_NUM_REGIONS];
/* The purgeable zone constructed by create_purgeable_zone() would like to hand off tiny and small
* allocations to the default scalable zone. Record the latter as the "helper" zone here. */
struct szone_s *helper_zone;
} szone_t;
#define SZONE_PAGED_SIZE ((sizeof(szone_t) + vm_page_size - 1) & ~ (vm_page_size - 1))
#if DEBUG_MALLOC || DEBUG_CLIENT
static void szone_sleep(void);
#endif
__private_extern__ void malloc_error_break(void);
// msg prints after fmt, ...
static NOINLINE void szone_error(szone_t *szone, int is_corruption, const char *msg, const void *ptr, const char *fmt, ...)
__printflike(5, 6);
static void protect(void *address, size_t size, unsigned protection, unsigned debug_flags);
static void *allocate_pages(szone_t *szone, size_t size, unsigned char align, unsigned debug_flags,
int vm_page_label);
static void deallocate_pages(szone_t *szone, void *addr, size_t size, unsigned debug_flags);
static int madvise_free_range(szone_t *szone, region_t r, uintptr_t pgLo, uintptr_t pgHi);
static kern_return_t _szone_default_reader(task_t task, vm_address_t address, vm_size_t size, void **ptr);
static INLINE mag_index_t mag_get_thread_index(szone_t *szone) ALWAYSINLINE;
static magazine_t *mag_lock_zine_for_region_trailer(szone_t *szone, magazine_t *magazines, region_trailer_t *trailer,
mag_index_t mag_index);
static INLINE rgnhdl_t hash_lookup_region_no_lock(region_t *regions, size_t num_entries, size_t shift, region_t r)
ALWAYSINLINE;
static void hash_region_insert_no_lock(region_t *regions, size_t num_entries, size_t shift, region_t r);
static region_t *hash_regions_alloc_no_lock(szone_t *szone, size_t num_entries);
static region_t *hash_regions_grow_no_lock(szone_t *szone, region_t *regions, size_t old_size,
size_t *mutable_shift, size_t *new_size);
static INLINE uintptr_t free_list_gen_checksum(uintptr_t ptr) ALWAYSINLINE;
static INLINE uintptr_t free_list_checksum_ptr(szone_t *szone, void *p) ALWAYSINLINE;
static INLINE void *free_list_unchecksum_ptr(szone_t *szone, ptr_union *ptr) ALWAYSINLINE;
static unsigned free_list_count(szone_t *szone, free_list_t *ptr);
static INLINE void recirc_list_extract(szone_t *szone, magazine_t *mag_ptr, region_trailer_t *node) ALWAYSINLINE;
static INLINE void recirc_list_splice_last(szone_t *szone, magazine_t *mag_ptr, region_trailer_t *node) ALWAYSINLINE;
static INLINE void recirc_list_splice_first(szone_t *szone, magazine_t *mag_ptr, region_trailer_t *node) ALWAYSINLINE;
static INLINE void BITARRAY_SET(uint32_t *bits, msize_t index) ALWAYSINLINE;
static INLINE void BITARRAY_CLR(uint32_t *bits, msize_t index) ALWAYSINLINE;
static INLINE boolean_t BITARRAY_BIT(uint32_t *bits, msize_t index) ALWAYSINLINE;
static msize_t get_tiny_free_size(const void *ptr);
static msize_t get_tiny_previous_free_msize(const void *ptr);
static INLINE msize_t get_tiny_meta_header(const void *ptr, boolean_t *is_free) ALWAYSINLINE;
static INLINE void set_tiny_meta_header_in_use(const void *ptr, msize_t msize) ALWAYSINLINE;
static INLINE void set_tiny_meta_header_in_use_1(const void *ptr) ALWAYSINLINE;
static INLINE void set_tiny_meta_header_middle(const void *ptr) ALWAYSINLINE;
static INLINE void set_tiny_meta_header_free(const void *ptr, msize_t msize) ALWAYSINLINE;
static INLINE boolean_t tiny_meta_header_is_free(const void *ptr) ALWAYSINLINE;
static INLINE void *tiny_previous_preceding_free(void *ptr, msize_t *prev_msize) ALWAYSINLINE;
static void tiny_free_list_add_ptr(szone_t *szone, magazine_t *tiny_mag_ptr, void *ptr, msize_t msize);
static void tiny_free_list_remove_ptr(szone_t *szone, magazine_t *tiny_mag_ptr, void *ptr, msize_t msize);
static INLINE region_t tiny_region_for_ptr_no_lock(szone_t *szone, const void *ptr) ALWAYSINLINE;
static void tiny_finalize_region(szone_t *szone, magazine_t *tiny_mag_ptr);
static int tiny_free_detach_region(szone_t *szone, magazine_t *tiny_mag_ptr, region_t r);
static size_t tiny_free_reattach_region(szone_t *szone, magazine_t *tiny_mag_ptr, region_t r);
static void tiny_free_scan_madvise_free(szone_t *szone, magazine_t *depot_ptr, region_t r);
static void tiny_free_try_depot_unmap_no_lock(szone_t *szone, magazine_t *depot_ptr, region_trailer_t *node);
static void tiny_free_do_recirc_to_depot(szone_t *szone, magazine_t *tiny_mag_ptr, mag_index_t mag_index);
static boolean_t tiny_get_region_from_depot(szone_t *szone, magazine_t *tiny_mag_ptr, mag_index_t mag_index);
static INLINE void tiny_free_no_lock(szone_t *szone, magazine_t *tiny_mag_ptr, mag_index_t mag_index, region_t region,
void *ptr, msize_t msize) ALWAYSINLINE;
static void *tiny_malloc_from_region_no_lock(szone_t *szone, magazine_t *tiny_mag_ptr, mag_index_t mag_index,
msize_t msize);
static boolean_t tiny_try_realloc_in_place(szone_t *szone, void *ptr, size_t old_size, size_t new_size);
static boolean_t tiny_check_region(szone_t *szone, region_t region);
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);
static void *tiny_malloc_from_free_list(szone_t *szone, magazine_t *tiny_mag_ptr, mag_index_t mag_index,
msize_t msize);
static INLINE void *tiny_malloc_should_clear(szone_t *szone, msize_t msize, boolean_t cleared_requested) ALWAYSINLINE;
static INLINE void free_tiny(szone_t *szone, void *ptr, region_t tiny_region, size_t known_size) ALWAYSINLINE;
static void print_tiny_free_list(szone_t *szone);
static void print_tiny_region(boolean_t verbose, region_t region, size_t bytes_at_end);
static boolean_t tiny_free_list_check(szone_t *szone, grain_t slot);
static INLINE void small_meta_header_set_is_free(msize_t *meta_headers, unsigned index, msize_t msize) ALWAYSINLINE;
static INLINE void small_meta_header_set_in_use(msize_t *meta_headers, msize_t index, msize_t msize) ALWAYSINLINE;
static INLINE void small_meta_header_set_middle(msize_t *meta_headers, msize_t index) ALWAYSINLINE;
static void small_free_list_add_ptr(szone_t *szone, magazine_t *small_mag_ptr, void *ptr, msize_t msize);
static void small_free_list_remove_ptr(szone_t *szone, magazine_t *small_mag_ptr, void *ptr, msize_t msize);
static INLINE region_t small_region_for_ptr_no_lock(szone_t *szone, const void *ptr) ALWAYSINLINE;
static void small_finalize_region(szone_t *szone, magazine_t *small_mag_ptr);
static int small_free_detach_region(szone_t *szone, magazine_t *small_mag_ptr, region_t r);
static size_t small_free_reattach_region(szone_t *szone, magazine_t *small_mag_ptr, region_t r);
static void small_free_scan_depot_madvise_free(szone_t *szone, magazine_t *depot_ptr, region_t r);
static void small_free_try_depot_unmap_no_lock(szone_t *szone, magazine_t *depot_ptr, region_trailer_t *node);
static void small_free_do_recirc_to_depot(szone_t *szone, magazine_t *small_mag_ptr, mag_index_t mag_index);
static boolean_t small_get_region_from_depot(szone_t *szone, magazine_t *small_mag_ptr, mag_index_t mag_index);
static INLINE void small_free_no_lock(szone_t *szone, magazine_t *small_mag_ptr, mag_index_t mag_index, region_t region,
void *ptr, msize_t msize) ALWAYSINLINE;
static void *small_malloc_from_region_no_lock(szone_t *szone, magazine_t *small_mag_ptr, mag_index_t mag_index,
msize_t msize);
static boolean_t small_try_realloc_in_place(szone_t *szone, void *ptr, size_t old_size, size_t new_size);
static boolean_t small_check_region(szone_t *szone, region_t region);
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);
static void *small_malloc_from_free_list(szone_t *szone, magazine_t *small_mag_ptr, mag_index_t mag_index,
msize_t msize);
static INLINE void *small_malloc_should_clear(szone_t *szone, msize_t msize, boolean_t cleared_requested) ALWAYSINLINE;
static INLINE void free_small(szone_t *szone, void *ptr, region_t small_region, size_t known_size) ALWAYSINLINE;
static void print_small_free_list(szone_t *szone);
static void print_small_region(szone_t *szone, boolean_t verbose, region_t region, size_t bytes_at_end);
static boolean_t small_free_list_check(szone_t *szone, grain_t grain);
#if DEBUG_MALLOC
static void large_debug_print(szone_t *szone);
#endif
static large_entry_t *large_entry_for_pointer_no_lock(szone_t *szone, const void *ptr);
static void large_entry_insert_no_lock(szone_t *szone, large_entry_t range);
static INLINE void large_entries_rehash_after_entry_no_lock(szone_t *szone, large_entry_t *entry) ALWAYSINLINE;
static INLINE large_entry_t *large_entries_alloc_no_lock(szone_t *szone, unsigned num) ALWAYSINLINE;
static void large_entries_free_no_lock(szone_t *szone, large_entry_t *entries, unsigned num,
vm_range_t *range_to_deallocate);
static large_entry_t *large_entries_grow_no_lock(szone_t *szone, vm_range_t *range_to_deallocate);
static vm_range_t large_entry_free_no_lock(szone_t *szone, large_entry_t *entry);
static NOINLINE 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);
static void *large_malloc(szone_t *szone, size_t num_pages, unsigned char alignment, boolean_t cleared_requested);
static NOINLINE void free_large(szone_t *szone, void *ptr);
static INLINE int large_try_realloc_in_place(szone_t *szone, void *ptr, size_t old_size, size_t new_size) ALWAYSINLINE;
/*
* Mark these NOINLINE to avoid bloating the purgeable zone call backs
*/
static NOINLINE void szone_free(szone_t *szone, void *ptr);
static NOINLINE void *szone_malloc_should_clear(szone_t *szone, size_t size, boolean_t cleared_requested);
static NOINLINE void *szone_malloc(szone_t *szone, size_t size);
static NOINLINE void *szone_calloc(szone_t *szone, size_t num_items, size_t size);
static NOINLINE void *szone_valloc(szone_t *szone, size_t size);
static NOINLINE size_t szone_size_try_large(szone_t *szone, const void *ptr);
static NOINLINE size_t szone_size(szone_t *szone, const void *ptr);
static NOINLINE void *szone_realloc(szone_t *szone, void *ptr, size_t new_size);
static NOINLINE void *szone_memalign(szone_t *szone, size_t alignment, size_t size);
static NOINLINE void szone_free_definite_size(szone_t *szone, void *ptr, size_t size);
static NOINLINE unsigned szone_batch_malloc(szone_t *szone, size_t size, void **results, unsigned count);
static NOINLINE void szone_batch_free(szone_t *szone, void **to_be_freed, unsigned count);
static void szone_destroy(szone_t *szone);
static NOINLINE size_t szone_good_size(szone_t *szone, size_t size);
static NOINLINE boolean_t szone_check_all(szone_t *szone, const char *function);
static boolean_t szone_check(szone_t *szone);
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);
static NOINLINE void szone_print(szone_t *szone, boolean_t verbose);
static void szone_log(malloc_zone_t *zone, void *log_address);
static void szone_force_lock(szone_t *szone);
static void szone_force_unlock(szone_t *szone);
static boolean_t szone_locked(szone_t *szone);
static void szone_statistics(szone_t *szone, malloc_statistics_t *stats);
static void purgeable_free(szone_t *szone, void *ptr);
static void *purgeable_malloc(szone_t *szone, size_t size);
static void *purgeable_calloc(szone_t *szone, size_t num_items, size_t size);
static void *purgeable_valloc(szone_t *szone, size_t size);
static size_t purgeable_size(szone_t *szone, const void *ptr);
static void *purgeable_realloc(szone_t *szone, void *ptr, size_t new_size);
static void *purgeable_memalign(szone_t *szone, size_t alignment, size_t size);
static void purgeable_free_definite_size(szone_t *szone, void *ptr, size_t size);
static unsigned purgeable_batch_malloc(szone_t *szone, size_t size, void **results, unsigned count);
static void purgeable_batch_free(szone_t *szone, void **to_be_freed, unsigned count);
static void purgeable_destroy(szone_t *szone);
static size_t purgeable_good_size(szone_t *szone, size_t size);
static boolean_t purgeable_check(szone_t *szone);
static kern_return_t purgeable_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);
static void purgeable_print(szone_t *szone, boolean_t verbose);
static void purgeable_log(malloc_zone_t *zone, void *log_address);
static void purgeable_force_lock(szone_t *szone);
static void purgeable_force_unlock(szone_t *szone);
static boolean_t purgeable_locked(szone_t *szone);
static void purgeable_statistics(szone_t *szone, malloc_statistics_t *stats);
static void *frozen_malloc(szone_t *zone, size_t new_size);
static void *frozen_calloc(szone_t *zone, size_t num_items, size_t size);
static void *frozen_valloc(szone_t *zone, size_t new_size);
static void *frozen_realloc(szone_t *zone, void *ptr, size_t new_size);
static void frozen_free(szone_t *zone, void *ptr);
static void frozen_destroy(szone_t *zone);
#define SZONE_LOCK(szone) \
do { \
LOCK(szone->large_szone_lock); \
} while (0)
#define SZONE_UNLOCK(szone) \
do { \
UNLOCK(szone->large_szone_lock); \
} while (0)
#define SZONE_TRY_LOCK(szone) \
TRY_LOCK(szone->large_szone_lock);
#define SZONE_MAGAZINE_PTR_LOCK(szone, mag_ptr) \
do { \
LOCK(mag_ptr->magazine_lock); \
} while(0)
#define SZONE_MAGAZINE_PTR_UNLOCK(szone, mag_ptr) \
do { \
UNLOCK(mag_ptr->magazine_lock); \
} while(0)
#define SZONE_MAGAZINE_PTR_TRY_LOCK(szone, mag_ptr) \
TRY_LOCK(mag_ptr->magazine_lock);
#if DEBUG_MALLOC
# define LOG(szone,ptr) \
(szone->log_address && (((uintptr_t)szone->log_address == -1) || \
(szone->log_address == (void *)(ptr))))
#else
# define LOG(szone,ptr) 0
#endif
#if DEBUG_MALLOC || DEBUG_CLIENT
# define CHECK(szone,fun) \
if ((szone)->debug_flags & CHECK_REGIONS) \
szone_check_all(szone, fun)
#else
# define CHECK(szone,fun) \
do {} while (0)
#endif
/********************* VERY LOW LEVEL UTILITIES ************************/
#if DEBUG_MALLOC || DEBUG_CLIENT
static void
szone_sleep(void)
{
if (getenv("MallocErrorSleep")) {
_malloc_printf(ASL_LEVEL_NOTICE, "*** sleeping to help debug\n");
sleep(3600); // to help debug
}
}
#endif
extern const char *__crashreporter_info__;
// msg prints after fmt, ...
static NOINLINE void
szone_error(szone_t *szone, int is_corruption, const char *msg, const void *ptr, const char *fmt, ...)
{
va_list ap;
_SIMPLE_STRING b = _simple_salloc();
if (szone) SZONE_UNLOCK(szone); // FIXME: unlock magazine and region locks?
if (b) {
if (fmt) {
va_start(ap, fmt);
_simple_vsprintf(b, fmt, ap);
va_end(ap);
}
if (ptr) {
_simple_sprintf(b, "*** error for object %p: %s\n", ptr, msg);
} else {
_simple_sprintf(b, "*** error: %s\n", msg);
}
malloc_printf("%s*** set a breakpoint in malloc_error_break to debug\n", _simple_string(b));
} else {
/*
* Should only get here if vm_allocate() can't get a single page of
* memory, implying _simple_asl_log() would also fail. So we just
* print to the file descriptor.
*/
if (fmt) {
va_start(ap, fmt);
_malloc_vprintf(MALLOC_PRINTF_NOLOG, fmt, ap);
va_end(ap);
}
if (ptr) {
_malloc_printf(MALLOC_PRINTF_NOLOG, "*** error for object %p: %s\n", ptr, msg);
} else {
_malloc_printf(MALLOC_PRINTF_NOLOG, "*** error: %s\n", msg);
}
_malloc_printf(MALLOC_PRINTF_NOLOG, "*** set a breakpoint in malloc_error_break to debug\n");
}
malloc_error_break();
#if DEBUG_MALLOC
szone_print(szone, 1);
szone_sleep();
#endif
#if DEBUG_CLIENT
szone_sleep();
#endif
// Call abort() if this is a memory corruption error and the abort on
// corruption flag is set, or if any error should abort.
if ((is_corruption && (szone->debug_flags & SCALABLE_MALLOC_ABORT_ON_CORRUPTION)) ||
(szone->debug_flags & SCALABLE_MALLOC_ABORT_ON_ERROR)) {
__crashreporter_info__ = b ? _simple_string(b) : msg;
abort();
} else if (b) {
_simple_sfree(b);
}
}
static void
protect(void *address, size_t size, unsigned protection, unsigned debug_flags)
{
kern_return_t err;
if (!(debug_flags & SCALABLE_MALLOC_DONT_PROTECT_PRELUDE)) {
err = vm_protect(mach_task_self(), (vm_address_t)(uintptr_t)address - vm_page_size, vm_page_size, 0, protection);
if (err) {
malloc_printf("*** can't protect(%p) region for prelude guard page at %p\n",
protection,(uintptr_t)address - (1 << vm_page_shift));
}
}
if (!(debug_flags & SCALABLE_MALLOC_DONT_PROTECT_POSTLUDE)) {
err = vm_protect(mach_task_self(), (vm_address_t)(uintptr_t)address + size, vm_page_size, 0, protection);
if (err) {
malloc_printf("*** can't protect(%p) region for postlude guard page at %p\n",
protection, (uintptr_t)address + size);
}
}
}
static void *
allocate_pages(szone_t *szone, size_t size, unsigned char align, unsigned debug_flags, int vm_page_label)
{
// align specifies a desired alignment (as a log) or 0 if no alignment requested
void *vm_addr;
uintptr_t addr = 0, aligned_address;
boolean_t add_guard_pages = debug_flags & SCALABLE_MALLOC_ADD_GUARD_PAGES;
boolean_t purgeable = debug_flags & SCALABLE_MALLOC_PURGEABLE;
size_t allocation_size = round_page(size);
size_t delta;
int flags = VM_MAKE_TAG(vm_page_label);
if (align) add_guard_pages = 0; // too cumbersome to deal with that
if (!allocation_size) allocation_size = 1 << vm_page_shift;
if (add_guard_pages) allocation_size += 2 * (1 << vm_page_shift);
if (align) allocation_size += (size_t)1 << align;
if (purgeable) flags |= VM_FLAGS_PURGABLE;
if (allocation_size < size) // size_t arithmetic wrapped!
return NULL;
vm_addr = mmap(0, allocation_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, flags, 0);
if ((uintptr_t)vm_addr == -1) {
szone_error(szone, 0, "can't allocate region", NULL, "*** mmap(size=%lu) failed (error code=%d)\n",
allocation_size, errno);
return NULL;
}
addr = (uintptr_t)vm_addr;
if (align) {
aligned_address = (addr + ((uintptr_t)1 << align) - 1) & ~ (((uintptr_t)1 << align) - 1);
if (aligned_address != addr) {
delta = aligned_address - addr;
if (munmap((void *)addr, delta) == -1)
malloc_printf("*** munmap unaligned header failed with %d\n", errno);
addr = aligned_address;
allocation_size -= delta;
}
if (allocation_size > size) {
if (munmap((void *)(addr + size), allocation_size - size) == -1)
malloc_printf("*** munmap unaligned footer failed with %d\n", errno);
}
}
if (add_guard_pages) {
addr += (uintptr_t)1 << vm_page_shift;
protect((void *)addr, size, 0, debug_flags);
}
return (void *)addr;
}
static void
deallocate_pages(szone_t *szone, void *addr, size_t size, unsigned debug_flags)
{
int err;
boolean_t add_guard_pages = debug_flags & SCALABLE_MALLOC_ADD_GUARD_PAGES;
if (add_guard_pages) {
addr = (void *)((uintptr_t)addr - (1 << vm_page_shift));
size += 2 * (1 << vm_page_shift);
}
err = munmap(addr, size);
if ((err == -1) && szone)