The specifics of profiling Microsoft Visual C++ applications with the Allocation profiler are described in the following sections:
The Allocation profiler traces calls to functions that allocate and de-allocate memory blocks and objects. In general, applications can use two function groups to allocate and de-allocate memory: they can use system memory management calls, or they can call on the runtime memory manager, which is part of the runtime library of Visual C++. The runtime memory manager requests large blocks from the system, and then eventually releases them. After that, it works on its own memory-allocation calls from the application. This improves the speed and, most importantly, allows you to avoid system memory thrashing (frequent allocation and de-allocation of small blocks).
The Allocation profiler traces calls to runtime memory manager and system memory management functions. For information on profiling system memory management functions, see Tracing System Memory Management Functions. As for runtime memory management functions, the profiler traces the following:
|Due to certain specifics of Visual C++ 6.0 applications, AQTime does not trace the
The profiler can show the class names when creating and deleting C++ objects. This functionality is supported for 32-bit and 64-bit Visual C++ applications. The profiler traces the objects created dynamically (that is, the object created by the
new operator). Objects allocated on a stack are not traced (but they do not cause leaks as well).
Note that in order for a C++ object to be reported with its class name, the class must have a constructor written in code or generated by the C++ compiler (compilers typically generate constructors if a class is a descendant of another class)
Classes that do not have a constructor are reported under the C++ native memory class name (with a complete call stack for each leaked instance). See Allocation Profiler - Results of the Classes Data Category for more information.
In order to be able to profile a Visual C++ application with the Allocation profiler, you need to compile it with debug information. AQTime can track memory usage of Visual C++ applications compiled either in the Release or Debug configuration. However, applications compiled in the Release configuration have certain limitations. Namely:
If several classes have similar constructors, the linker can exclude the implementation of “redundant” constructors leaving only one of them. As a result, the calls to constructors of different classes will be interpreted as calls to a single constructor, thus the objects of different classes will be treated as the objects of the same class.
If a class has an empty constructor, the linker excludes the constructor’s implementation from compilation and the class is reported under the C++ native memory class name in the profiling results.
The Release configuration produces more static allocations of memory. When the application starts, it allocates a block of memory of a predefined size, uses it during the runtime and releases it upon exit. The issue is that the memory is freed after AQTime retrieves the profiling results, so these blocks are reported as leaked.
Therefore, we recommend that you compile your application in the Debug configuration. You can do this by specifying the Debug compilation mode or by performing the steps below:
#define Debug directive in your source code.
Select Project > Settings from the main menu in Visual C++. This will open the Project Settings dialog.
Move to the C/C++ page.
Select General from the Category list and then add
_Debug to the preprocessor definitions.
Select the Code Generation category. Choose Debug Single-Threaded, Debug Multithreaded or Debug Multithreaded DLL from the Use run-time library dropdown list.
Press OK to close the Project Settings dialog.
Recompile your application.
Note that when profiling Visual C++ applications compiled in the Debug configuration, the Allocation profiler can report memory leaks of a larger size than the size of memory blocks you allocate in the application’s code. This happens because actually Visual C++ applications compiled in Debug configuration allocate larger memory blocks than it is specified in the source code. For instance, if your code allocates 2000 bytes, the compiled application will allocate 2052 bytes. These extra bytes are used by special instructions, which the compiler generates to control memory block overwrites. If the Allocation profiler detects that some memory block is not released, it will report the actual block size, not the size specified in the source code.
You may need to perform the following preparations before profiling your Visual C++ application:
In order for AQTime to profile the above-mentioned routines in Visual C++ applications, you may need to add certain libraries to the Setup panel in addition to your modules. This depends on the compiler options that were enabled when you compiled your application.
To add a library to the Setup panel, press the Add Module button on the Setup toolbar, or select Add Module from the panel’s context menu. If the required library is not listed in the Modules pane, AQTime will suggest to add it when you start profiling.
To monitor the runtime memory manager, you should add the Microsoft Visual C++ Runtime Library (MSVCRT) to the Modules pane. Depending on whether the application was compiled in the Release or Debug configuration, either the MSVCRT.DLL or MSVCRTD.DLL (debugging version) file should be added.
By depending on the compiler options, your application can use dynamically linked MFC libraries or statically linked MFC libraries. AQTime can track memory manager functions regardless of which MFC version you use - dynamic or static. However, when AQTime tracks the call stack of memory management functions, it will track only those routines that are shown in the Modules pane. So, if you want to see a more detailed call stack, you need to add MFC libraries to the Setup panel.
Let’s continue with a simple example. Suppose, your application contains the
fooA function that calls MFC’s function
CDC::GetPen, which, in turn, calls the
malloc routine located in the MSVCRTD dynamic link library. The
malloc routine, in its turn, can call
debug_malloc or some other functions. The hierarchy of function calls looks like this:
fooA (your exe) -> CDC::GetPen (mfc71d.dll) -> malloc (msvcrtd.dll) -> debug_malloc (msvcrtd.dll)
If you do not add MFC71 and MSVCRTD libraries to the Setup panel, the call stack will hold only the
fooA function. If you add these libraries to the Setup panel, you will get a more detailed call stack.