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