source: rtems/cpukit/libcsupport/src/malloc.c @ 0ccd9daa

Last change on this file since 0ccd9daa was 0ccd9daa, checked in by Joel Sherrill <joel.sherrill@…>, on Sep 4, 2003 at 12:41:29 PM

2003-09-04 Joel Sherrill <joel@…>

PR 479/rtems_misc

  • src/malloc.c: Per multiple standards realloc does not free the original memory if the allocation of the requested larger block fails.
  • Property mode set to 100644
File size: 9.6 KB
Line 
1/*
2 *  RTEMS Malloc Family Implementation
3 *
4 *
5 *  COPYRIGHT (c) 1989-1999.
6 *  On-Line Applications Research Corporation (OAR).
7 *
8 *  The license and distribution terms for this file may be
9 *  found in the file LICENSE in this distribution or at
10 *  http://www.OARcorp.com/rtems/license.html.
11 *
12 *  $Id$
13 */
14
15#if HAVE_CONFIG_H
16#include "config.h"
17#endif
18
19#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__
20#include <rtems.h>
21#include <rtems/libcsupport.h>
22#include <rtems/score/apimutex.h>
23#ifdef RTEMS_NEWLIB
24#include <sys/reent.h>
25#endif
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <sys/types.h>
30#include <assert.h>
31#include <errno.h>
32#include <string.h>
33
34#include <unistd.h>    /* sbrk(2) */
35
36rtems_id RTEMS_Malloc_Heap;
37size_t RTEMS_Malloc_Sbrk_amount;
38
39#ifdef RTEMS_DEBUG
40#define MALLOC_STATS
41#define MALLOC_DIRTY
42#endif
43
44#ifdef MALLOC_STATS
45#define MSBUMP(f,n)    rtems_malloc_stats.f += (n)
46
47struct {
48    unsigned32  space_available;             /* current size of malloc area */
49    unsigned32  malloc_calls;                /* # calls to malloc */
50    unsigned32  free_calls;
51    unsigned32  realloc_calls;
52    unsigned32  calloc_calls;
53    unsigned32  max_depth;                   /* most ever malloc'd at 1 time */
54    unsigned64  lifetime_allocated;
55    unsigned64  lifetime_freed;
56} rtems_malloc_stats;
57
58#else                   /* No rtems_malloc_stats */
59#define MSBUMP(f,n)
60#endif
61
62void RTEMS_Malloc_Initialize(
63  void   *start,
64  size_t  length,
65  size_t  sbrk_amount
66)
67{
68  rtems_status_code   status;
69  void               *starting_address;
70  rtems_unsigned32    old_address;
71  rtems_unsigned32    u32_address;
72
73  /*
74   * If the starting address is 0 then we are to attempt to
75   * get length worth of memory using sbrk. Make sure we
76   * align the address that we get back.
77   */
78
79  starting_address = start;
80  RTEMS_Malloc_Sbrk_amount = sbrk_amount;
81
82  if (!starting_address) {
83    u32_address = (unsigned int)sbrk(length);
84
85    if (u32_address == (rtems_unsigned32) -1) {
86      rtems_fatal_error_occurred( RTEMS_NO_MEMORY );
87      /* DOES NOT RETURN!!! */
88    }
89
90    if (u32_address & (CPU_HEAP_ALIGNMENT-1)) {
91      old_address = u32_address;
92      u32_address = (u32_address + CPU_HEAP_ALIGNMENT) & ~(CPU_HEAP_ALIGNMENT-1);
93
94       /*
95        * adjust the length by whatever we aligned by
96        */
97
98      length -= u32_address - old_address;
99    }
100
101    starting_address = (void *)u32_address;
102  }
103
104  /*
105   *  If the BSP is not clearing out the workspace, then it is most likely
106   *  not clearing out the initial memory for the heap.  There is no
107   *  standard supporting zeroing out the heap memory.  But much code
108   *  with UNIX history seems to assume that memory malloc'ed during
109   *  initialization (before any free's) is zero'ed.  This is true most
110   *  of the time under UNIX because zero'ing memory when it is first
111   *  given to a process eliminates the chance of a process seeing data
112   *  left over from another process.  This would be a security violation.
113   */
114
115  if ( rtems_cpu_configuration_get_do_zero_of_workspace() )
116     memset( starting_address, 0, length );
117
118  /*
119   *  Unfortunately we cannot use assert if this fails because if this
120   *  has failed we do not have a heap and if we do not have a heap
121   *  STDIO cannot work because there will be no buffers.
122   */
123
124  status = rtems_region_create(
125    rtems_build_name( 'H', 'E', 'A', 'P' ),
126    starting_address,
127    length,
128    CPU_HEAP_ALIGNMENT,
129    RTEMS_DEFAULT_ATTRIBUTES,
130    &RTEMS_Malloc_Heap
131  );
132  if ( status != RTEMS_SUCCESSFUL )
133    rtems_fatal_error_occurred( status );
134
135#ifdef MALLOC_STATS
136  /* zero all the stats */
137  (void) memset( &rtems_malloc_stats, 0, sizeof(rtems_malloc_stats) );
138#endif
139 
140  MSBUMP(space_available, length);
141}
142
143#ifdef RTEMS_NEWLIB
144void *malloc(
145  size_t  size
146)
147{
148  void              *return_this;
149  void              *starting_address;
150  rtems_unsigned32   the_size;
151  rtems_unsigned32   sbrk_amount;
152  rtems_status_code  status;
153
154  MSBUMP(malloc_calls, 1);
155
156  if ( !size )
157    return (void *) 0;
158
159  /*
160   * Try to give a segment in the current region if there is not
161   * enough space then try to grow the region using rtems_region_extend().
162   * If this fails then return a NULL pointer.
163   */
164
165  status = rtems_region_get_segment(
166    RTEMS_Malloc_Heap,
167    size,
168    RTEMS_NO_WAIT,
169    RTEMS_NO_TIMEOUT,
170    &return_this
171  );
172
173  if ( status != RTEMS_SUCCESSFUL ) {
174    /*
175     *  Round to the "requested sbrk amount" so hopefully we won't have
176     *  to grow again for a while.  This effectively does sbrk() calls
177     *  in "page" amounts.
178     */
179
180    sbrk_amount = RTEMS_Malloc_Sbrk_amount;
181
182    if ( sbrk_amount == 0 )
183      return (void *) 0;
184
185    the_size = ((size + sbrk_amount) / sbrk_amount * sbrk_amount);
186
187    if (((rtems_unsigned32)starting_address = (void *)sbrk(the_size)) 
188            == (rtems_unsigned32) -1)
189      return (void *) 0;
190
191    status = rtems_region_extend(
192      RTEMS_Malloc_Heap,
193      starting_address,
194      the_size
195    );
196    if ( status != RTEMS_SUCCESSFUL ) {
197      sbrk(-the_size);
198      errno = ENOMEM;
199      return (void *) 0;
200    }
201   
202    MSBUMP(space_available, the_size);
203
204    status = rtems_region_get_segment(
205      RTEMS_Malloc_Heap,
206       size,
207       RTEMS_NO_WAIT,
208       RTEMS_NO_TIMEOUT,
209       &return_this
210    );
211    if ( status != RTEMS_SUCCESSFUL ) {
212      errno = ENOMEM;
213      return (void *) 0;
214    }
215  }
216
217#ifdef MALLOC_STATS
218  if (return_this)
219  {
220      unsigned32 actual_size;
221      unsigned32 current_depth;
222      status = rtems_region_get_segment_size(
223                   RTEMS_Malloc_Heap, return_this, &actual_size);
224      MSBUMP(lifetime_allocated, actual_size);
225      current_depth = rtems_malloc_stats.lifetime_allocated -
226                   rtems_malloc_stats.lifetime_freed;
227      if (current_depth > rtems_malloc_stats.max_depth)
228          rtems_malloc_stats.max_depth = current_depth;
229  }
230#endif
231
232#ifdef MALLOC_DIRTY
233  (void) memset(return_this, 0xCF, size);
234#endif
235
236  return return_this;
237}
238
239void *calloc(
240  size_t nelem,
241  size_t elsize
242)
243{
244  register char *cptr;
245  int length;
246
247  MSBUMP(calloc_calls, 1);
248
249  length = nelem * elsize;
250  cptr = malloc( length );
251  if ( cptr )
252    memset( cptr, '\0', length );
253
254  MSBUMP(malloc_calls, -1);   /* subtract off the malloc */
255
256  return cptr;
257}
258
259void *realloc(
260  void *ptr,
261  size_t size
262)
263{
264  rtems_unsigned32  old_size;
265  rtems_status_code status;
266  char *new_area;
267
268  MSBUMP(realloc_calls, 1);
269
270  if ( !ptr )
271    return malloc( size );
272
273  if ( !size ) {
274    free( ptr );
275    return (void *) 0;
276  }
277
278  new_area = malloc( size );
279 
280  MSBUMP(malloc_calls, -1);   /* subtract off the malloc */
281
282  /*
283   *  There used to be a free on this error case but it is wrong to
284   *  free the memory per OpenGroup Single UNIX Specification V2
285   *  and the C Standard.
286   */
287
288  if ( !new_area ) {
289    return (void *) 0;
290  }
291
292  status = rtems_region_get_segment_size( RTEMS_Malloc_Heap, ptr, &old_size );
293  if ( status != RTEMS_SUCCESSFUL ) {
294    errno = EINVAL;
295    return (void *) 0;
296  }
297
298  memcpy( new_area, ptr, (size < old_size) ? size : old_size );
299  free( ptr );
300
301  return new_area;
302
303}
304
305void free(
306  void *ptr
307)
308{
309  rtems_status_code status;
310
311  MSBUMP(free_calls, 1);
312
313  if ( !ptr )
314    return;
315
316#ifdef MALLOC_STATS
317  {
318      unsigned32        size;
319      status = rtems_region_get_segment_size( RTEMS_Malloc_Heap, ptr, &size );
320      if ( status == RTEMS_SUCCESSFUL ) {
321          MSBUMP(lifetime_freed, size);
322      }
323  }
324#endif
325 
326  status = rtems_region_return_segment( RTEMS_Malloc_Heap, ptr );
327  if ( status != RTEMS_SUCCESSFUL ) {
328    errno = EINVAL;
329    assert( 0 );
330  }
331}
332/* end if RTEMS_NEWLIB */
333#endif
334
335#ifdef MALLOC_STATS
336/*
337 * Dump the malloc statistics
338 * May be called via atexit()  (installable by our bsp) or
339 * at any time by user
340 */
341
342void malloc_dump(void)
343{
344    unsigned32 allocated = rtems_malloc_stats.lifetime_allocated -
345                     rtems_malloc_stats.lifetime_freed;
346
347    printf("Malloc stats\n");
348    printf("  avail:%uk  allocated:%uk (%d%%) "
349              "max:%uk (%d%%) lifetime:%Luk freed:%Luk\n",
350           (unsigned int) rtems_malloc_stats.space_available / 1024,
351           (unsigned int) allocated / 1024,
352           /* avoid float! */
353           (allocated * 100) / rtems_malloc_stats.space_available,
354           (unsigned int) rtems_malloc_stats.max_depth / 1024,
355           (rtems_malloc_stats.max_depth * 100) / rtems_malloc_stats.space_available,
356           (unsigned64) rtems_malloc_stats.lifetime_allocated / 1024,
357           (unsigned64) rtems_malloc_stats.lifetime_freed / 1024);
358    printf("  Call counts:   malloc:%d   free:%d   realloc:%d   calloc:%d\n",
359           rtems_malloc_stats.malloc_calls,
360           rtems_malloc_stats.free_calls,
361           rtems_malloc_stats.realloc_calls,
362           rtems_malloc_stats.calloc_calls);
363}
364
365
366void malloc_walk(size_t source, size_t printf_enabled)
367{
368   register Region_Control *the_region;
369   Objects_Locations        location;
370 
371  _RTEMS_Lock_allocator();                      /* to prevent deletion */
372   the_region = _Region_Get( RTEMS_Malloc_Heap, &location );
373   if ( location == OBJECTS_LOCAL )
374   {
375      _Heap_Walk( &the_region->Memory, source, printf_enabled );
376   }
377  _RTEMS_Unlock_allocator();
378}
379
380#else
381
382void malloc_dump(void)
383{
384   return;
385}
386 
387void malloc_walk(size_t source, size_t printf_enabled)
388{
389   return;
390}
391
392#endif
393
394/*
395 *  "Reentrant" versions of the above routines implemented above.
396 */
397
398#ifdef RTEMS_NEWLIB
399void *_malloc_r(
400  struct _reent *ignored,
401  size_t  size
402)
403{
404  return malloc( size );
405}
406
407void *_calloc_r(
408  struct _reent *ignored,
409  size_t nelem,
410  size_t elsize
411)
412{
413  return calloc( nelem, elsize );
414}
415
416void *_realloc_r(
417  struct _reent *ignored,
418  void *ptr,
419  size_t size
420)
421{
422  return realloc( ptr, size );
423}
424
425void _free_r(
426  struct _reent *ignored,
427  void *ptr
428)
429{
430  free( ptr );
431}
432
433#endif
Note: See TracBrowser for help on using the repository browser.