1 /* Copyright (c) 1995, 2002, 2009 Xilinx, Inc. All rights reserved.
3 Redistribution and use in source and binary forms, with or without
4 modification, are permitted provided that the following conditions are
7 1. Redistributions source code must retain the above copyright notice,
8 this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
14 3. Neither the name of Xilinx nor the names of its contributors may be
15 used to endorse or promote products derived from this software without
16 specific prior written permission.
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
19 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
21 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
24 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 typedef unsigned int size_t;
43 /* The only extern functions I need if not printing. */
44 extern void* sbrk(size_t incr
);
45 extern void *memcpy(void *s1
, const void *s2
, size_t n
);
46 extern void *memset(void *s
, int c
, size_t n
);
49 typedef unsigned char BOOLEAN
;
50 const BOOLEAN FALSE
=0;
51 const BOOLEAN TRUE
=1;
53 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
54 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
56 #define M_DBG_NORMAL 0
57 #define M_DBG_PARTIAL 1
60 /* debugging breakpoint aids */
61 static char xil_mem_null_free
[] = "xil_mem_null_free";
62 static char xil_mem_chkcnt
[] = "xil_mem_chkcnt";
64 /* Flag values describing the state of a memory block.
65 /* Indicator for allocated blk */
66 #define M_ALLOCEDFLAG 0x5a
67 /* End-of-block if debug level */
68 #define M_ALLOCED 0xc99cc99c
69 /* Free block indicator. */
70 #define M_FREEFLAG 0xa5
71 /* End-of-block if debug level */
72 #define M_FREE 0x9cc99cc9
73 /* Zero length block. */
74 #define M_ZEROFLAG 0xaa
76 /* Header of a memory block. */
77 typedef unsigned char DATA_T
;
78 typedef DATA_T
* DATA_P
;
81 unsigned dbglev
:2; /* Debug level this was created with. */
82 unsigned size
:22; /* Size of block / 8. 32 Meg max. */
83 unsigned flag
:8; /* Indicates whether allocated or freed. */
85 typedef struct M_HEADER
* M_HEADERP
;
87 BOOLEAN
isalloced(M_HEADERP
this)
89 return this->flag
== M_ALLOCEDFLAG
;
91 BOOLEAN
isfree(M_HEADERP
this)
93 return this->flag
== M_FREEFLAG
;
95 BOOLEAN
iszero(M_HEADERP
this)
97 return this->flag
== M_ZEROFLAG
;
100 void setalloced(M_HEADERP
this) { this->flag
= M_ALLOCEDFLAG
; }
101 void setfree(M_HEADERP
this) { this->flag
= M_FREEFLAG
; }
102 void setzero(M_HEADERP
this) { this->flag
= M_ZEROFLAG
; }
104 int getdbglev(M_HEADERP
this) { return this->dbglev
; }
105 void setdbglev(M_HEADERP
this, int d
) { this->dbglev
= d
; }
107 size_t getsize(M_HEADERP
this) { return this->size
<< 3; } /* Alignment is 8. */
108 void setsize(M_HEADERP
this, size_t s
){ this->size
= s
>> 3; }
110 DATA_T
* getend(M_HEADERP
this) { return (((DATA_T
*)this)+getsize(this)); }
112 /* Next pointer is after data in block. */
113 M_HEADERP
getnext(M_HEADERP
this) { return *(((M_HEADERP
*)getend(this)) - 1); }
114 void setnext(M_HEADERP
this, M_HEADERP n
) { *(((M_HEADERP
*)getend(this)) - 1) = n
; }
116 /* Routines used to set a flag at end of block if debuglevel != normal. */
117 /* Sentinel is right BEFORE the next pointer. */
118 unsigned long* getsentinel(M_HEADERP
this);
119 void setsentinel(M_HEADERP
this, unsigned long lflag
);
120 BOOLEAN
testsentinel(M_HEADERP
this, unsigned long lflag
);
122 /* Routines to handle data. Depend on debug level. */
123 DATA_T
* getdata(M_HEADERP
this) { return (((DATA_T
*)this)+sizeof(*this)); }
124 size_t getdatasize(M_HEADERP
this);
126 /* Fill data with a pattern. */
127 void setdata(M_HEADERP
this, int f
);
130 BOOLEAN
checkalloc(M_HEADERP
this); /* Is this a valid allocated memory pointer? */
131 BOOLEAN
checkfree(M_HEADERP
this); /* Is this a valid freelist entry? */
135 /* Get length of data. */
137 getdatasize(M_HEADERP
this)
139 /* By default, size is size of block - size of header. */
140 int tmp_size
= getsize(this) - sizeof(struct M_HEADER
);
142 if (this->dbglev
!= M_DBG_NORMAL
)
144 /* Subtract size of sentinel, and next pointer. */
145 tmp_size
-= sizeof(long) + sizeof(M_HEADERP
);
146 /* If only eight bytes, no room for sentinel. */
152 /* Free block always has a next pointer. Otherwise not. */
154 tmp_size
-= sizeof(M_HEADERP
);
159 /* Set the data buffer to value f. */
161 setdata(M_HEADERP
this, int f
)
163 memset(getdata(this), f
, getdatasize(this));
166 /* At the end of the block, there may be a longword with
167 special meaning. This is the sentinel. If there is a sentinel,
168 there is by definition a next pointer. */
170 getsentinel(M_HEADERP
this)
172 DATA_T
* addr
= (getend(this) - sizeof(M_HEADERP
)); /* location of next pointer. */
173 if (getdata(this) < addr
)
174 return ((unsigned long*)addr
) - 1; /* Right before next pointer. */
176 return NULL
; /* Block too small. No room for sent. */
180 setsentinel(M_HEADERP
this, unsigned long lflag
)
182 unsigned long* addr
= getsentinel(this);
188 testsentinel(M_HEADERP
this, unsigned long lflag
)
190 unsigned long* addr
= getsentinel(this);
192 return *addr
== lflag
;
197 /* sizeof(struct M_HEADER)+sizeof(M_HEADERP); Alignment */
198 #define M_BLOCKSIZE 8
199 /* 4096 / 8; // M_BLOCKSIZE ; Number of freelist entries. */
200 #define M_FREESIZE 512
201 /* 64 * 1024; Size of incremental memory hunks allocated, */
202 #define M_BRKINC 2048
204 static M_HEADERP freelist
[M_FREESIZE
]; /* Free list. */
206 static M_HEADERP alloclist
= NULL
; /* Pointer to linked list
207 of Allocated blocks. */
208 static int mdebuglevel
= M_DBG_NORMAL
;
210 static DATA_T zerobuf
[M_BLOCKSIZE
] = { M_ZEROFLAG
, M_ZEROFLAG
, M_ZEROFLAG
,
211 M_ZEROFLAG
, M_ZEROFLAG
, M_ZEROFLAG
,
212 M_ZEROFLAG
, M_ZEROFLAG
};
213 static M_HEADERP zeroblock
= (M_HEADERP
)zerobuf
;
215 static unsigned long totalallocated
= 0; /* NOT actually malloced, but
216 rather the size of the pool. */
218 static unsigned long totalmalloc
= 0; /* Total amount malloced. */
220 static unsigned long highwater
= 0; /* Largest amount of memory
221 allocated at any time. */
222 static long nummallocs
= 0;
223 static long numfrees
= 0;
224 static long numreallocs
= 0;
230 static int m_curcount
= 0;
233 getmemblock(size_t n
)
235 M_HEADERP block
= (M_HEADERP
) sbrk(n
);
246 mdebuglevel
= M_DBG_NORMAL
;
248 printf ("%s\n", msg
);
251 /* Go into infinite loop. */
259 getfreeindex(size_t size
)
261 return MIN(size
/ M_BLOCKSIZE
, M_FREESIZE
- 1);
265 void coalesce(M_HEADERP h
)
267 /* Coalesce block h with free block any free blocks after it.
268 Assumes that H is currently allocated. Sentinel at end is
269 set to allocated so if H is free, caller has to fix it. */
274 M_HEADERP next
= (M_HEADERP
)getend(h
);
276 if (next
|| isalloced(next
))
277 break; /* no more coalscing can be done. */
279 /* Take it off the free list. */
280 i
= getfreeindex(getsize(next
));
283 freelist
[i
] = getnext(next
);
286 while (f
!= NULL
&& getnext(f
) != next
)
289 /* Didn't find it in the free list. */
291 die ("Coalesce failed.");
293 setnext(f
, getnext(next
));
296 /* Add two blocks together and start over. */
297 setsize(h
, getsize(h
) + getsize(next
));
299 if (getdbglev(h
) > M_DBG_NORMAL
)
301 setsentinel(h
, M_ALLOCED
);
307 checkalloc(M_HEADERP
this)
309 if (!isalloced(this))
310 return die ("checkalloc: pointer header clobbered.");
312 if (getdbglev(this) > M_DBG_NORMAL
)
314 if (!testsentinel(this, M_ALLOCED
))
315 return die ("checkalloc: pointer length overrun.");
321 checkfree(M_HEADERP
this)
326 die ("checkfree: pointer header clobbered.");
328 if (getdbglev(this) > M_DBG_NORMAL
)
330 if (!testsentinel(this, M_FREE
))
331 die ("checkfree: pointer length overrun.");
334 i
= getdatasize(this);
336 if (*d
++ != M_FREEFLAG
)
337 die("checkfree: freed data clobbered.");
347 for (i
= 0; i
< M_FREESIZE
; i
+= 1)
349 M_HEADERP h
= (M_HEADERP
) freelist
[i
];
353 if (i
!= (M_FREESIZE
- 1) && getsize(h
) != (i
* M_BLOCKSIZE
))
354 die ("checkfreelist: free list size mismatch.");
363 M_HEADERP a
= (M_HEADERP
) alloclist
;
371 /* Free a block of memory. This is done by adding to the free list. */
373 addtofreelist (M_HEADERP h
)
376 /* Merge freed blocks together. */
379 /* link this block to the front of the appropriate free list. */
380 i
= getfreeindex(getsize(h
));
381 setnext(h
, freelist
[i
]);
384 /* Set the flag info. */
386 setdbglev(h
, mdebuglevel
);
387 if (mdebuglevel
> M_DBG_NORMAL
)
389 /* Fill with some meaningful (and testable) data. */
390 setdata(h
, M_FREEFLAG
);
391 setsentinel(h
, M_FREE
);
399 for ( i
= 0; i
< M_BLOCKSIZE
; i
+= 1)
401 if (zerobuf
[i
] != M_ZEROFLAG
)
402 die ("malloc_verify: Zero block clobbered.");
409 xil_malloc_debug (int level
)
411 mdebuglevel
= MAX (M_DBG_NORMAL
, MIN (M_DBG_FULL
, level
));
415 xil_malloc (size_t nbytes
)
427 return getdata(zeroblock
);
429 if (mdebuglevel
== M_DBG_FULL
)
432 static unsigned do_cnt
= ~0;
433 static unsigned done_cnt
= 0;
436 char *x
= (char *)getenv(xil_mem_chkcnt
);
441 if (do_cnt
== 1 || done_cnt
% do_cnt
== 0)
449 nbytes
+= sizeof (struct M_HEADER
);
451 /* If debug, leave room for flag and next pointer. */
452 if (mdebuglevel
> M_DBG_NORMAL
)
453 nbytes
+= sizeof (long) + sizeof (M_HEADERP
*);
455 /* Round up to allocation unit */
456 msize
= ((nbytes
+ M_BLOCKSIZE
- 1) / M_BLOCKSIZE
) * M_BLOCKSIZE
;
458 /* Look around for a block of approximately the right size. */
460 minf
= getfreeindex(msize
);
461 maxf
= MIN(minf
* 2, M_FREESIZE
);
463 for (i
= minf
; i
< M_FREESIZE
; i
+= 1)
466 i
= M_FREESIZE
- 1; /* Skip over blocks too large. */
469 p
= NULL
; /* Previous. */
472 if (getsize(h
) >= nbytes
)
474 /* Take h out of linked list */
476 setnext(p
, getnext(h
));
478 freelist
[i
] = getnext(h
);
481 die ("malloc: freelist clobbered.\n");
493 /* Didn't find any free pointers. Allocate more heap.
494 Round up to next heap increment. */
495 i
= ((msize
+ sizeof(long) + M_BRKINC
- 1) / M_BRKINC
) * M_BRKINC
;
496 if ((h
= getmemblock (i
)) == NULL
)
499 printf ("xil_malloc: Out of dynamic memory.\n");
504 /* Mark end of block with zero for four bytes so we don't merge next block
505 into free list accidentally. */
506 setsize(h
, i
- sizeof(long));
507 *((long*)getend(h
)) = 0;
510 /* Merge allocated blocks so we can free a bigger part of what is left! */
512 if (getsize(h
) >= msize
+ M_BLOCKSIZE
)
516 /* add the remainder of this block to the free list. */
517 rsize
= getsize(h
) - msize
;
518 r
= (M_HEADERP
) (((DATA_T
*)h
) + msize
);
525 setdbglev(h
, mdebuglevel
);
526 if (mdebuglevel
> M_DBG_NORMAL
)
528 // Chain into alloc'd list and set sentinel. */
529 setsentinel(h
, M_ALLOCED
);
530 setnext(h
, alloclist
);
535 if (!m_reenter
&& m_prtflag
)
538 printf("%d malloc\n",h
+1);
542 if ((DATA_T
*)m_stopaddr
== getdata(h
))
544 if (m_stopcnt
== ++m_curcount
)
552 totalmalloc
+= getsize(h
);
553 if (totalmalloc
> highwater
)
554 highwater
= totalmalloc
;
568 if (mdebuglevel
!= M_DBG_NORMAL
&& getenv(xil_mem_null_free
))
569 die ("free: tried to free NULL pointer.");
571 return; /* Let `em do it. */
577 /* Drop through to here if not a smartheap allocation. This
578 handles free of both xil_malloc and libc malloc. */
580 h
= (M_HEADERP
) (((DATA_T
*)ap
) - sizeof (struct M_HEADER
));
586 if (!m_reenter
&& m_prtflag
) {
588 printf("%d mfree\n",h
+1);
596 die ("free: tried to free pointer twice.");
598 die ("free: tried to free a block not allocated by malloc.");
602 if (getdbglev(h
) > M_DBG_NORMAL
)
604 /* Make sure things look reasonable. */
607 /* Try to find the pointer in the alloc list. */
609 alloclist
= getnext(h
);
612 M_HEADERP a
= alloclist
;
613 while (a
!= NULL
&& getnext(a
) != h
)
616 /* If a is NULL, debuglevel must have been reset at some point. */
618 setnext(a
, getnext(h
));
622 totalmalloc
-= getsize(h
);
626 if (mdebuglevel
== M_DBG_FULL
)
629 static unsigned do_cnt
= ~0;
630 static unsigned done_cnt
= 0;
633 char *x
= (char *)getenv(xil_mem_chkcnt
);
638 if (do_cnt
== 1 || done_cnt
% do_cnt
== 0)
650 M_HEADERP h
= (M_HEADERP
) (((DATA_T
*)ap
) - sizeof (struct M_HEADER
));
651 return getdatasize(h
);
655 xil_realloc (void* oldblk
, size_t newsize
)
665 if (mdebuglevel
!= M_DBG_NORMAL
)
666 die ("realloc: tried to realloc NULL pointer.");
668 return xil_malloc(newsize
); /* Don't need to copy anything. */
671 /* Make sure this is a valid block. */
672 h
= (M_HEADERP
) (((char*)oldblk
) - sizeof (struct M_HEADER
));
674 /* if old block was zero bytes, just alloc a new one. */
676 return xil_malloc(newsize
); /* Source is empty anyway. */
678 /* If old block was already freed, error. */
680 die ("realloc: tried to realloc freed pointer.");
684 long* pdesc
= *(long**)h
; /* Get pointer to the block descriptor. */
685 long* pnextdesc
= (long*)*pdesc
;
686 if ((pdesc
[1] & ~3) != (long)h
) /* Should point back to block. */
687 die ("realloc: header clobbered.");
689 /* This must be a libc block. We need to figure out how big it is.
690 Length of block is delta between two descriptors - sizeof (void*). */
692 oldsize
= (size_t) ((pnextdesc
[1] & ~3) - (pdesc
[1] & ~3)-sizeof(void*));
694 /* Don't bother to change anything unless there's not enough room. */
695 if (oldsize
< newsize
)
697 /* Alloc a new block with our malloc. */
698 if ((newblk
= xil_malloc(newsize
)) == NULL
)
701 /* Copy the old data to it. */
702 memcpy (newblk
, oldblk
, (newsize
< oldsize
) ? newsize
: oldsize
);
708 /* If the new size is bigger than my allocated
709 size, or if more than 1/4 of the block would be left free, allocate
710 a new block and copy the data. Otherwise, leave well enough alone. */
714 oldsize
= getdatasize(h
);
716 if (oldsize
< newsize
717 || (newsize
> (2*M_BLOCKSIZE
) && (newsize
*4) < (oldsize
*3)))
719 if (( newblk
= xil_malloc( newsize
)) == NULL
)
722 memcpy (newblk
, oldblk
, (newsize
< oldsize
) ? newsize
: oldsize
);
732 xil_calloc (size_t number
, size_t size
)
736 size_t temp
= number
* size
+ sizeof (long) - 1;
737 temp
-= temp
% sizeof (long);
739 blockptr
= xil_malloc( temp
);
742 longptr
= (long*) blockptr
;
743 temp
/= sizeof (long);
752 #define M_STAT_NORMAL 0
753 #define M_STAT_VERBOSE 1
754 #define M_STAT_REALLYVERBOSE 2
758 xil_mstats(int verbosity
)
760 unsigned long totalfree
= 0;
762 printf("Memory Statics:\n"
763 "---------------\n");
764 printf(" Number of calls to malloc: %ld.\n", nummallocs
);
765 printf(" Number of calls to free: %ld.\n", numfrees
);
766 printf(" Number of calls to realloc: %ld.\n", numreallocs
);
767 printf(" Total allocated memory: %lu (0x%lx)\n",
768 totalallocated
, totalallocated
);
769 printf(" Currently malloced memory: %lu (0x%lx)\n",
770 totalmalloc
, totalmalloc
);
774 for (i
= 0; i
< M_FREESIZE
; i
+= 1)
776 M_HEADERP h
= freelist
[i
];
777 unsigned long numblocks
= 0;
780 totalfree
+= getsize(h
);
784 if (verbosity
> M_STAT_NORMAL
&& numblocks
> 0)
786 printf(" There are %d blocks on freelist for size %d\n",
787 numblocks
, i
* M_BLOCKSIZE
);
791 printf(" Currently free memory: %lu (0x%lx)\n",
792 totalfree
, totalfree
);
793 printf(" High water mark: %lu (0x%lx)\n",
794 highwater
, highwater
);
801 xil_mstats(int verbosity
)