source: rtems/cpukit/libcsupport/src/malloc.c @ 40ff680

4.104.114.84.95
Last change on this file since 40ff680 was 7d6701d, checked in by Ralf Corsepius <ralf.corsepius@…>, on 11/19/06 at 02:17:10

Fix typo in yesterday's patch.

  • Property mode set to 100644
File size: 14.9 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.rtems.com/license/LICENSE.
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#include <inttypes.h>
36
37#include <rtems/chain.h>
38
39#ifndef HAVE_UINTMAX_T
40/* Fall back to unsigned long if uintmax_t is not available */
41#define unsigned long uintmax_t
42
43#ifndef PRIuMAX
44#define PRIuMAX         "lu"
45#endif
46#endif
47
48#ifdef MALLOC_ARENA_CHECK
49#define SENTINELSIZE    12
50#define SENTINEL       "\xD1\xAC\xB2\xF1" "BITE ME"
51#define CALLCHAINSIZE 5
52struct mallocNode {
53    struct mallocNode *back;
54    struct mallocNode *forw;
55    int                callChain[CALLCHAINSIZE];
56    size_t             size;
57    void              *memory;
58};
59static struct mallocNode mallocNodeHead = { &mallocNodeHead, &mallocNodeHead };
60void reportMallocError(const char *msg, struct mallocNode *mp)
61{
62    unsigned char *sp = (unsigned char *)mp->memory + mp->size;
63    int i, ind = 0;
64    static char cbuf[500];
65    ind += sprintf(cbuf+ind, "Malloc Error: %s\n", msg);
66    if ((mp->forw->back != mp) || (mp->back->forw != mp))
67        ind += sprintf(cbuf+ind, "mp:0x%x  mp->forw:0x%x  mp->forw->back:0x%x  mp->back:0x%x  mp->back->forw:0x%x\n",
68                        mp, mp->forw, mp->forw->back, mp->back, mp->back->forw);
69    if (mp->memory != (mp + 1))
70        ind += sprintf(cbuf+ind, "mp+1:0x%x  ", mp + 1);
71    ind += sprintf(cbuf+ind, "mp->memory:0x%x  mp->size:%d\n", mp->memory, mp->size);
72    if (memcmp((char *)mp->memory + mp->size, SENTINEL, SENTINELSIZE) != 0) {
73        ind += sprintf(cbuf+ind, "mp->sentinel: ");
74        for (i = 0 ; i < SENTINELSIZE ; i++)
75            ind += sprintf(cbuf+ind, " 0x%x", sp[i]);
76        ind += sprintf(cbuf+ind, "\n");
77    }
78    ind += sprintf(cbuf+ind, "Call chain:");
79    for (i = 0 ; i < CALLCHAINSIZE ; i++) {
80        if (mp->callChain[i] == 0)
81            break;
82        ind += sprintf(cbuf+ind, " 0x%x", mp->callChain[i]);
83    }
84    printk("\n\n%s\n\n", cbuf);
85}
86#endif
87
88Chain_Control RTEMS_Malloc_GC_list;
89
90rtems_id RTEMS_Malloc_Heap;
91size_t RTEMS_Malloc_Sbrk_amount;
92
93#ifdef RTEMS_DEBUG
94#define MALLOC_STATS
95#define MALLOC_DIRTY
96#endif
97
98#ifdef MALLOC_STATS
99#define MSBUMP(f,n)    rtems_malloc_stats.f += (n)
100
101struct {
102    uint32_t    space_available;             /* current size of malloc area */
103    uint32_t    malloc_calls;                /* # calls to malloc */
104    uint32_t    free_calls;
105    uint32_t    realloc_calls;
106    uint32_t    calloc_calls;
107    uint32_t    max_depth;                   /* most ever malloc'd at 1 time */
108    uintmax_t    lifetime_allocated;
109    uintmax_t    lifetime_freed;
110} rtems_malloc_stats;
111
112#else                   /* No rtems_malloc_stats */
113#define MSBUMP(f,n)
114#endif
115
116void RTEMS_Malloc_Initialize(
117  void   *start,
118  size_t  length,
119  size_t  sbrk_amount
120)
121{
122  rtems_status_code   status;
123  void               *starting_address;
124  uintptr_t           old_address;
125  uintptr_t           uaddress;
126
127  /*
128   *  Initialize the garbage collection list to start with nothing on it.
129   */
130  Chain_Initialize_empty(&RTEMS_Malloc_GC_list);
131
132  /*
133   * If the starting address is 0 then we are to attempt to
134   * get length worth of memory using sbrk. Make sure we
135   * align the address that we get back.
136   */
137
138  starting_address = start;
139  RTEMS_Malloc_Sbrk_amount = sbrk_amount;
140
141  if (!starting_address) {
142    uaddress = (uintptr_t)sbrk(length);
143
144    if (uaddress == (uintptr_t) -1) {
145      rtems_fatal_error_occurred( RTEMS_NO_MEMORY );
146      /* DOES NOT RETURN!!! */
147    }
148
149    if (uaddress & (CPU_HEAP_ALIGNMENT-1)) {
150      old_address = uaddress;
151      uaddress = (uaddress + CPU_HEAP_ALIGNMENT) & ~(CPU_HEAP_ALIGNMENT-1);
152
153       /*
154        * adjust the length by whatever we aligned by
155        */
156
157      length -= uaddress - old_address;
158    }
159
160    starting_address = (void *)uaddress;
161  }
162
163  /*
164   *  If the BSP is not clearing out the workspace, then it is most likely
165   *  not clearing out the initial memory for the heap.  There is no
166   *  standard supporting zeroing out the heap memory.  But much code
167   *  with UNIX history seems to assume that memory malloc'ed during
168   *  initialization (before any free's) is zero'ed.  This is true most
169   *  of the time under UNIX because zero'ing memory when it is first
170   *  given to a process eliminates the chance of a process seeing data
171   *  left over from another process.  This would be a security violation.
172   */
173
174  if ( rtems_cpu_configuration_get_do_zero_of_workspace() )
175     memset( starting_address, 0, length );
176
177  /*
178   *  Unfortunately we cannot use assert if this fails because if this
179   *  has failed we do not have a heap and if we do not have a heap
180   *  STDIO cannot work because there will be no buffers.
181   */
182
183  status = rtems_region_create(
184    rtems_build_name( 'H', 'E', 'A', 'P' ),
185    starting_address,
186    length,
187    CPU_HEAP_ALIGNMENT,
188    RTEMS_DEFAULT_ATTRIBUTES,
189    &RTEMS_Malloc_Heap
190  );
191  if ( status != RTEMS_SUCCESSFUL )
192    rtems_fatal_error_occurred( status );
193
194#ifdef MALLOC_STATS
195  /* zero all the stats */
196  (void) memset( &rtems_malloc_stats, 0, sizeof(rtems_malloc_stats) );
197#endif
198
199  MSBUMP(space_available, length);
200}
201
202#ifdef RTEMS_NEWLIB
203void *malloc(
204  size_t  size
205)
206{
207  void              *return_this;
208  void              *starting_address;
209  uint32_t     the_size;
210  uint32_t     sbrk_amount;
211  rtems_status_code  status;
212  Chain_Node        *to_be_freed;
213
214  MSBUMP(malloc_calls, 1);
215
216  if ( !size )
217    return (void *) 0;
218
219  /*
220   *  Do not attempt to allocate memory if in a critical section or ISR.
221   */
222
223  if (_System_state_Is_up(_System_state_Get())) {
224    if (_Thread_Dispatch_disable_level > 0)
225      return (void *) 0;
226
227    if (_ISR_Nest_level > 0)
228      return (void *) 0;
229  }
230
231  /*
232   *  If some free's have been deferred, then do them now.
233   */
234  while ((to_be_freed = Chain_Get(&RTEMS_Malloc_GC_list)) != NULL)
235    free(to_be_freed);
236
237  /*
238   * Try to give a segment in the current region if there is not
239   * enough space then try to grow the region using rtems_region_extend().
240   * If this fails then return a NULL pointer.
241   */
242
243#ifdef MALLOC_ARENA_CHECK
244  size += sizeof(struct mallocNode) + SENTINELSIZE;
245#endif
246  status = rtems_region_get_segment(
247    RTEMS_Malloc_Heap,
248    size,
249    RTEMS_NO_WAIT,
250    RTEMS_NO_TIMEOUT,
251    &return_this
252  );
253
254  if ( status != RTEMS_SUCCESSFUL ) {
255    /*
256     *  Round to the "requested sbrk amount" so hopefully we won't have
257     *  to grow again for a while.  This effectively does sbrk() calls
258     *  in "page" amounts.
259     */
260
261    sbrk_amount = RTEMS_Malloc_Sbrk_amount;
262
263    if ( sbrk_amount == 0 )
264      return (void *) 0;
265
266    the_size = ((size + sbrk_amount) / sbrk_amount * sbrk_amount);
267
268    if ((starting_address = (void *)sbrk(the_size))
269            == (void*) -1)
270      return (void *) 0;
271
272    status = rtems_region_extend(
273      RTEMS_Malloc_Heap,
274      starting_address,
275      the_size
276    );
277    if ( status != RTEMS_SUCCESSFUL ) {
278      sbrk(-the_size);
279      errno = ENOMEM;
280      return (void *) 0;
281    }
282
283    MSBUMP(space_available, the_size);
284
285    status = rtems_region_get_segment(
286      RTEMS_Malloc_Heap,
287       size,
288       RTEMS_NO_WAIT,
289       RTEMS_NO_TIMEOUT,
290       &return_this
291    );
292    if ( status != RTEMS_SUCCESSFUL ) {
293      errno = ENOMEM;
294      return (void *) 0;
295    }
296  }
297
298#ifdef MALLOC_STATS
299  if (return_this)
300  {
301      size_t     actual_size;
302      uint32_t   current_depth;
303      status = rtems_region_get_segment_size(
304                   RTEMS_Malloc_Heap, return_this, &actual_size);
305      MSBUMP(lifetime_allocated, actual_size);
306      current_depth = rtems_malloc_stats.lifetime_allocated -
307                   rtems_malloc_stats.lifetime_freed;
308      if (current_depth > rtems_malloc_stats.max_depth)
309          rtems_malloc_stats.max_depth = current_depth;
310  }
311#endif
312
313#ifdef MALLOC_DIRTY
314  (void) memset(return_this, 0xCF, size);
315#endif
316
317#ifdef MALLOC_ARENA_CHECK
318  {
319  struct mallocNode *mp = (struct mallocNode *)return_this;
320  int key, *fp, *nfp, i;
321  rtems_interrupt_disable(key);
322  mp->memory = mp + 1;
323  return_this = mp->memory;
324  mp->size = size - (sizeof(struct mallocNode) + SENTINELSIZE);
325  fp = (int *)&size - 2;
326  for (i = 0 ; i < CALLCHAINSIZE ; i++) {
327    mp->callChain[i] = fp[1];
328    nfp = (int *)(fp[0]);
329    if((nfp <= fp) || (nfp > (int *)(1 << 24)))
330     break;
331    fp = nfp;
332  }
333  while (i < CALLCHAINSIZE)
334    mp->callChain[i++] = 0;
335  memcpy((char *)mp->memory + mp->size, SENTINEL, SENTINELSIZE);
336  mp->forw = mallocNodeHead.forw;
337  mp->back = &mallocNodeHead;
338  mallocNodeHead.forw->back = mp;
339  mallocNodeHead.forw = mp;
340  rtems_interrupt_enable(key);
341  }
342#endif
343  return return_this;
344}
345
346void *calloc(
347  size_t nelem,
348  size_t elsize
349)
350{
351  register char *cptr;
352  int length;
353
354  MSBUMP(calloc_calls, 1);
355
356  length = nelem * elsize;
357  cptr = malloc( length );
358  if ( cptr )
359    memset( cptr, '\0', length );
360
361  MSBUMP(malloc_calls, -1);   /* subtract off the malloc */
362
363  return cptr;
364}
365
366void *realloc(
367  void *ptr,
368  size_t size
369)
370{
371  size_t old_size;
372  rtems_status_code status;
373  char *new_area;
374
375  MSBUMP(realloc_calls, 1);
376
377  /*
378   *  Do not attempt to allocate memory if in a critical section or ISR.
379   */
380
381  if (_System_state_Is_up(_System_state_Get())) {
382    if (_Thread_Dispatch_disable_level > 0)
383      return (void *) 0;
384
385    if (_ISR_Nest_level > 0)
386      return (void *) 0;
387  }
388
389  /*
390   * Continue with realloc().
391   */
392  if ( !ptr )
393    return malloc( size );
394
395  if ( !size ) {
396    free( ptr );
397    return (void *) 0;
398  }
399
400#ifdef MALLOC_ARENA_CHECK
401  {
402  void *np;
403  np = malloc(size);
404  if (!np) return np;
405  memcpy(np,ptr,size);
406  free(ptr);
407  return np;
408  }
409#endif
410  status =
411    rtems_region_resize_segment( RTEMS_Malloc_Heap, ptr, size, &old_size );
412
413  if( status == RTEMS_SUCCESSFUL ) {
414    return ptr;
415  }
416  else if ( status != RTEMS_UNSATISFIED ) {
417    errno = EINVAL;
418    return (void *) 0;
419  }
420
421  new_area = malloc( size );
422
423  MSBUMP(malloc_calls, -1);   /* subtract off the malloc */
424
425  /*
426   *  There used to be a free on this error case but it is wrong to
427   *  free the memory per OpenGroup Single UNIX Specification V2
428   *  and the C Standard.
429   */
430
431  if ( !new_area ) {
432    return (void *) 0;
433  }
434
435  status = rtems_region_get_segment_size( RTEMS_Malloc_Heap, ptr, &old_size );
436  if ( status != RTEMS_SUCCESSFUL ) {
437    errno = EINVAL;
438    return (void *) 0;
439  }
440
441  memcpy( new_area, ptr, (size < old_size) ? size : old_size );
442  free( ptr );
443
444  return new_area;
445
446}
447
448void free(
449  void *ptr
450)
451{
452  rtems_status_code status;
453
454  MSBUMP(free_calls, 1);
455
456  if ( !ptr )
457    return;
458
459  /*
460   *  Do not attempt to free memory if in a critical section or ISR.
461   */
462
463  if (_System_state_Is_up(_System_state_Get())) {
464    if ((_Thread_Dispatch_disable_level > 0) || (_ISR_Nest_level > 0)) {
465      Chain_Append(&RTEMS_Malloc_GC_list, (Chain_Node *)ptr);
466      return;
467    }
468  }
469
470#ifdef MALLOC_ARENA_CHECK
471  {
472  struct mallocNode *mp = (struct mallocNode *)ptr - 1;
473  struct mallocNode *mp1;
474  int key;
475  rtems_interrupt_disable(key);
476  if ((mp->memory != (mp + 1))
477   || (memcmp((char *)mp->memory + mp->size, SENTINEL, SENTINELSIZE) != 0))
478    reportMallocError("Freeing with inconsistent pointer/sentinel", mp);
479  mp1 = mallocNodeHead.forw;
480  while (mp1 != &mallocNodeHead) {
481    if (mp1 == mp)
482      break;
483    mp1 = mp1->forw;
484  }
485  if (mp1 != mp)
486    reportMallocError("Freeing, but not on allocated list", mp);
487  mp->forw->back = mp->back;
488  mp->back->forw = mp->forw;
489  mp->back = mp->forw = NULL;
490  ptr = mp;
491  rtems_interrupt_enable(key);
492  }
493#endif
494#ifdef MALLOC_STATS
495  {
496      size_t size;
497      status = rtems_region_get_segment_size( RTEMS_Malloc_Heap, ptr, &size );
498      if ( status == RTEMS_SUCCESSFUL ) {
499          MSBUMP(lifetime_freed, size);
500      }
501  }
502#endif
503
504  status = rtems_region_return_segment( RTEMS_Malloc_Heap, ptr );
505  if ( status != RTEMS_SUCCESSFUL ) {
506    errno = EINVAL;
507    assert( 0 );
508  }
509}
510
511#ifdef MALLOC_ARENA_CHECK
512void checkMallocArena(void)
513{
514    struct mallocNode *mp = mallocNodeHead.forw;
515    int key;
516    rtems_interrupt_disable(key);
517    while (mp != &mallocNodeHead) {
518        if ((mp->forw->back != mp)
519         || (mp->back->forw != mp))
520            reportMallocError("Pointers mangled", mp);
521        if((mp->memory != (mp + 1))
522         || (memcmp((char *)mp->memory + mp->size, SENTINEL, SENTINELSIZE) != 0))
523            reportMallocError("Inconsistent pointer/sentinel", mp);
524        mp = mp->forw;
525    }
526    rtems_interrupt_enable(key);
527}
528#endif
529
530/* end if RTEMS_NEWLIB */
531#endif
532
533#ifdef MALLOC_STATS
534/*
535 * Dump the malloc statistics
536 * May be called via atexit()  (installable by our bsp) or
537 * at any time by user
538 */
539
540void malloc_dump(void)
541{
542    uint32_t   allocated = rtems_malloc_stats.lifetime_allocated -
543                     rtems_malloc_stats.lifetime_freed;
544
545    printf("Malloc stats\n");
546    printf("  avail:%"PRIu32"k  allocated:%"PRIu32"k (%"PRId32"%%) "
547              "max:%"PRIu32"k (%"PRIu32"%%)"
548              " lifetime:%"PRIuMAX"k freed:%"PRIuMAX"k\n",
549           rtems_malloc_stats.space_available / 1024,
550           allocated / 1024,
551           /* avoid float! */
552           (allocated * 100) / rtems_malloc_stats.space_available,
553           rtems_malloc_stats.max_depth / 1024,
554           (rtems_malloc_stats.max_depth * 100) / rtems_malloc_stats.space_available,
555           rtems_malloc_stats.lifetime_allocated / 1024,
556           rtems_malloc_stats.lifetime_freed / 1024
557           );
558    printf("  Call counts:   malloc:%"PRIu32"   free:%"PRIu32"   realloc:%"PRIu32"   calloc:%"PRIu32"\n",
559           rtems_malloc_stats.malloc_calls,
560           rtems_malloc_stats.free_calls,
561           rtems_malloc_stats.realloc_calls,
562           rtems_malloc_stats.calloc_calls);
563}
564
565
566void malloc_walk(size_t source, size_t printf_enabled)
567{
568   register Region_Control *the_region;
569   Objects_Locations        location;
570
571  _RTEMS_Lock_allocator();                      /* to prevent deletion */
572   the_region = _Region_Get( RTEMS_Malloc_Heap, &location );
573   if ( location == OBJECTS_LOCAL )
574   {
575      _Heap_Walk( &the_region->Memory, source, printf_enabled );
576   }
577  _RTEMS_Unlock_allocator();
578}
579
580#else
581
582void malloc_dump(void)
583{
584   return;
585}
586
587void malloc_walk(size_t source, size_t printf_enabled)
588{
589   return;
590}
591
592#endif
593
594/*
595 *  "Reentrant" versions of the above routines implemented above.
596 */
597
598#ifdef RTEMS_NEWLIB
599void *_malloc_r(
600  struct _reent *ignored,
601  size_t  size
602)
603{
604  return malloc( size );
605}
606
607void *_calloc_r(
608  struct _reent *ignored,
609  size_t nelem,
610  size_t elsize
611)
612{
613  return calloc( nelem, elsize );
614}
615
616void *_realloc_r(
617  struct _reent *ignored,
618  void *ptr,
619  size_t size
620)
621{
622  return realloc( ptr, size );
623}
624
625void _free_r(
626  struct _reent *ignored,
627  void *ptr
628)
629{
630  free( ptr );
631}
632
633#endif
Note: See TracBrowser for help on using the repository browser.