The Monitor is a Windows program that, although optional, greatly enhances the user friendliness of C Memory Scavenger. It communicates, in both directions, with the CMemScav library. Here follows a detailed description of this program. Note that the Monitor always runs on Windows, even though the application to be monitored may run on Solaris.
The program's GUI (Graphical User Interface) is divided into three major frames, a graph, a tool bar and a status bar. Most of the buttons appear both in the tool bar and in one of the frames. There are no differences whatsoever between a button in the tool bar and its corresponding button in a frame. Do they look the same, they are the same. The frames "Connection with Application", "Analyze - Check Outstanding Memory Allocations at the Application", and "Synthesize - Generate C Memory Scavenger Code to be Copied into the Application to Free Memory" divide in a natural way the program into three major areas - Connection / disconnection, the collection of data about present allocations, and the creation of CMemScav calls to be inserted into the application. The "Analyze..." frame is further divided into two smaller frames, "Summary..." and "Detailed List...". The graph does logically belong to the frame "Summary...", but to keep the size down of the already big GUI, it is placed below "Connection...". Finally, status of what is going on or have already been done, is displayed in the status bar at the bottom.
The frame "Connection with the Application"
Before connecting to an application, it should be up and running and CMemScavInit
should have been called with on == TRUE
and a non-negative port number, see The CMemScav Library
. The IP address to the computer running the application should be entered (where 127.0.0.1
could be used for the local host, i.e., the same computer as where the Montor
is running) as well as the port number chosen in the CMemScavInit
call. Connect to the application using the "ringing-phone" button and disconnect with the "non-ringing-phone" button. When connected, it is possible to halt the application, and then to release it again. To be precise, the application is not exactly halted, but as soon as any thread tries to allocate or deallocate any memory, it will have to wait for a locked mutex, until the release button is used and the mutex is unlocked. Finally, there is a check box "Autostart of cont. summary", which is explained later.
The frame "Analyze - Check Outstanding Memory Allocations at the Application"
This frame embraces two smaller frames described below, as well as two text fields labeled "Data below was intiated from" and "Line". If data in one or both of the smaller frames are showed, these two text fields tell how this data was initiated. Either a button in one of these frames was used, and hence, the Monitor
initiated the collection of data shown, or a call to CMemScavShowStatus()
in the application triggered the collection. In the first case, the first text field shows the text "A call from this program" and the background color is white. In the second case, the background color of both text fields turn yellow, and the full path of the source file and corresponding line is shown, from where the CMemScav
call was done. The first case is showed at Simple Example
, while the second case is shown below.
The frame "Summary of Allocation"
The data here is the output from CMemScavShowStatus, which either can be initiated from this program, or from the application. It contains the number of calls to all memory allocation functions as well as how much memory still is allocated due to these calls. For example, if two calls to calloc are done, both of 1024B, and one of these is later freed, "Allocated (B)" then shows 1024. If a memory block is reallocated with one of realloc or HeapRealloc, these functions take over the full amount of "Allocated (B)" for this block.
The summary also shows "Internal Use", which is how much memory the CMemScav
library has allocated internally to store necessary data. Most of this data is the full paths of the file names shown in the first column in the frame "Detailed List...". Even if only file names without paths are shown in this column, the full paths are always stored internally. Therefore, in extreme examples, like Simple Example
, much more memory is used internally then was saved using this tool. However, this is not normally the case.
To initiate the summary from this program, click one time at the camera button (after a successful connection). Repeated clicks will show updated status. To automate "repeated clickings", use the video button instead. Now a call to the application is sent repeatedly to report back the present summary, and the result is displayed. Two different speeds are available for this "filming", from "Normal", which means twice per second, and "Slower", once per 10 seconds. This is chosen be (un)checking appropriate check box. However, to get faster than the "Normal" speed, hold down Ctrl+G instead of using the video button. To see also all old values, a history of all summary values are displayed in the graph above this frame. You may also choose whether the number of calls should count only those not yet released, or all calls to the memory allocation functions, by the last check box.
If the check box "Autostart of cont. summary" in the frame "Connection..." is checked when a connection is setup, the filmimg is automatically started as soon as the connection is established. However, as soon as the application does a call to either CMemScavShowStatus() or CMemScavShowList(), the filming is switched off.
The present summary can also be saved to file, using the floppy button in this frame.
The frame "Detailed List of Outstanding Memory Allocations"
The data here is the output from CMemScavShowList, which either can be initiated from this program, or from the application. It contains a full list of all outstanding memory allocations done by the application (during the time CMemScav has been enabled). To initiate the collection of the data, click one time at the list/table button. Note that this will automatically generate an update of the summary, so that these two frames always show data from the same time. For all allocations the following data is given:
File name of the file where the allocation was done (full path if the check box "Show Full Path" is checked).
Line number in the file above, where the allocation was done.
A text string that equals the name of the allocation function.
How much memory was allocated, in bytes.
The thread's id (the output from GetCurrentThreadId() for Windows, and from pthread_self() for Solaris).
A running index that is incremented by one for every memory allocation.
The allocated memory address (in hex format).
Some statistics is also shown:
Records: the number of entries in the table.
Files: the number of different source files.
Unique code lines: the number of different file/line entries.
Threads: the number of different thread ids.
The data in the table is originally sorted according to the Index (lowest on top), but by clicking at any column header, the data will be sorted by this column. Another click at the same column header will invert the sorting. The data in the table can be saved to file by using the floppy button in this frame. If no entries are selected, all entries will be saved, otherwise only the selected ones will be saved. The sort order will be lost next time the detailed list is filled with data.
There is also a possibility to free memory in the application directly from the Monitor. To be sure the info in the Monitor is up-to-date when the chosen pointers are freed, the following procedure must be followed:
1. Pause the application.
2. Get a detailed list of outstandling memory allocations.
3. Select the pointers that should be freed.
4. Free the selected pointers by clicking at the dustbin/trash can button. A window pops up, telling how much memory was freed. This is the situation shown in the Monitor above. Note that four pointers can be seen there to be selected, with 80197 = 3615 + 22804 + 20 + 53758 bytes in total.
5. Points 2-4 or points 3-4 may be repeated any number of times.
6. Release the application.
The frame "Synthesize - Generate C Memory Scavenger Code to be Copied into the Application to Free Memory"
The tools here are for creating calls to the library function CMemScavFreeThese()
(or to CMemScavFreeAll()
, which is a special case of the former), to be included in the application's source code. However, there is nothing that prevents you from creating such calls by yourself, and sometimes this is necessary because the Monitor
can only create calls within a certain amount of regularity. For detailed information about these calls, see The CMemScav Library
Input is the data in the frame "Detailed List...". If some entries are selected in this frame, only these comprise the input, otherwise all entries build up the input. In the frame "Synthesize...", there are a number of ways to affect the output, created by the "hand-holding-a-pencil-writing-on-a-notepad" button. The default values for these settings are:
"Regardless of file name" NOT checked
"Only the calling thread" NOT checked
"The exact line"
These default settings create as many calls to CMemScavFreeThese() as there are input entries. Each entry looks like:
CGarbcollFreeThese(File, n, n, 0, 0, 0, 0, 0);
Here, File is the full path to the file in column 1 from the input and n is the line number from column 2, for the respective entry. Hence, these calls will clear the showed memory allocations in the input, and will still do so even if the amount of memory will change in the future. This is so because the 4th and 5th arguments, int minSize and int maxSize, are both set to zero here. If the radio button "The exact size" had been checked instead of "All sizes", we would have gotten lines like
CGarbcollFreeThese(File, n, n, m, m, 0, 0, 0);
instead, where m is the exact amount of memory allocated in each entry. In this case, if the application would change the amount of allocated memory in some places, these ones would not be freed by the last example above. On the other side, these calls would not by mistake free other allocations that might have been done in the same file and at the same line but with different sizes. A third option is to use the radio button "Between min & max" below the radio button "The exact size". In this case, the "Min" and "Max" text boxes will be enabled and arbitrary values can be set.
To further complicate the situation, the same file and source line might at different times allocate the same amount of memory, perhaps by different threads. Only some of these might be of interest to free at a given situation. If the allocations to free are those that belong to the same thread that should do the freeing, check the "Only the calling thread" check box. Then the 6th argument will be GetCurrentThreadId() for Windows, and pthread_self() for Solaris. If another thread is of interest, you have to enter this thread's id by yourself. Save the thread's id in a variable in the application and use this as the 6th argument.
Another way, which may be used together with the thread id, is to use the Indexes. All calls to memory allocation functions get their unique index number, shown in the column labeled "Index" in the frame "Detailed List...". By a call to CMemScavGetIndex() you can get the present index value at any time. For example, call this function both before entering the function sloppyFunc(), which probably leak some memory, and after returning from sloppyFunc(), getting the values startIndex and stopIndex. Now calling:
CGarbcollFreeThese(NULL, 0, 0, 0, 0, threadId, startIndex, stopIndex);
at any time after returning from sloppyFunc(), where threadId may be a thread index, or zero, will clear off all possible memory allocations done after calling CMemScavGetIndex() the first time and before calling it the second time. If only one thread is running, or if the thead that called sloppyFunc() has the id threadId, the call above will clean up after sloppyFunc(), regardless of what memory leaks it caused. The only responsibility left to you, is that you guarantee that the application does not need any of the allocations sloppyFunc() left behind. If you cannot do so, it is probably too soon to clean up after sloppyFunc() at this place in the code. There are also some help in the Monitor to setup different Index values, but to be a powerful tool, these indexes should probably be taken from calls to CMemScavGetIndex().
Using the floppy button, the generated calls to the CMemScav functions can be save to disk. To help the inserting of these lines into the application, each line can be indented with a number of spaces or tabs, of your choice. This file will have Windows style line breaks if the application is running on Windows, and Unix style line breaks on Solaris.