forked from Imagelibrary/rtems
367 lines
7.8 KiB
C
367 lines
7.8 KiB
C
/*
|
|
* RTEMS Malloc Family Implementation
|
|
*
|
|
*
|
|
* COPYRIGHT (c) 1989, 1990, 1991, 1992, 1993, 1994.
|
|
* On-Line Applications Research Corporation (OAR).
|
|
* All rights assigned to U.S. Government, 1994.
|
|
*
|
|
* This material may be reproduced by or for the U.S. Government pursuant
|
|
* to the copyright license under the clause at DFARS 252.227-7013. This
|
|
* notice must appear in all copies of this file and its derivatives.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
#include <rtems.h>
|
|
#include "libcsupport.h"
|
|
#ifdef RTEMS_NEWLIB
|
|
#include <sys/reent.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <unistd.h> /* sbrk(2) */
|
|
|
|
rtems_id RTEMS_Malloc_Heap;
|
|
size_t RTEMS_Malloc_Sbrk_amount;
|
|
|
|
#ifdef RTEMS_DEBUG
|
|
#define MALLOC_STATS
|
|
#define MALLOC_DIRTY
|
|
#endif
|
|
|
|
#ifdef MALLOC_STATS
|
|
#define MSBUMP(f,n) malloc_stats.f += (n)
|
|
|
|
struct {
|
|
unsigned32 space_available; /* current size of malloc area */
|
|
unsigned32 malloc_calls; /* # calls to malloc */
|
|
unsigned32 free_calls;
|
|
unsigned32 realloc_calls;
|
|
unsigned32 calloc_calls;
|
|
unsigned32 max_depth; /* most ever malloc'd at 1 time */
|
|
unsigned64 lifetime_allocated;
|
|
unsigned64 lifetime_freed;
|
|
} malloc_stats;
|
|
|
|
#else /* No malloc_stats */
|
|
#define MSBUMP(f,n)
|
|
#endif
|
|
|
|
void RTEMS_Malloc_Initialize(
|
|
void *start,
|
|
size_t length,
|
|
size_t sbrk_amount
|
|
)
|
|
{
|
|
rtems_status_code status;
|
|
void *starting_address;
|
|
rtems_unsigned32 old_address;
|
|
rtems_unsigned32 u32_address;
|
|
|
|
/*
|
|
* If the starting address is 0 then we are to attempt to
|
|
* get length worth of memory using sbrk. Make sure we
|
|
* align the address that we get back.
|
|
*/
|
|
|
|
starting_address = start;
|
|
RTEMS_Malloc_Sbrk_amount = sbrk_amount;
|
|
|
|
if (!starting_address) {
|
|
u32_address = (unsigned int)sbrk(length);
|
|
|
|
if (u32_address == -1) {
|
|
rtems_fatal_error_occurred( RTEMS_NO_MEMORY );
|
|
/* DOES NOT RETURN!!! */
|
|
}
|
|
|
|
if (u32_address & (CPU_ALIGNMENT-1)) {
|
|
old_address = u32_address;
|
|
u32_address = (u32_address + CPU_ALIGNMENT) & ~(CPU_ALIGNMENT-1);
|
|
|
|
/*
|
|
* adjust the length by whatever we aligned by
|
|
*/
|
|
|
|
length -= u32_address - old_address;
|
|
}
|
|
|
|
starting_address = (void *)u32_address;
|
|
}
|
|
|
|
/*
|
|
* Unfortunately we cannot use assert if this fails because if this
|
|
* has failed we do not have a heap and if we do not have a heap
|
|
* STDIO cannot work because there will be no buffers.
|
|
*/
|
|
|
|
status = rtems_region_create(
|
|
rtems_build_name( 'H', 'E', 'A', 'P' ),
|
|
starting_address,
|
|
length,
|
|
CPU_ALIGNMENT,
|
|
RTEMS_DEFAULT_ATTRIBUTES,
|
|
&RTEMS_Malloc_Heap
|
|
);
|
|
if ( status != RTEMS_SUCCESSFUL )
|
|
rtems_fatal_error_occurred( status );
|
|
|
|
#ifdef MALLOC_STATS
|
|
/* zero all the stats */
|
|
(void) memset(&malloc_stats, 0, sizeof(malloc_stats));
|
|
#endif
|
|
|
|
MSBUMP(space_available, length);
|
|
}
|
|
|
|
void *malloc(
|
|
size_t size
|
|
)
|
|
{
|
|
void *return_this;
|
|
void *starting_address;
|
|
rtems_unsigned32 the_size;
|
|
rtems_unsigned32 sbrk_amount;
|
|
rtems_status_code status;
|
|
|
|
MSBUMP(malloc_calls, 1);
|
|
|
|
if ( !size )
|
|
return (void *) 0;
|
|
|
|
/*
|
|
* Try to give a segment in the current region if there is not
|
|
* enough space then try to grow the region using rtems_region_extend().
|
|
* If this fails then return a NULL pointer.
|
|
*/
|
|
|
|
status = rtems_region_get_segment(
|
|
RTEMS_Malloc_Heap,
|
|
size,
|
|
RTEMS_NO_WAIT,
|
|
RTEMS_NO_TIMEOUT,
|
|
&return_this
|
|
);
|
|
|
|
if ( status != RTEMS_SUCCESSFUL ) {
|
|
/*
|
|
* Round to the "requested sbrk amount" so hopefully we won't have
|
|
* to grow again for a while. This effectively does sbrk() calls
|
|
* in "page" amounts.
|
|
*/
|
|
|
|
sbrk_amount = RTEMS_Malloc_Sbrk_amount;
|
|
|
|
if ( sbrk_amount == 0 )
|
|
return (void *) 0;
|
|
|
|
the_size = ((size + sbrk_amount) / sbrk_amount * sbrk_amount);
|
|
|
|
if (((rtems_unsigned32)starting_address = sbrk(the_size)) == -1)
|
|
return (void *) 0;
|
|
|
|
status = rtems_region_extend(
|
|
RTEMS_Malloc_Heap,
|
|
starting_address,
|
|
the_size
|
|
);
|
|
if ( status != RTEMS_SUCCESSFUL ) {
|
|
sbrk(-the_size);
|
|
errno = ENOMEM;
|
|
return (void *) 0;
|
|
}
|
|
|
|
MSBUMP(space_available, the_size);
|
|
|
|
status = rtems_region_get_segment(
|
|
RTEMS_Malloc_Heap,
|
|
size,
|
|
RTEMS_NO_WAIT,
|
|
RTEMS_NO_TIMEOUT,
|
|
&return_this
|
|
);
|
|
if ( status != RTEMS_SUCCESSFUL ) {
|
|
errno = ENOMEM;
|
|
return (void *) 0;
|
|
}
|
|
}
|
|
|
|
#ifdef MALLOC_STATS
|
|
if (return_this)
|
|
{
|
|
unsigned32 actual_size;
|
|
unsigned32 current_depth;
|
|
status = rtems_region_get_segment_size(RTEMS_Malloc_Heap, return_this, &actual_size);
|
|
MSBUMP(lifetime_allocated, actual_size);
|
|
current_depth = malloc_stats.lifetime_allocated - malloc_stats.lifetime_freed;
|
|
if (current_depth > malloc_stats.max_depth)
|
|
malloc_stats.max_depth = current_depth;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MALLOC_DIRTY
|
|
(void) memset(return_this, 0xCF, size);
|
|
#endif
|
|
|
|
return return_this;
|
|
}
|
|
|
|
void *calloc(
|
|
size_t nelem,
|
|
size_t elsize
|
|
)
|
|
{
|
|
register char *cptr;
|
|
int length;
|
|
|
|
MSBUMP(calloc_calls, 1);
|
|
|
|
length = nelem * elsize;
|
|
cptr = malloc( length );
|
|
if ( cptr )
|
|
memset( cptr, '\0', length );
|
|
|
|
MSBUMP(malloc_calls, -1); /* subtract off the malloc */
|
|
|
|
return cptr;
|
|
}
|
|
|
|
void *realloc(
|
|
void *ptr,
|
|
size_t size
|
|
)
|
|
{
|
|
rtems_unsigned32 old_size;
|
|
rtems_status_code status;
|
|
char *new_area;
|
|
|
|
MSBUMP(realloc_calls, 1);
|
|
|
|
if ( !ptr )
|
|
return malloc( size );
|
|
|
|
if ( !size ) {
|
|
free( ptr );
|
|
return (void *) 0;
|
|
}
|
|
|
|
new_area = malloc( size );
|
|
if ( !new_area ) {
|
|
free( ptr );
|
|
return (void *) 0;
|
|
}
|
|
|
|
status = rtems_region_get_segment_size( RTEMS_Malloc_Heap, ptr, &old_size );
|
|
if ( status != RTEMS_SUCCESSFUL ) {
|
|
errno = EINVAL;
|
|
return (void *) 0;
|
|
}
|
|
|
|
memcpy( new_area, ptr, (size < old_size) ? size : old_size );
|
|
free( ptr );
|
|
|
|
return new_area;
|
|
|
|
}
|
|
|
|
void free(
|
|
void *ptr
|
|
)
|
|
{
|
|
rtems_status_code status;
|
|
|
|
MSBUMP(free_calls, 1);
|
|
|
|
if ( !ptr )
|
|
return;
|
|
|
|
#ifdef MALLOC_STATS
|
|
{
|
|
unsigned32 size;
|
|
status = rtems_region_get_segment_size( RTEMS_Malloc_Heap, ptr, &size );
|
|
if ( status == RTEMS_SUCCESSFUL ) {
|
|
MSBUMP(lifetime_freed, size);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
status = rtems_region_return_segment( RTEMS_Malloc_Heap, ptr );
|
|
if ( status != RTEMS_SUCCESSFUL ) {
|
|
errno = EINVAL;
|
|
assert( 0 );
|
|
}
|
|
}
|
|
|
|
#ifdef MALLOC_STATS
|
|
/*
|
|
* Dump the malloc statistics
|
|
* May be called via atexit() (installable by our bsp) or
|
|
* at any time by user
|
|
*/
|
|
|
|
void malloc_dump(void)
|
|
{
|
|
unsigned32 allocated = malloc_stats.lifetime_allocated - malloc_stats.lifetime_freed;
|
|
|
|
printf("Malloc stats\n");
|
|
printf(" avail:%uk allocated:%uk (%d%%) max:%uk (%d%%) lifetime:%Luk freed:%Luk\n",
|
|
(unsigned int) malloc_stats.space_available / 1024,
|
|
(unsigned int) allocated / 1024,
|
|
/* avoid float! */
|
|
(allocated * 100) / malloc_stats.space_available,
|
|
(unsigned int) malloc_stats.max_depth / 1024,
|
|
(malloc_stats.max_depth * 100) / malloc_stats.space_available,
|
|
(unsigned long long) malloc_stats.lifetime_allocated / 1024,
|
|
(unsigned long long) malloc_stats.lifetime_freed / 1024);
|
|
printf(" Call counts: malloc:%d free:%d realloc:%d calloc:%d\n",
|
|
malloc_stats.malloc_calls,
|
|
malloc_stats.free_calls,
|
|
malloc_stats.realloc_calls,
|
|
malloc_stats.calloc_calls);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* "Reentrant" versions of the above routines implemented above.
|
|
*/
|
|
|
|
#ifdef RTEMS_NEWLIB
|
|
void *malloc_r(
|
|
struct _reent *ignored,
|
|
size_t size
|
|
)
|
|
{
|
|
return malloc( size );
|
|
}
|
|
|
|
void *calloc_r(
|
|
size_t nelem,
|
|
size_t elsize
|
|
)
|
|
{
|
|
return calloc( nelem, elsize );
|
|
}
|
|
|
|
void *realloc_r(
|
|
void *ptr,
|
|
size_t size
|
|
)
|
|
{
|
|
return realloc_r( ptr, size );
|
|
}
|
|
|
|
void free_r(
|
|
void *ptr
|
|
)
|
|
{
|
|
free( ptr );
|
|
}
|
|
#endif
|
|
|