3
Copyright (C) 1995 Pascal Haible. All Rights Reserved.
5
Permission is hereby granted, free of charge, to any person obtaining a
6
copy of this software and associated documentation files (the "Software"),
7
to deal in the Software without restriction, including without limitation
8
the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
and/or sell copies of the Software, and to permit persons to whom the
10
Software is furnished to do so, subject to the following conditions:
12
The above copyright notice and this permission notice shall be included in
13
all copies or substantial portions of the Software.
15
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
PASCAL HAIBLE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
20
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
Except as contained in this notice, the name of Pascal Haible shall
24
not be used in advertising or otherwise to promote the sale, use or other
25
dealings in this Software without prior written authorization from
29
/* $XFree86: xc/programs/Xserver/os/xalloc.c,v 3.33 2002/04/04 14:05:57 eich Exp $ */
31
/* Only used if INTERNAL_MALLOC is defined
32
* - otherwise xalloc() in utils.c is used
34
#ifdef INTERNAL_MALLOC
36
#include <stdlib.h> /* for malloc() etc. */
46
extern Bool Must_have_memory;
49
***** New malloc approach for the X server *****
52
* Some statistics about memory allocation of the X server
53
* The test session included several clients of different size, including
54
* xv, emacs and xpaint with a new canvas of 3000x2000, zoom 5.
55
* All clients were running together.
56
* A protocolling version of Xalloc recorded 318917 allocating actions
57
* (191573 Xalloc, 85942 XNFalloc, 41438 Xrealloc, 279727 Xfree).
58
* Results grouped by size, excluding the next lower size
59
* (i.e. size=32 means 16<size<=32):
61
* size nr of alloc max nr of blocks allocated together
82
* The most used sizes:
110
* Conclusions: more than the half of all allocations are <= 32 bytes.
111
* But of these about 150,000 blocks, only a maximum of about 6,000 are
112
* allocated together (including memory leaks..).
113
* On the other side, only 935 of the 191573 or 0.5% were larger than 8kB
114
* (362 or 0.2% larger than 16k).
116
* What makes the server really grow is the fragmentation of the heap,
117
* and the fact that it can't shrink.
118
* To cure this, we do the following:
119
* - large blocks (>=11k) are mmapped on xalloc, and unmapped on xfree,
120
* so we don't need any free lists etc.
121
* As this needs 2 system calls, we only do this for the quite
122
* infrequent large (>=11k) blocks.
123
* - instead of reinventing the wheel, we use system malloc for medium
124
* sized blocks (>256, <11k).
125
* - for small blocks (<=256) we use an other approach:
126
* As we need many small blocks, and most ones for a short time,
127
* we don't go through the system malloc:
128
* for each fixed sizes a seperate list of free blocks is kept.
129
* to KISS (Keep it Small and Simple), we don't free them
130
* (not freeing a block of 32 bytes won't be worse than having fragmented
131
* a larger area on allocation).
132
* This way, we (almost) allways have a fitting free block right at hand,
133
* and don't have to walk any lists.
137
* structure layout of a allocated block
138
* unsigned long size:
139
* rounded up netto size for small and medium blocks
140
* brutto size == mmap'ed area for large blocks
141
* unsigned long DEBUG ? MAGIC : unused
143
* ( unsigned long MAGIC2 ) only if SIZE_TAIL defined
147
/* use otherwise unused long in the header to store a magic */
148
/* shouldn't this be removed for production release ? */
152
/* Xfree fills the memory with a certain pattern (currently 0xF0) */
153
/* this should really be removed for production release! */
157
/* this must be a multiple of SIZE_STEPS below */
158
#define MAX_SMALL 264 /* quite many blocks of 264 */
160
#define MIN_LARGE (11*1024)
161
/* worst case is 25% loss with a page size of 4k */
163
/* SIZE_STEPS defines the granularity of size of small blocks -
164
* this makes blocks align to that, too! */
165
#define SIZE_STEPS (sizeof(double))
166
#define SIZE_HEADER (2*sizeof(long)) /* = sizeof(double) for 32bit */
168
#if defined(__sparc__)
169
#define SIZE_TAIL (2*sizeof(long)) /* = sizeof(double) for 32bit */
171
#define SIZE_TAIL (sizeof(long))
177
#define TAIL_SIZE SIZE_TAIL
182
#if defined(__alpha__) || defined(__alpha) || \
183
defined(__ia64__) || defined(ia64) || \
184
defined(__sparc64__) || \
185
defined(__s390x__) || \
186
defined(__x86_64__) || defined(x86_64)
187
#define MAGIC 0x1404196414071968
188
#define MAGIC_FREE 0x1506196615061966
189
#define MAGIC2 0x2515207525182079
191
#define MAGIC 0x14071968
192
#define MAGIC_FREE 0x15061966
193
#define MAGIC2 0x25182079
196
/* To get some statistics about memory allocation */
199
#define XALLOC_LOG_FILE "/tmp/Xalloc.log" /* unsecure... */
200
#define LOG_BODY(_body) \
202
f = fopen(XALLOC_LOG_FILE, "a"); \
208
#if defined(linux) && defined(i386)
209
#define LOG_ALLOC(_fun, _size, _ret) \
210
{ unsigned long *from; \
211
__asm__("movl %%ebp,%0" : /*OUT*/ "=r" (from) : /*IN*/ ); \
212
LOG_BODY(fprintf(f, "%s\t%i\t%p\t[%lu]\n", _fun, _size, _ret, *(from+1))) \
215
#define LOG_ALLOC(_fun, _size, _ret) \
216
LOG_BODY(fprintf(f, "%s\t%i\t%p\n", _fun, _size, _ret))
218
#define LOG_REALLOC(_fun, _ptr, _size, _ret) \
219
LOG_BODY(fprintf(f, "%s\t%p\t%i\t%p\n", _fun, _ptr, _size, _ret))
220
#define LOG_FREE(_fun, _ptr) \
221
LOG_BODY(fprintf(f, "%s\t%p\n", _fun, _ptr))
223
#define LOG_ALLOC(_fun, _size, _ret)
224
#define LOG_REALLOC(_fun, _ptr, _size, _ret)
225
#define LOG_FREE(_fun, _ptr)
226
#endif /* XALLOC_LOG */
228
static unsigned long *free_lists[MAX_SMALL/SIZE_STEPS];
231
* systems that support it should define HAS_MMAP_ANON or MMAP_DEV_ZERO
232
* and include the appropriate header files for
233
* mmap(), munmap(), PROT_READ, PROT_WRITE, MAP_PRIVATE,
234
* PAGE_SIZE or _SC_PAGESIZE (and MAP_ANON for HAS_MMAP_ANON).
236
* systems that don't support MAP_ANON fall through to the 2 fold behaviour
240
#define HAS_MMAP_ANON
241
#include <sys/types.h>
242
#include <sys/mman.h>
243
#include <asm/page.h> /* PAGE_SIZE */
244
#define HAS_SC_PAGESIZE /* _SC_PAGESIZE may be an enum for Linux */
245
#define HAS_GETPAGESIZE
249
#define HAS_MMAP_ANON
250
#include <sys/types.h>
251
#include <sys/mman.h>
252
#include <mach/vm_param.h> /* PAGE_SIZE */
253
#define HAS_SC_PAGESIZE
254
#define HAS_GETPAGESIZE
257
#if defined(CSRG_BASED)
258
#define HAS_MMAP_ANON
259
#define HAS_GETPAGESIZE
260
#include <sys/types.h>
261
#include <sys/mman.h>
262
#endif /* CSRG_BASED */
265
#define HAS_GETPAGESIZE
266
#define MMAP_DEV_ZERO
267
#include <sys/types.h>
268
#include <sys/mman.h>
272
#if defined(SVR4) && !defined(DGUX)
273
#define MMAP_DEV_ZERO
274
#include <sys/types.h>
275
#include <sys/mman.h>
277
#endif /* SVR4 && !DGUX */
279
#if defined(sun) && !defined(SVR4) /* SunOS */
280
#define MMAP_DEV_ZERO /* doesn't SunOS have MAP_ANON ?? */
281
#define HAS_GETPAGESIZE
282
#include <sys/types.h>
283
#include <sys/mman.h>
284
#endif /* sun && !SVR4 */
290
#if defined(HAS_MMAP_ANON) || defined (MMAP_DEV_ZERO)
295
static int devzerofd = -1;
300
* empty trap function for gdb. Breakpoint here
301
* to find who tries to free a free area
308
Xalloc (unsigned long amount)
310
register unsigned long *ptr;
315
/* zero size requested */
317
LOG_ALLOC("Xalloc=0", amount, 0);
320
/* negative size (or size > 2GB) - what do we do? */
321
if ((long)amount < 0) {
324
FatalError("Xalloc: Xalloc(<0)\n");
326
ErrorF("Xalloc warning: Xalloc(<0) ignored..\n");
328
LOG_ALLOC("Xalloc<0", amount, 0);
332
/* alignment check */
333
#if defined(__alpha__) || defined(__alpha) || \
334
defined(__sparc__) || \
335
defined(__mips__) || \
336
defined(__powerpc__) || \
337
defined(__arm32__) || \
338
defined(__ia64__) || defined(ia64) || \
339
defined(__s390x__) || defined(__s390__)
340
amount = (amount + (sizeof(long)-1)) & ~(sizeof(long)-1);
343
if (amount <= MAX_SMALL) {
347
/* pick a ready to use small chunk */
348
indx = (amount-1) / SIZE_STEPS;
349
ptr = free_lists[indx];
351
/* list empty - get 20 or 40 more */
352
/* amount = size rounded up */
353
amount = (indx+1) * SIZE_STEPS;
354
ptr = (unsigned long *)calloc(1,(amount+SIZE_HEADER+TAIL_SIZE)
355
* (amount<100 ? 40 : 20));
358
unsigned long *p1, *p2;
360
p2 = (unsigned long *)((char *)ptr + SIZE_HEADER);
361
for (i=0; i<(amount<100 ? 40 : 20); i++) {
366
#endif /* XALLOC_DEBUG */
368
*(unsigned long *)((unsigned char *)p1 + amount) = MAGIC2;
369
#endif /* SIZE_TAIL */
370
p2 = (unsigned long *)((char *)p1 + SIZE_HEADER + amount + TAIL_SIZE);
371
*(unsigned long **)p1 = p2;
373
/* last one has no next one */
374
*(unsigned long **)p1 = NULL;
375
/* put the second in the list */
376
free_lists[indx] = (unsigned long *)((char *)ptr + SIZE_HEADER + amount + TAIL_SIZE + SIZE_HEADER);
377
/* take the fist one */
378
ptr = (unsigned long *)((char *)ptr + SIZE_HEADER);
379
LOG_ALLOC("Xalloc-S", amount, ptr);
382
} /* else fall through to 'Out of memory' */
384
/* take that piece of mem out of the list */
385
free_lists[indx] = *((unsigned long **)ptr);
386
/* already has size (and evtl. magic) filled in */
389
#endif /* XALLOC_DEBUG */
390
LOG_ALLOC("Xalloc-S", amount, ptr);
394
#if defined(HAS_MMAP_ANON) || defined(MMAP_DEV_ZERO)
395
} else if (amount >= MIN_LARGE) {
400
/* round up amount */
401
amount += SIZE_HEADER + TAIL_SIZE;
402
/* round up brutto amount to a multiple of the page size */
403
amount = (amount + pagesize-1) & ~(pagesize-1);
405
ptr = (unsigned long *)mmap((caddr_t)0,
407
PROT_READ | PROT_WRITE,
412
ptr = (unsigned long *)mmap((caddr_t)0,
414
PROT_READ | PROT_WRITE,
415
MAP_ANON | MAP_PRIVATE,
420
ptr[0] = amount - SIZE_HEADER - TAIL_SIZE;
423
#endif /* XALLOC_DEBUG */
425
((unsigned long *)((char *)ptr + amount - TAIL_SIZE))[0] = MAGIC2;
426
#endif /* SIZE_TAIL */
427
ptr = (unsigned long *)((char *)ptr + SIZE_HEADER);
428
LOG_ALLOC("Xalloc-L", amount, ptr);
430
} /* else fall through to 'Out of memory' */
431
#endif /* HAS_MMAP_ANON || MMAP_DEV_ZERO */
436
/* 'normal' malloc() */
437
ptr=(unsigned long *)calloc(1,amount+SIZE_HEADER+TAIL_SIZE);
438
if (ptr != (unsigned long *)NULL) {
442
#endif /* XALLOC_DEBUG */
444
*(unsigned long *)((char *)ptr + amount + SIZE_HEADER) = MAGIC2;
445
#endif /* SIZE_TAIL */
446
ptr = (unsigned long *)((char *)ptr + SIZE_HEADER);
447
LOG_ALLOC("Xalloc-M", amount, ptr);
451
if (Must_have_memory)
452
FatalError("Out of memory");
453
LOG_ALLOC("Xalloc-oom", amount, 0);
459
* "no failure" realloc, alternate interface to Xalloc w/o Must_have_memory
463
XNFalloc (unsigned long amount)
465
register pointer ptr;
467
/* zero size requested */
469
LOG_ALLOC("XNFalloc=0", amount, 0);
472
/* negative size (or size > 2GB) - what do we do? */
473
if ((long)amount < 0) {
476
FatalError("Xalloc: XNFalloc(<0)\n");
478
ErrorF("Xalloc warning: XNFalloc(<0) ignored..\n");
480
LOG_ALLOC("XNFalloc<0", amount, 0);
481
return (unsigned long *)NULL;
483
ptr = Xalloc(amount);
486
FatalError("Out of memory");
496
Xcalloc (unsigned long amount)
500
ret = Xalloc (amount);
502
#if defined(HAS_MMAP_ANON) || defined(MMAP_DEV_ZERO)
503
&& (amount < MIN_LARGE) /* mmaped anonymous mem is already cleared */
506
bzero ((char *) ret, (int) amount);
514
XNFcalloc (unsigned long amount)
518
ret = XNFalloc (amount);
520
#if defined(HAS_MMAP_ANON) || defined(MMAP_DEV_ZERO)
521
&& (amount < MIN_LARGE) /* mmaped anonymous mem is already cleared */
524
bzero ((char *) ret, (int) amount);
533
Xrealloc (pointer ptr, unsigned long amount)
535
register unsigned long *new_ptr;
537
/* zero size requested */
541
LOG_REALLOC("Xrealloc=0", ptr, amount, 0);
544
/* negative size (or size > 2GB) - what do we do? */
545
if ((long)amount < 0) {
548
FatalError("Xalloc: Xrealloc(<0)\n");
550
ErrorF("Xalloc warning: Xrealloc(<0) ignored..\n");
554
LOG_REALLOC("Xrealloc<0", ptr, amount, 0);
558
new_ptr = Xalloc(amount);
559
if ( (new_ptr) && (ptr) ) {
560
unsigned long old_size;
561
old_size = ((unsigned long *)ptr)[-2];
563
if (MAGIC != ((unsigned long *)ptr)[-1]) {
564
if (MAGIC_FREE == ((unsigned long *)ptr)[-1]) {
567
FatalError("Xalloc error: range already freed in Xrealloc() :-(\n");
569
ErrorF("Xalloc error: range already freed in Xrealloc() :-(\a\n");
573
LOG_REALLOC("Xalloc error: ranged already freed in Xrealloc() :-(",
579
FatalError("Xalloc error: header corrupt in Xrealloc() :-(\n");
581
ErrorF("Xalloc error: header corrupt in Xrealloc() :-(\n");
584
LOG_REALLOC("Xalloc error: header corrupt in Xrealloc() :-(",
588
#endif /* XALLOC_DEBUG */
589
/* copy min(old size, new size) */
590
memcpy((char *)new_ptr, (char *)ptr, (amount < old_size ? amount : old_size));
595
LOG_REALLOC("Xrealloc", ptr, amount, new_ptr);
596
return (void *)new_ptr;
598
if (Must_have_memory)
599
FatalError("Out of memory");
600
LOG_REALLOC("Xrealloc", ptr, amount, 0);
606
* "no failure" realloc, alternate interface to Xrealloc w/o Must_have_memory
610
XNFrealloc (pointer ptr, unsigned long amount)
612
if (( ptr = (pointer)Xrealloc( ptr, amount ) ) == NULL)
614
FatalError( "Out of memory" );
628
unsigned long *pheader;
630
/* free(NULL) IS valid :-( - and widely used throughout the server.. */
634
pheader = (unsigned long *)((char *)ptr - SIZE_HEADER);
636
if (MAGIC != pheader[1]) {
638
if (MAGIC_FREE == pheader[1]) {
641
FatalError("Xalloc error: range already freed in Xrealloc() :-(\n");
643
ErrorF("Xalloc error: range already freed in Xrealloc() :-(\a\n");
647
LOG_FREE("Xalloc error: ranged already freed in Xrealloc() :-(", ptr);
652
FatalError("Xalloc error: Header corrupt in Xfree() :-(\n");
654
ErrorF("Xalloc error: Header corrupt in Xfree() :-(\n");
657
LOG_FREE("Xalloc error: Header corrupt in Xfree() :-(", ptr);
660
#endif /* XALLOC_DEBUG */
663
if (size <= MAX_SMALL) {
669
if (MAGIC2 != *(unsigned long *)((char *)ptr + size)) {
673
FatalError("Xalloc error: Tail corrupt in Xfree() for small block (adr=0x%x, val=0x%x)\n",(char *)ptr + size,*(unsigned long *)((char *)ptr + size));
675
ErrorF("Xalloc error: Tail corrupt in Xfree() for small block (adr=0x%x, val=0x%x)\n",(char *)ptr + size,*(unsigned long *)((char *)ptr + size));
678
LOG_FREE("Xalloc error: Tail corrupt in Xfree() for small block", ptr);
681
#endif /* SIZE_TAIL */
684
memset(ptr,0xF0,size);
685
#endif /* XFREE_ERASES */
687
pheader[1] = MAGIC_FREE;
689
/* put this small block at the head of the list */
690
indx = (size-1) / SIZE_STEPS;
691
*(unsigned long **)(ptr) = free_lists[indx];
692
free_lists[indx] = (unsigned long *)ptr;
693
LOG_FREE("Xfree", ptr);
696
#if defined(HAS_MMAP_ANON) || defined(MMAP_DEV_ZERO)
697
} else if (size >= MIN_LARGE) {
702
if (MAGIC2 != ((unsigned long *)((char *)ptr + size))[0]) {
706
FatalError("Xalloc error: Tail corrupt in Xfree() for big block (adr=0x%x, val=0x%x)\n",(char *)ptr+size,((unsigned long *)((char *)ptr + size))[0]);
708
ErrorF("Xalloc error: Tail corrupt in Xfree() for big block (adr=0x%x, val=0x%x)\n",(char *)ptr+size,((unsigned long *)((char *)ptr + size))[0]);
711
LOG_FREE("Xalloc error: Tail corrupt in Xfree() for big block", ptr);
715
#endif /* SIZE_TAIL */
717
LOG_FREE("Xfree", ptr);
719
munmap((caddr_t)pheader, (size_t)size);
720
/* no need to clear - mem is inaccessible after munmap.. */
721
#endif /* HAS_MMAP_ANON */
728
if (MAGIC2 != *(unsigned long *)((char *)ptr + size)) {
732
FatalError("Xalloc error: Tail corrupt in Xfree() for medium block (adr=0x%x, val=0x%x)\n",(char *)ptr + size,*(unsigned long *)((char *)ptr + size));
734
ErrorF("Xalloc error: Tail corrupt in Xfree() for medium block (adr=0x%x, val=0x%x)\n",(char *)ptr + size,*(unsigned long *)((char *)ptr + size));
737
LOG_FREE("Xalloc error: Tail corrupt in Xfree() for medium block", ptr);
740
#endif /* SIZE_TAIL */
743
memset(pheader,0xF0,size+SIZE_HEADER);
744
#endif /* XFREE_ERASES */
746
pheader[1] = MAGIC_FREE;
749
LOG_FREE("Xfree", ptr);
750
free((char *)pheader);
755
OsInitAllocator (void)
757
static Bool beenhere = FALSE;
763
#if defined(HAS_MMAP_ANON) || defined (MMAP_DEV_ZERO)
765
#if defined(_SC_PAGESIZE) || defined(HAS_SC_PAGESIZE)
766
pagesize = sysconf(_SC_PAGESIZE);
770
pagesize = sysconf(_SC_PAGE_SIZE);
772
#ifdef HAS_GETPAGESIZE
774
pagesize = getpagesize();
778
pagesize = PAGE_SIZE;
781
FatalError("OsInitAllocator: Cannot determine page size\n");
784
/* set up linked lists of free blocks */
785
bzero ((char *) free_lists, MAX_SMALL/SIZE_STEPS*sizeof(unsigned long *));
788
/* open /dev/zero on systems that have mmap, but not MAP_ANON */
790
if ((devzerofd = open("/dev/zero", O_RDWR, 0)) < 0)
791
FatalError("OsInitAllocator: Cannot open /dev/zero (errno=%d)\n",
797
/* reset the log file to zero length */
800
f = fopen(XALLOC_LOG_FILE, "w");
807
#else /* !INTERNAL_MALLOC */
808
/* This is to avoid an empty .o */
809
static int no_internal_xalloc;
810
#endif /* INTERNAL_MALLOC */