Memory profiling user heap

Memory profiling can use heap management routines on target hardware platforms where there are no or only partial provisions for memory management instructions.

When using Memory profiling on some embedded or real-time target platforms, you might encounter one of the following situations:
  • Situation 1: There are no provisions for malloc, calloc, realloc or free functions on the target platform. The program uses custom heap management routines that may use a user API. Such routines could, for example, be based on a static buffer that performs memory allocation and free functions. In this case, you need to customize the memory heap parameters RTRT_DO_MALLOC and RTRT_DO_FREE in the target deployment port (TDP) to use the custom malloc and free functions. In this case, you can access the custom API functions.
  • Situation 2: There are partial implementations of malloc, calloc, realloc or free functions on the target platform, but other functions provide methods of allocating or freeing heap memory. In this case, you do not have access to any custom API. This requires customization of the TDP. Refer to the documentation provided in the target deployment port editor for customization options.
In both of the above situations, memory profiling can use the heap management routines to detect memory leaks, array bounds and other memory-related defects.
Note: Application pointers and block sizes can be modified by memory profiling in order to detect Late Detect Array Bounds Write (ABWL) errors. Actual-pointer and actual size refer to the memory data handled by memory profiling, whereas user pointer and user size refer to the memory handled natively by the application-under-analysis. This distinction is important for the memory profiling ABWL and red zone settings.

Target deployment port API

The TDP library provides the following API for memory profiling:
void * _PurifyLTHeapAction ( _PurifyLT_API_ACTION, void *, RTRT_U_INT32, RTRT_U_INT8 );
In the function _PurifyLTHeapAction, the first parameter is the type of action that will be or has been performed on the memory block pointed by the second parameter. The following actions can be used:
typedef enum {
        _PurifyLT_API_ALLOC,
        _PurifyLT_API_BEFORE_REALLOC,
        _PurifyLT_API_FREE
} _PurifyLT_API_ACTION;
The third parameter is the size of the block. The fourth parameter is either of the following constants:
#define _PurifyLT_NO_DELAYED_FREE    0
#define _PurifyLT_DELAYED_FREE       1

If an allocation or free instruction has a size of 0, this fourth parameter indicates a delayed free in order to detect Late Detect Free Memory Write (FWML ) and Freeing Freed Memory (FFM) errors. See the Build configuration settings section for the memory profiling settings.

A freed delay can only be performed if the block can be freed with RTRT_DO_FREE (for the situation 1 described previously) or ANSI C free (for situation 2). For example, if a function requires more parameters than the pointer to deallocate, then the FMWL and FFM error detection cannot be supported and FFM errors will be indicated by a Freeing Unallocated Memory (FUM) error instead.

The following function returns the size of an allocated block, or 0 if the block was not declared to Memory Profiling. This allows you to implement a library function similar to the msize from Microsoft Visual 6.0.
RTRT_SIZE_T _PurifyLTHeapPtrSize ( void * );
The following function returns the actual-size of a memory block, depending on the size requested. Call this function before the actual allocation to find out the quantity of memory that is available for the block and the contiguous red zones that are to be monitored by memory profiling.
RTRT_SIZE_T _PurifyLTHeapActualSize ( RTRT_SIZE_T );

Example

In the following examples, my_malloc, my_realloc, my_free and my_msize demonstrate the four supported memory heap behaviors. The following routine declares an allocation:
void *my_malloc ( int partId, size_t size )
{
  void *ret;
  size_t actual_size = _PurifyLTHeapActualSize(size);
  /* Here is any user code making ret a pointer to a heap or
     simulated heap memory block of actual_size bytes */
  ...
  /* Then comes the memory profiling action */
  return _PurifyLTHeapAction ( _PurifyLT_API_ALLOC, ret, size, 0 );
  /* The user-pointer is returned */
}

In situation 2, where you have access to a custom memory heap API, replace the "..." with the actual malloc API function.

For a my_calloc(size_t nelem, size_t elsize), pass on nelem*elsize as the third parameter of the _PurifyLTHeapAction function. In this case, you might need to replace this operation with a function that takes into account the alignments of elements.

To declare a reallocation, two operations are required:
void *my_realloc ( int partId, void * ptr, size_t size )
{
  void *ret;
  size_t actual_size = _PurifyLTHeapActualSize(size);
  /* Before comes first Memory Profiling action */
  ret = _PurifyLTHeapAction ( _PurifyLT_API_BEFORE_REALLOC, ptr, size, 0 );
  /* ret now contains the actual-pointer */
  /* Here is any user code making ret a reallocated pointer to a heap or
     simulated heap memory block of actual_size bytes */
  ...
  /* After comes second Memory Profiling action */
  return _PurifyLTHeapAction ( _PurifyLT_API_ALLOC, ret, size, 0 );
  /* The user-pointer is returned */
}
To free memory without using the delay:
void my_free ( int partId, void * ptr )
{
  /* Memory Profiling action comes first */
  void *ret = _PurifyLTHeapAction ( _PurifyLT_API_FREE, ptr, 0, 0 );
  /* Any code insuring actual deallocation of ret */
}
To free memory using a delay:
void my_free ( int partId, void * ptr )
{
  /* Memory Profiling action comes first */
  void *ret = _PurifyLTHeapAction ( _PurifyLT_API_FREE, ptr, 0, 1 );
  /* Nothing to do here */
}
To obtain the user size of a block:
size_t my_msize ( int partId, void * ptr )
{
  return _PurifyLTHeapPtrSize ( ptr );
}
Use the following macros to save customization time when dealing with functions that have the same prototypes as the standard ANSI functions:
#define _PurifyLT_MALLOC_LIKE(func) \
void *RTRT_CONCAT_MACRO(usr_,func) ( RTRT_SIZE_T size ) \
{ \
  void *ret; \
  ret = func ( _PurifyLTHeapActualSize ( size ) ); \
  return _PurifyLTHeapAction ( _PurifyLT_API_ALLOC, ret, size, 0 ); \
}
#define _PurifyLT_CALLOC_LIKE(func) \
void *RTRT_CONCAT_MACRO(usr_,func) ( RTRT_SIZE_T nelem, RTRT_SIZE_T elsize ) \
{ \
  void *ret; \
  ret = func ( _PurifyLTHeapActualSize ( nelem * elsize ) ); \
  return _PurifyLTHeapAction ( _PurifyLT_API_ALLOC, ret, nelem * elsize, 0 ); \
}
#define _PurifyLT_REALLOC_LIKE(func,delayed_free) \
void *RTRT_CONCAT_MACRO(usr_,func) ( void *ptr, RTRT_SIZE_T size ) \
{ \
  void *ret; \
  ret = func ( _PurifyLTHeapAction ( _PurifyLT_API_BEFORE_REALLOC, \
                                     ptr, size, delayed_free ), \
               _PurifyLTHeapActualSize ( size ) ); \
  return _PurifyLTHeapAction ( _PurifyLT_API_ALLOC, ret, size, 0 ); \
}
#define _PurifyLT_FREE_LIKE(func,delayed_free) \
void RTRT_CONCAT_MACRO(usr_,func) ( void *ptr ) \
{ \
  if ( delayed_free ) \
  { \
    _PurifyLTHeapAction ( _PurifyLT_API_FREE, ptr, 0, delayed_free ); \
  } \
  else \
  { \
    func ( _PurifyLTHeapAction ( _PurifyLT_API_FREE, ptr, 0, delayed_free ) ); \
  } \
}