Intel® Inspector User Guide for Linux* OS

ID 767796
Date 7/13/2023
Public

A newer version of this document is available. Customers should click here to go to the newest version.

Document Table of Contents

APIs for Custom Memory Allocation

Intel Inspector provides a set of APIs to help it identify the semantics of your malloc-like heap management functions. Annotating your code with these APIs reduces the number of false positives the Intel Inspector reports when analyzing your code.

Usage Tips

Follow these guidelines when using the memory allocation APIs:

  • Create wrapper functions for your routines, and put the __itt_heap_*_begin and __itt_heap_*_end calls in these functions.

  • Allocate a unique domain for each pair of allocate/free functions when calling __itt_heap_function_create. This allows the Intel Inspector to verify a matching free function is called for every allocate function call.

  • Annotate the beginning and end of every allocate function and free function.

  • Call all function pairs from the same stack frame, otherwise the Intel Inspector assumes an exception occurred and the allocation attempt failed.

  • Do not call an end function without first calling the matching begin function.

Using Memory Allocation APIs in Your Code

Use This

To Do This

typedef void* 
__itt_heap_function;
 
__itt_heap_function 
__itt_heap_function_create( 
      const __itt_char* name, 
      const __itt_char* domain ); 

Declare a handle type to match begin and end calls and domains.

  • name = Name of the function you want to annotate.

  • domain = String identifying a matching set of functions. For example, if there are three functions that all work with my_struct, such as alloc_my_structs, free_my_structs, and realloc_my_structs, pass the same domain to all three __itt_heap_function_create() calls.

NOTE:

Parameters of type __itt_char follow the Windows* OS unicode convention. If UNICODE is defined when compiling on a Windows* OS, __itt_char is wchar_t; otherwise it is char.

void __itt_heap_allocate_begin( 
   __itt_heap_function h, 
   size_t size, 
   int initialized );
 
void __itt_heap_allocate_end( 
   __itt_heap_function h, 
   void** addr, 
   size_t size, 
   int initialized ); 

Identify allocation functions.

  • h = Handle returned when this function's name was passed to __itt_heap_function_create().

  • size = Size in bytes of the requested memory region.

  • initialized = Flag indicating if the memory region will be initialized by this routine.

  • addr = Pointer to the address of the memory region this function has allocated, or 0 if the allocation failed.

void __itt_heap_free_begin( 
   __itt_heap_function h, 
   void* addr );
 
void __itt_heap_free_end( 
   __itt_heap_function h, 
   void* addr ); 

Identify deallocation functions.

  • h = Handle returned when this function's name was passed to __itt_heap_function_create().

  • addr = Pointer to the address of the memory region this function is deallocating.

void __itt_heap_reallocate_begin( 
   __itt_heap_function h, 
   void* addr, 
   size_t new_size, 
   int initialized );
 
void __itt_heap_reallocate_end( 
   __itt_heap_function h, 
   void* addr, 
   void** new_addr, 
   size_t new_size, 
   int initialized ); 

Identify reallocation functions.

Note that itt_heap_reallocate_end() must be called after the attempt even if no memory is returned. Intel Inspector assumes C-runtime realloc semantics.

  • h = Handle returned when this function's name was passed to __itt_heap_function_create().

  • addr = Pointer to the address of the memory region this function is reallocating. If addr is NULL, the Intel Inspector treats this as if it is an allocation.

  • new_addr = Pointer to a pointer to hold the address of the reallocated memory region.

  • size = Size in bytes of the requested memory region. If new_size is 0, the Intel Inspector treats this as if it is a deallocation.

void __itt_heap_internal_access_begin( void ); 

void __itt_heap_internal_access_end( void ); 

Identify functions related to private heap management, such as queries for remaining heap size and validation of heap state.

This function tells the Intel Inspector that this memory access is intentional and should not be flagged.

Call __itt_heap_internal_access_begin() before accessing the underlying heap management internals and __itt_heap_internal_access_end() after the access is finished

void __itt_heap_record_memory_growth_begin( void ); 

void __itt_heap_record_memory_growth_end( void ); 

Produce reports of memory growth that occurs between calls to __itt_heap_record_memory_growth_begin() and __itt_heap_record_memory_growth_end()

The report identifies any memory allocated but not freed between the two calls. Any memory allocated but not freed prior to the first call to __itt_heap_record_memory_growth_begin() is not reported. Any memory allocated but not freed after the final call to__itt_heap_record_memory_growth_end() is reported when collection is complete.

Usage Example: Heap Allocation

#include <ittnotify.h>

void* user_defined_malloc(size_t size);
void user_defined_free(void *p);
void* user_defined_realloc(void *p, size_t s);

__itt_heap_function my_allocator;
__itt_heap_function my_reallocator;
__itt_heap_function my_freer;

void* my_malloc(size_t s)
{
    void* p;

    __itt_heap_allocate_begin(my_allocator, s, 0);
    p = user_defined_malloc(s);
    __itt_heap_allocate_end(my_allocator, &p, s, 0);

    return p;
}


void my_free(void *p)
{
    __itt_heap_free_begin (my_freer, p);
    user_defined_free(p);
    __itt_heap_free_end (my_freer, p);
}

void* my_realloc(void *p, size_t s)
{
    void *np;

    __itt_heap_reallocate_begin (my_reallocator, p, s, 0);
    np = user_defined_realloc(p, s);
    __itt_heap_reallocate_end(my_reallocator, p, &np, s, 0);

    return(np);
}

// Make sure to call this init routine before any calls to
// user defined allocators.
void init_itt_calls()
{
    my_allocator = __itt_heap_function_create("my_malloc", "mydomain");
    my_reallocator = __itt_heap_function_create("my_realloc", "mydomain");
    my_freer = __itt_heap_function_create("my_free", "mydomain");
}

void test_size_of_held_memory(void *p)
{
    size_t s=0;

    // This will tell Intel Inspector that this memory access
    // is intentional, and should not be flagged.
    __itt_heap_internal_access_begin();

#ifdef TARGET_WINDOWS
    s = _msize(p);
#endif

    __itt_heap_internal_access_end();
}
// Now use my_alloc, my_free, my_realloc in place of the user defined
// functions.

Usage Example: Heap Growth

#include <ittnotify.h>

void ProcessTransaction(TransactionContext x)
{
   ...
   char* m = (char*) malloc(128); // Memory leak Inspector will report
   ...
   return;
}

// In this example, a leak report will be generated for each transaction in 
// the for loop.
void WaitForTransactions()
{
   ...
   for (;;)
   {
      __itt_heap_record_memory_growth_begin();
      TransactionContext x = WaitForTransaction(); // Transaction end-point
      ProcessTransaction(x);
      __itt_heap_record_memory_growth_end();
   }
}

Memory Allocation APIs for On-demand Memory Leak Detection and Memory Growth Detection

These APIs support on-demand memory leak detection and memory growth detection features in the GUI, and analogous memory leak reporting and growth detection capabilities in the CLI.

NOTE:

Mask values can be added or OR’ed together to reset both values with a single call.

Use This in C/C++ Code

Use This in Fortran Code

To Do This

void __itt_heap_reset_detection( 
    unsigned int mask); 
subroutine itt_heap_reset_detection(mask)
integer, intent(in), value:: mask
end subroutine itt_heap_reset_detection

Reset the starting point for on-demand leak detection and/or memory growth reporting.

For C/C++, the mask is:

  • __itt_heap_leaks to reset the leak detection starting point

  • __itt_heap_growth to reset the memory growth starting point.

For Fortran, the mask is:

  • itt_heap_leaks to reset the leak detection starting point

  • itt_heap_growth to reset the memory growth starting point.

If __itt_heap_record() is called without a prior call to __itt_heap_reset_detection(), the program’s start is used for the starting point.

void __itt_heap_record(unsigned int mask); 
subroutine itt_heap_record(mask)
integer, intent(in), value:: mask
end subroutine itt_heap_record

Record current data about memory leaks and/or memory growth for inclusion in the result.

For C/C++, the mask is:

  • __itt_heap_leaks to detect and record information about memory leaks

  • __itt_heap_growth to detect and record information about memory growth

For Fortran, the mask is:

  • itt_heap_leaks to detect and record information about memory leaks

  • itt_heap_growth to detect and record information about memory growth

If you are using the GUI for collection, this data is displayed immediately. If you are using the CLI for collection, the data is available after the result is finalized.

__itt_heap_record_memory_growth_begin() and __itt_heap_record_memory_growth_end() are aliases for __itt_heap_reset_detection(__itt_heap_growth) and __itt_heap_record(__itt_heap_growth), respectively.

Usage Example: On-demand Leak Reporting (C++)

In this example, the goal is to measure the memory leaked by the functions pipeline_stage1() , and pipeline_stage2() . We also want to examine the memory growth exhibited by the function pipeline_stage2() . Strictly speaking, the call to __itt_heap_record() after pipeline_stage1() finishes is not necessary, because any leaks that occur in pipeline stage 1 area also reported by the call to __itt_heap_record() that follows pipeline_stage2(). This example also demonstrates finer-grained leak and growth detection than that available via the CLI and GUI.

#include <ittnotify.h>

void ProcessPipeline()
{
    __itt_heap_reset_detection(__itt_heap_leaks);  // Start measuring memory leaks here
    pipeline_stage1();                             // Run pipeline stage 1
    __itt_heap_record(__itt_heap_leaks);           // Report leaks in stage 1

    // Now process stage 2 of the pipeline – measure growth and leaks there.
    // We reset the growth starting point for stage 2.  
    // There’s no need to reset the leak start point, since it follows stage 1.
    __itt_heap_reset_detection(__itt_heap_growth); // Reset growth starting point for stage 2 
    pipeline_stage2();
    __itt_heap_record(__itt_heap_leaks|__itt_heap_growth);// Report leaks and growth
}

Usage Example: On-demand Leak Detection (Fortran)

In this example, the goal is to measure the memory leaked by the functions pipeline_stage1() , and pipeline_stage2() . We also want to examine the memory growth exhibited by the function pipeline_stage2() . Strictly speaking, the call to itt_heap_record() after pipeline_stage1() finishes is not necessary, because any leaks that occur in pipeline stage 1 are also reported by the call toitt_heap_record() that follows pipeline_stage2(). This example also demonstrates finer-grained leak and growth detection than that available via the CLI and GUI.

#include <ittnotify.h>

subroutine process_pipeline()
    ! Start looking for leaks starting with pipeline stage 1.
    call itt_heap_reset_detection(itt_heap_leaks);  
    pipeline_stage1();                             
    call itt_heap_record(itt_heap_leaks); 

    ! Now process stage 2 of the pipeline – measure growth and leaks there.
    ! We reset the growth starting point for stage 2.  
    ! There’s no need to reset the leak start point, since it follows stage 1.
    call itt_heap_reset_baseline(itt_heap_growth);
    pipeline_stage2();
    call itt_heap_record(itt_heap_leaks+itt_heap_growth);
}