Memory Analysis is a QNX tool that uses the debug version of the memory allocation library (librcheck) to track heap memory, validate pointer arguments to C library functions, and detect memory corruption. When the tool is active, it displays data received from the librcheck library about memory events and problems that occur in the application being analyzed.
You can enable the Memory Analysis tool by selecting Memory as the launch mode. Although this mode supports other integrated tools, Memory Analysis is selected by default. Aside from Memory mode, you can select any other mode except Coverage and enable this tool by opening the launch configuration and clicking the Memory Analysis radio button in the rightmost tab.
When Memory Analysis has been enabled in the launch configuration, the application uses the debug version of the allocation library, librcheck. This library version tracks the history of all dynamic memory blocks and generates data about where in the program each block was allocated or freed. It also contains its own implementations of memory- and string-related functions, such as memcmp() and strcpy(), so it can validate pointer arguments passed to them and report any invalid pointers.
Reacting to process memory activity
Whenever an application process allocates or frees memory, or calls any function implemented in librcheck, the library generates data describing the memory activity.
Sending data to the transport agent
The librcheck library doesn't interact directly with the IDE on the host. Like other runtime analysis components, librcheck must send its data to a transport agent. In this example, we show the qconn service, but you could use any service that talks to the IDE over an IP connection.
Logging data to an output file or device
The qconn agent outputs the memory data to either a local file or a streaming device, depending on the advanced settings.
Reading and presenting the data
Within the IDE, the Memory Analysis tool reads the memory data from the log file or streaming device, then visually presents the data in a new analysis session.
When you launch an application with Memory Analysis enabled, the IDE switches to the QNX Analysis perspective and opens the Memory Analysis editor, which displays charts illustrating the application's heap usage. In the Analysis Sessions view, the IDE creates a new session for storing the analysis results. Each open Memory Analysis session has a header containing the tool's open session icon (), the binary name, the session number, and the launch time.
Under the header, all processes in the program being analyzed are listed. You can expand each process entry to see the threads, the executable binary, any shared libraries, and all source files that run within that process. For source files compiled into the executable binary, they're shown only if the binary was built with debug information. For source files compiled into shared libraries, you must have a debug version of these libraries within the shared libraries path on the host to see them listed.
Each session also has a Logs entry that lists the filename (without the path) specified in the Target output file or device field. If you double-click this item, the IDE opens the log containing the analysis data gathered so far. This action is handy when you're working with QNX customer support and you need to look up something specific. When the session ends, you'll see the trace (.rmat) file stored in workspace_dir/.metadata/.plugins/com.qnx.tools.ide.common.sessions.core/sessions/session_number/. You can then import these analysis results to view them later.
Finally, you can reopen a closed session (which is indicated with a different icon, ) by double-clicking its Analysis Sessions entry. The IDE then redisplays the program components for this session in that same view and the heap usage charts in the editor pane.
You can change which details get displayed by clicking the dropdown button () in the upper right corner of the view and choosing Preferences. This action opens a popup window that lets you choose the columns to display and the order to list them, from left to right.
Error Message | Meaning |
---|---|
allocator inconsistency - Malloc chain is corrupted, pointers out of order | A buffer overflow occurred in the heap and it's now corrupted. |
allocator inconsistency - Malloc chain is corrupted, end before end pointer | A buffer overflow occurred in the heap and it's now corrupted. |
pointer does not point to heap area | The program attempted to free non-heap memory. |
possible overwrite - Malloc block header corrupted | A buffer overflow occurred in the heap and it's now corrupted. |
allocator inconsistency - Pointers between this segment and adjoining segments are invalid | A buffer overflow occurred in the heap and it's now corrupted. |
data has been written outside allocated memory block | The program attempted to write data to a region beyond the allocated memory. |
data in free'd memory block has been modified | The program attempted to write data to a region that was previously freed. |
data area is not in use (can't be freed or realloced) | A buffer overflow occurred in the heap and it's now corrupted. |
unable to get additional memory from the system | There is no more heap memory that can be allocated. |
pointer points to the heap but not to a user writable area | A buffer overflow occurred in the heap and it's now corrupted. |
allocator inconsistency - Malloc segment in free list is in-use | A buffer overflow occurred in the heap and it's now corrupted. |
malloc region doesn't have a valid CRC in header | A buffer overflow occurred in the heap and it's now corrupted. |
free'd pointer isn't at start of allocated memory block | The program attempted to deallocate a pointer that shifted from its original value returned by the allocator. |
memory leak of size n | A heap block of size n has been lost. |
You can change which details get displayed by clicking the dropdown button () in the upper right corner of the view and choosing Preferences. This action opens a popup window that lets you choose the columns to display and the order to list them, from left to right.
When you launch a Memory Analysis session, the IDE automatically opens an editor window that shows the analysis results for that session. These results are refreshed regularly as the program runs. If you double-click any component within a session, the IDE opens another window to display only the results for that component. You can open such windows for both active and finished sessions.
Windows showing program-level data display all five tabs. Windows displaying data for a program component display only the Allocations tab.
The Details chart uses a 2D bar chart format by default. You can change the format by right-clicking anywhere in the top part of the editor, choosing Chart types, then choosing one of these four options: BarChart (the default), BarChart_3D, Differentiator, and Differentiator_3D.
The radio buttons just above the Details chart determine which types of events are graphed. By default, requested allocations and deallocations are displayed, but you can also display actual allocations and actual deallocations to see how much the heap memory footprint grows or shrinks.
The Memory Allocation and Deallocation Events chart illustrates the byte sizes for memory events too, but for a larger section of the session data set. In this bottom chart, the byte size range is shown along the vertical axis and by default, the event ID range is shown along the horizontal axis.
Initially, the data set from the entire session is graphed. Because the bottom chart can get quite wide if there's a lot of data, you can divide the chart into different pages to display only part of the data set at a time. To do this, in the Page Size text field just below the chart, enter the number of events that you want to see at a given time. On the left of this text field is a spinner that lets you manually enter a page to display, or navigate between pages with the arrow buttons.
This tab displays memory statistics based on bins, which group together events based on the sizes of the memory blocks involved. The librcheck library maintains bin allocation counts, updating these counts as the program allocates or deallocates blocks within the corresponding size ranges.
In the Bins tab, these counts are displayed in the Details: Blocks per Bin chart in the top left area. The horizontal axis lists the bin size ranges while the vertical axis lists the blocks count. The size ranges are based on the Bins counters field under Memory Snapshots. If you leave this field blank, the bin size ranges are based on powers of 2, with a special bin for large allocations between 4 KB and 2 GB.
The Blocks per Bin chart in the bottom area illustrates the changes in blocks count, with timestamp values (in microseconds since the program start) shown along the horizontal axis and the blocks count shown along the vertical axis. A separate line is drawn for each bin, in a specific color.
The radio buttons just above the top chart control which types of blocks (used or free) are graphed. Also, you can change the type of this chart through its right-click menu, which has two options: BarChart (the default) or BarChart_3D. For the bottom chart, the radio buttons let you show and hide individual lines, and you can right-click to select LineChart_3D (the default) or LineChart as the chart type.
The Detail pane in the upper right corner lists the number of used and free blocks in each bin, for each timestamp within the selected region of the bottom chart. The information is displayed in a table, with expandable rowsets for each timestamp. When you expand a rowset and click the leftmost column in a specific row, the top chart is refreshed to show the blocks count for that timestamp.
For efficiency, the system allocator preallocates bands of small buffers for satisfying small allocation requests. This saves programs from a trip through the kernel's memory manager, thus improving performance. Any activity in these bands is shown on the Bands tab.
In the top left area, the Details: Blocks per Band chart displays the blocks count for all bands. The horizontal axis lists the band sizes while the vertical axis lists the blocks count. The band sizes are an allocator property that depends on the memory header size and the alignment for the CPU.
The radio buttons just above the top chart control which types of blocks (used or free) are graphed. Also, you can change the type of this chart through its right-click menu, which has two options: BarChart (the default) or BarChart_3D. For the bottom chart, the radio buttons let you show and hide individual lines, and you can right-click to select LineChart_3D (the default) or LineChart as the chart type.
The Detail pane in the upper right corner lists the number of used and free blocks in each band, for each timestamp within the selected region of the bottom chart. The information is displayed in a table, with expandable rowsets for each timestamp. When you expand a rowset and click the leftmost column in a specific row, the top chart is refreshed to show the blocks count for that timestamp.
The Bands toolbar contains the same buttons as the Bins toolbar.
The radio buttons just above the top chart control which types of blocks (used, free, or overhead) are graphed. Also, you can change the type of this chart through its right-click menu, which has three options: BarChart, BarChart_3D, or PieChart (the default). For the bottom chart, the radio buttons let you show and hide individual lines, and you can right-click to select LineChart_3D, LineChart (the default), or AreaChartStacked as the chart type.
The Detail pane in the upper right corner displays a table that lists the total, used, free, and overhead byte sizes of the heap, for each timestamp within the selected region of the bottom chart.
The Usage toolbar contains the same buttons as the Bins and Bands toolbars.
This last tab lets you adjust Memory Analysis settings only for active sessions. You can't change the Advanced Settings but you can change any field in the Memory Errors, Memory Tracing, and Memory Snapshots dropdowns. Any new settings take effect when you click the Apply button in the toolbar.
In the Tools tab, ensure that the Memory Analysis radio button is selected and the Create control thread box is unchecked (disabled) under Advanced Settings.
Running a separate data-collection thread can cause the application to deadlock when the debugger is also running. If you disable this thread, your program must call the librcheck API to perform leak checks and memory tracing at runtime. Note that you can't send signals to a target process to control librcheck when no control thread is running.
In the Memory Analysis controls, change any settings to customize what gets reported.
You can adjust how Memory Analysis measures heap usage, enable or disable specific memory checks, and turn tracing on or off and specify block size tracing limits.
When you're ready to debug and analyze your application, click the Debug button ().
The IDE launches the application and attaches the debugger, which stops execution at the startup location specified in the Debug tab. A new Memory Analysis session is shown in Analysis Sessions.
Tell the debugger to ignore the SIGSEGV signal, with this command: handle SIGSEGV nopass
Sometimes, the debugger tooling does unsafe operations and causes the OS to emit this particular signal. This directive tells gdb to not pass this signal onto the program.
The program continues execution until the next breakpoint. When execution stops again, you can view the stack trace and other useful information in the Debug perspective. You can also switch to the QNX Analysis perspective and view the Memory Analysis data gathered up till that point, in the editor pane.
If you export Memory Analysis results to a CSV file, the resulting data in the file depends on the event type selected in the Export Memory Analysis Data window.
The tables shown below list the fields exported to the CSV file, in the order in which they're written, for each event type reported by Memory Analysis. If you want to see field names in the column headers when viewing the data in a spreadsheet application (e.g., Excel), be sure to check Generate header row in the export controls window.
Name | Description |
---|---|
Session Name | Abbreviated session name, which is just the binary name without the session number or timestamp shown in the Analysis Sessions entry. |
Session Time | When the session was created. For an imported session, it's the time of the import, not the time of creation. |
Event ID | Unique ID for the memory event. |
Time Stamp | Timestamp of when the event occurred on the target machine. |
Process ID | PID of the process. |
Name | Description |
---|---|
Thread ID | TID of the thread. |
CPU | CPU number (for multicore machines). |
Alloc Kind | Memory operation type (e.g., malloc, free). |
Actual Size | Number of bytes in the allocated block. |
Requested Size | Number of bytes requested by the program. |
Deallocation | Whether the memory block was freed. |
Pointer | Pointer value associated with the event. |
Source Location | Location in the source code where the memory was allocated. |
Root Location | Location in the source code for the root of the stack trace; typically, this is main() or a thread entry function. |
Full Trace | Full stack trace for the allocation. |
Name | Description |
---|---|
Thread ID | TID of the thread. |
CPU | CPU number (for multicore machines). |
Message | Error message returned. |
Pointer | Pointer value associated with the event. |
Trap Function | Function where the error was caught. |
Alloc Kind | Type of allocation for the argument (pointer) being validated. |
Severity | Error severity. |
Memory State | Whether the pointer memory is used or free. |
Source Location | Location in the source code where the memory was allocated. |
Root Location | Location in the source code for the root of the stack trace; typically, this is main() or a thread entry function. |
Full Trace | Full stack trace for the error. |
Full Alloc Trace | Full allocation stack trace for the pointer. |
Name | Description |
---|---|
Size | Maximum size, in bytes, for the bin to which the memory belongs. The bin size ranges are based on the Bins counters field under Memory Snapshots. The default size ranges are based on powers of two, with a special bin for large allocations between 4 KB and 2 GB. |
Allocations | Number of allocated blocks in this bin. |
Deallocations | Number of freed blocks in this bin. |
Name | Description |
---|---|
Size | Size, in bytes, of the preallocated band of memory used. |
Total Blocks | Number of total blocks in this band. |
Free Blocks | Number of free blocks in this band. |