source: rtems/cpukit/libcsupport/src/malloc.c @ 55d7626

4.104.114.84.95
Last change on this file since 55d7626 was 55d7626, checked in by Joel Sherrill <joel.sherrill@…>, on Jul 11, 2007 at 8:56:10 PM

2007-07-11 Joel Sherrill <joel.sherrill@…>

  • libcsupport/src/malloc.c: Clean up Malloc debug code.
  • score/include/rtems/score/heap.h: Spacing.
  • score/inline/rtems/score/thread.inl:
  • score/src/heapfree.c. Clean up and add explicit check of the address being freed actually being in the heap.
  • score/src/heapwalk.c: Switch to printk and do not call abort.
  • Property mode set to 100644
File size: 14.0 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/protectedheap.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
88Heap_Control  RTEMS_Malloc_Heap;
89Chain_Control RTEMS_Malloc_GC_list;
90
91/* rtems_id RTEMS_Malloc_Heap; */
92size_t RTEMS_Malloc_Sbrk_amount;
93
94#ifdef RTEMS_DEBUG
95#define MALLOC_STATS
96#define MALLOC_DIRTY
97/*#define MALLOC_ARENA_CHECK
98void checkMallocArena(void); */
99#endif
100
101#ifdef MALLOC_STATS
102#define MSBUMP(f,n)    rtems_malloc_stats.f += (n)
103
104struct {
105    uint32_t    space_available;             /* current size of malloc area */
106    uint32_t    malloc_calls;                /* # calls to malloc */
107    uint32_t    free_calls;
108    uint32_t    realloc_calls;
109    uint32_t    calloc_calls;
110    uint32_t    max_depth;                   /* most ever malloc'd at 1 time */
111    uintmax_t    lifetime_allocated;
112    uintmax_t    lifetime_freed;
113} rtems_malloc_stats;
114
115#else                   /* No rtems_malloc_stats */
116#define MSBUMP(f,n)
117#endif
118
119void RTEMS_Malloc_Initialize(
120  void   *start,
121  size_t  length,
122  size_t  sbrk_amount
123)
124{
125  uint32_t      status;
126  void         *starting_address;
127  uintptr_t     old_address;
128  uintptr_t     uaddress;
129
130  /*
131   *  Initialize the garbage collection list to start with nothing on it.
132   */
133  Chain_Initialize_empty(&RTEMS_Malloc_GC_list);
134
135  /*
136   * If the starting address is 0 then we are to attempt to
137   * get length worth of memory using sbrk. Make sure we
138   * align the address that we get back.
139   */
140
141  starting_address = start;
142  RTEMS_Malloc_Sbrk_amount = sbrk_amount;
143
144  if (!starting_address) {
145    uaddress = (uintptr_t)sbrk(length);
146
147    if (uaddress == (uintptr_t) -1) {
148      rtems_fatal_error_occurred( RTEMS_NO_MEMORY );
149      /* DOES NOT RETURN!!! */
150    }
151
152    if (uaddress & (CPU_HEAP_ALIGNMENT-1)) {
153      old_address = uaddress;
154      uaddress = (uaddress + CPU_HEAP_ALIGNMENT) & ~(CPU_HEAP_ALIGNMENT-1);
155
156       /*
157        * adjust the length by whatever we aligned by
158        */
159
160      length -= uaddress - old_address;
161    }
162
163    starting_address = (void *)uaddress;
164  }
165
166  /*
167   *  If the BSP is not clearing out the workspace, then it is most likely
168   *  not clearing out the initial memory for the heap.  There is no
169   *  standard supporting zeroing out the heap memory.  But much code
170   *  with UNIX history seems to assume that memory malloc'ed during
171   *  initialization (before any free's) is zero'ed.  This is true most
172   *  of the time under UNIX because zero'ing memory when it is first
173   *  given to a process eliminates the chance of a process seeing data
174   *  left over from another process.  This would be a security violation.
175   */
176
177  if ( rtems_cpu_configuration_get_do_zero_of_workspace() )
178     memset( starting_address, 0, length );
179
180  /*
181   *  Unfortunately we cannot use assert if this fails because if this
182   *  has failed we do not have a heap and if we do not have a heap
183   *  STDIO cannot work because there will be no buffers.
184   */
185
186  status = _Protected_heap_Initialize( 
187    &RTEMS_Malloc_Heap,
188    starting_address,
189    length,
190    CPU_HEAP_ALIGNMENT
191  );
192  if ( !status )
193    rtems_fatal_error_occurred( status );
194
195#ifdef MALLOC_STATS
196  /* zero all the stats */
197  (void) memset( &rtems_malloc_stats, 0, sizeof(rtems_malloc_stats) );
198#endif
199
200  MSBUMP(space_available, length);
201}
202
203#ifdef RTEMS_NEWLIB
204void *malloc(
205  size_t  size
206)
207{
208  void        *return_this;
209  void        *starting_address;
210  uint32_t     the_size;
211  uint32_t     sbrk_amount;
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 heap if there is not
239   * enough space then try to grow the heap.
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  return_this = _Protected_heap_Allocate( &RTEMS_Malloc_Heap, size );
247
248  if ( !return_this ) {
249    /*
250     *  Round to the "requested sbrk amount" so hopefully we won't have
251     *  to grow again for a while.  This effectively does sbrk() calls
252     *  in "page" amounts.
253     */
254
255    sbrk_amount = RTEMS_Malloc_Sbrk_amount;
256
257    if ( sbrk_amount == 0 )
258      return (void *) 0;
259
260    the_size = ((size + sbrk_amount) / sbrk_amount * sbrk_amount);
261
262    if ((starting_address = (void *)sbrk(the_size))
263            == (void*) -1)
264      return (void *) 0;
265
266    if ( !_Protected_heap_Extend(
267            &RTEMS_Malloc_Heap, starting_address, the_size) ) {
268      sbrk(-the_size);
269      errno = ENOMEM;
270      return (void *) 0;
271    }
272
273    MSBUMP(space_available, the_size);
274
275    return_this = _Protected_heap_Allocate( &RTEMS_Malloc_Heap, size );
276    if ( !return_this ) {
277      errno = ENOMEM;
278      return (void *) 0;
279    }
280  }
281
282#ifdef MALLOC_STATS
283  if (return_this)
284  {
285      size_t     actual_size = 0;
286      uint32_t   current_depth;
287      void      *ptr = return_this;
288      _Protected_heap_Get_block_size(&RTEMS_Malloc_Heap, ptr, &actual_size);
289      MSBUMP(lifetime_allocated, actual_size);
290      current_depth = rtems_malloc_stats.lifetime_allocated -
291                   rtems_malloc_stats.lifetime_freed;
292      if (current_depth > rtems_malloc_stats.max_depth)
293          rtems_malloc_stats.max_depth = current_depth;
294  }
295#endif
296
297#ifdef MALLOC_DIRTY
298  (void) memset(return_this, 0xCF, size);
299#endif
300
301#ifdef MALLOC_ARENA_CHECK
302  {
303  struct mallocNode *mp = (struct mallocNode *)return_this;
304  int key, *fp, *nfp, i;
305  rtems_interrupt_disable(key);
306  mp->memory = mp + 1;
307  return_this = mp->memory;
308  mp->size = size - (sizeof(struct mallocNode) + SENTINELSIZE);
309  fp = (int *)&size - 2;
310  for (i = 0 ; i < CALLCHAINSIZE ; i++) {
311    mp->callChain[i] = fp[1];
312    nfp = (int *)(fp[0]);
313    if((nfp <= fp) || (nfp > (int *)(1 << 24)))
314     break;
315    fp = nfp;
316  }
317  while (i < CALLCHAINSIZE)
318    mp->callChain[i++] = 0;
319  memcpy((char *)mp->memory + mp->size, SENTINEL, SENTINELSIZE);
320  mp->forw = mallocNodeHead.forw;
321  mp->back = &mallocNodeHead;
322  mallocNodeHead.forw->back = mp;
323  mallocNodeHead.forw = mp;
324  rtems_interrupt_enable(key);
325  }
326#endif
327  return return_this;
328}
329
330void *calloc(
331  size_t nelem,
332  size_t elsize
333)
334{
335  register char *cptr;
336  int length;
337
338  MSBUMP(calloc_calls, 1);
339
340  length = nelem * elsize;
341  cptr = malloc( length );
342  if ( cptr )
343    memset( cptr, '\0', length );
344
345  MSBUMP(malloc_calls, -1);   /* subtract off the malloc */
346
347  return cptr;
348}
349
350void *realloc(
351  void *ptr,
352  size_t size
353)
354{
355  size_t old_size;
356  char *new_area;
357
358  MSBUMP(realloc_calls, 1);
359
360  /*
361   *  Do not attempt to allocate memory if in a critical section or ISR.
362   */
363
364  if (_System_state_Is_up(_System_state_Get())) {
365    if (_Thread_Dispatch_disable_level > 0)
366      return (void *) 0;
367
368    if (_ISR_Nest_level > 0)
369      return (void *) 0;
370  }
371
372  /*
373   * Continue with realloc().
374   */
375  if ( !ptr )
376    return malloc( size );
377
378  if ( !size ) {
379    free( ptr );
380    return (void *) 0;
381  }
382
383#ifdef MALLOC_ARENA_CHECK
384  {
385    void *np;
386    np = malloc(size);
387    if (!np) return np;
388    memcpy(np,ptr,size);
389    free(ptr);
390    return np;
391  }
392#endif
393  if ( _Protected_heap_Resize_block( &RTEMS_Malloc_Heap, ptr, size ) ) {
394    return ptr;
395  }
396
397  new_area = malloc( size );
398
399  MSBUMP(malloc_calls, -1);   /* subtract off the malloc */
400
401  /*
402   *  There used to be a free on this error case but it is wrong to
403   *  free the memory per OpenGroup Single UNIX Specification V2
404   *  and the C Standard.
405   */
406
407  if ( !new_area ) {
408    return (void *) 0;
409  }
410
411  if ( !_Protected_heap_Get_block_size(&RTEMS_Malloc_Heap, ptr, &old_size) ) {
412    errno = EINVAL;
413    return (void *) 0;
414  }
415
416  memcpy( new_area, ptr, (size < old_size) ? size : old_size );
417  free( ptr );
418
419  return new_area;
420
421}
422
423void free(
424  void *ptr
425)
426{
427  MSBUMP(free_calls, 1);
428
429  if ( !ptr )
430    return;
431
432  /*
433   *  Do not attempt to free memory if in a critical section or ISR.
434   */
435
436  if (_System_state_Is_up(_System_state_Get())) {
437    if ((_Thread_Dispatch_disable_level > 0) || (_ISR_Nest_level > 0)) {
438      Chain_Append(&RTEMS_Malloc_GC_list, (Chain_Node *)ptr);
439      return;
440    }
441  }
442
443#ifdef MALLOC_ARENA_CHECK
444  {
445  struct mallocNode *mp = (struct mallocNode *)ptr - 1;
446  struct mallocNode *mp1;
447  int key;
448  rtems_interrupt_disable(key);
449  if ((mp->memory != (mp + 1))
450   || (memcmp((char *)mp->memory + mp->size, SENTINEL, SENTINELSIZE) != 0))
451    reportMallocError("Freeing with inconsistent pointer/sentinel", mp);
452  mp1 = mallocNodeHead.forw;
453  while (mp1 != &mallocNodeHead) {
454    if (mp1 == mp)
455      break;
456    mp1 = mp1->forw;
457  }
458  if (mp1 != mp)
459    reportMallocError("Freeing, but not on allocated list", mp);
460  mp->forw->back = mp->back;
461  mp->back->forw = mp->forw;
462  mp->back = mp->forw = NULL;
463  ptr = mp;
464  rtems_interrupt_enable(key);
465  }
466#endif
467#ifdef MALLOC_STATS
468  {
469    size_t size;
470    if (_Protected_heap_Get_block_size(&RTEMS_Malloc_Heap, ptr, &size) ) {
471      MSBUMP(lifetime_freed, size);
472    }
473  }
474#endif
475
476  if ( !_Protected_heap_Free( &RTEMS_Malloc_Heap, ptr ) ) {
477    errno = EINVAL;
478    assert( 0 );
479  }
480}
481
482#ifdef MALLOC_ARENA_CHECK
483void checkMallocArena(void)
484{
485    struct mallocNode *mp = mallocNodeHead.forw;
486    int key;
487    rtems_interrupt_disable(key);
488    while (mp != &mallocNodeHead) {
489        if ((mp->forw->back != mp)
490         || (mp->back->forw != mp))
491            reportMallocError("Pointers mangled", mp);
492        if((mp->memory != (mp + 1))
493         || (memcmp((char *)mp->memory + mp->size, SENTINEL, SENTINELSIZE) != 0))
494            reportMallocError("Inconsistent pointer/sentinel", mp);
495        mp = mp->forw;
496    }
497    rtems_interrupt_enable(key);
498}
499#endif
500
501/* end if RTEMS_NEWLIB */
502#endif
503
504#ifdef MALLOC_STATS
505/*
506 * Dump the malloc statistics
507 * May be called via atexit()  (installable by our bsp) or
508 * at any time by user
509 */
510
511void malloc_dump(void)
512{
513    uint32_t   allocated = rtems_malloc_stats.lifetime_allocated -
514                     rtems_malloc_stats.lifetime_freed;
515
516    printf("Malloc stats\n");
517    printf("  avail:%"PRIu32"k  allocated:%"PRIu32"k (%"PRId32"%%) "
518              "max:%"PRIu32"k (%"PRIu32"%%)"
519              " lifetime:%"PRIuMAX"k freed:%"PRIuMAX"k\n",
520           rtems_malloc_stats.space_available / 1024,
521           allocated / 1024,
522           /* avoid float! */
523           (allocated * 100) / rtems_malloc_stats.space_available,
524           rtems_malloc_stats.max_depth / 1024,
525           (rtems_malloc_stats.max_depth * 100) / rtems_malloc_stats.space_available,
526           rtems_malloc_stats.lifetime_allocated / 1024,
527           rtems_malloc_stats.lifetime_freed / 1024
528           );
529    printf("  Call counts:   malloc:%"PRIu32"   free:%"PRIu32"   realloc:%"PRIu32"   calloc:%"PRIu32"\n",
530           rtems_malloc_stats.malloc_calls,
531           rtems_malloc_stats.free_calls,
532           rtems_malloc_stats.realloc_calls,
533           rtems_malloc_stats.calloc_calls);
534}
535
536
537void malloc_walk(size_t source, size_t printf_enabled)
538{
539  _Protected_heap_Walk( &RTEMS_Malloc_Heap, source, printf_enabled );
540}
541
542#else
543
544void malloc_dump(void)
545{
546   return;
547}
548
549void malloc_walk(size_t source, size_t printf_enabled)
550{
551   return;
552}
553
554#endif
555
556/*
557 *  "Reentrant" versions of the above routines implemented above.
558 */
559
560#ifdef RTEMS_NEWLIB
561void *_malloc_r(
562  struct _reent *ignored,
563  size_t  size
564)
565{
566  return malloc( size );
567}
568
569void *_calloc_r(
570  struct _reent *ignored,
571  size_t nelem,
572  size_t elsize
573)
574{
575  return calloc( nelem, elsize );
576}
577
578void *_realloc_r(
579  struct _reent *ignored,
580  void *ptr,
581  size_t size
582)
583{
584  return realloc( ptr, size );
585}
586
587void _free_r(
588  struct _reent *ignored,
589  void *ptr
590)
591{
592  free( ptr );
593}
594
595#endif
Note: See TracBrowser for help on using the repository browser.