Using Runtime Checking |
9 |
![]() |
Runtime checking (RTC) enables you to automatically detect runtime errors in an application during the development phase. RTC lets you detect runtime errors such as memory access errors and memory leak errors and monitor memory usage.
Note - Access checking is available only on SPARC systems.
The following topics are covered in this chapter:
Basic Concepts
Because RTC is an integral debugging feature, all debugging functions such as setting breakpoints and examining variables can be used with RTC, except the Collector.
For more detailed information on any aspect of RTC, see the online help.
A possible solution is to insert special files in the executable image to handle program text areas and data areas larger than 8 megabytes.
To turn on the desired checking mode, start dbx with the -C option:
% dbx -C program_name |
The -C flag forces early loading of the RTC library. When you start dbx without the -C option and then enable checking, the RTC library is loaded when you issue the next run command; that may cause reloading of the shared libraries needed by the program. Using the -C flag initially allows you to avoid reloading.
To turn on memory use and memory leak checking:
Note - You must turn on the type of checking you want before you run the program.
(dbx) check -memuse |
To turn on memory access checking only:
(dbx) check -access |
To turn on memory leak, memory use, and memory access checking:
(dbx) check -all |
(dbx) uncheck -all |
Run the program being tested, with or without breakpoints.
The function access_error() reads variable j before it is initialized. RTC reports this access error as a Read from uninitialized (rui).
Using Access Checking (SPARC only)
RTC checks whether your program accesses memory correctly by monitoring each read, write, and memory free operation.
The following example shows a typical access error:
Read from uninitialized (rui): Location of error: Basic.c, line 56, read_uninited_memory() |
Memory Access Errors
RTC detects the following memory access errors:
Using Memory Leak Checking
A memory leak is a dynamically allocated block of memory that has no pointers pointing to it anywhere in the data space of the program. Such blocks are orphaned memory. Because there are no pointers pointing to the blocks, programs cannot even reference them, much less free them. RTC finds and reports such blocks.
A leak can result from incorrect use of an API:
Memory leaks can be avoided by following a good programming practice of always freeing memory when it is no longer needed and paying close attention to library functions that return allocated memory. If you use such functions, remember to free up the memory appropriately.
Detecting Memory Leak Errors
RTC detects the following memory leak errors:
Note - RTC only finds leaks of malloc memory. If your program does not use malloc, RTC cannot find memory leaks.
Possible Leaks
There are two cases where RTC may report a "possible" leak. The first case is when no pointers were found pointing to the beginning of the block, but a pointer was found pointing to the interior of the block. This case is reported as an "Address in Block (aib)" error. If it was a stray pointer that happened to point into the block, this would be a real memory leak. However, some programs deliberately move the only pointer to an array back and forth as needed to access its entries. In this case it would not be a memory leak. Because RTC cannot distinguish these two cases, it reports them as possible leaks, allowing the user to make the determination.
Note - RTC leak checking requires use of the standard libc malloc/free/realloc functions or allocators based on those functions. For other allocators, see Using Fix & Continue With RTC on page 121.
Checking for Leaks
If memory leak checking is turned on, a scan for memory leaks is automatically performed just before the program being tested exits. Any detected leaks are reported. The program should not be killed with the kill command. Here is a typical memory leak error message:
Memory leak (mel): |
Clicking on the call stack location hypertext link takes you to that line of the source code in the editor window.
Understanding the Memory Leak Report
With leak checking turned on, you get an automatic leak report when the program exits. All possible leaks are reported--provided the program has not been killed using the kill command. By default, a non-verbose leak report is generated, which is controlled by the dbxenv variable rtc_mel_at_exit.
location |
Location where leaked block was allocated |
addr |
Address of leaked block |
size |
Size of leaked block |
stack |
Call stack at time of allocation, as constrained by check -frames |
The non-verbose report capsulizes the error information into a table, while the verbose report gives you a separate error message for each error. They both contain a hypertext link to the location of the error in the source code.
Following is a typical verbose leak report:
Generating a Leak Report
You can ask for a leak report at any time using the showleaks command, which reports new memory leaks since the last showleaks command. Combining Leaks
Because the number of individual leaks can be very large, RTC automatically combines leaks allocated at the same place into a single combined leak report. The decision to combine leaks, or report them individually, is controlled by the number-of-frames-to-match parameter specified by the -match m option on a check -leaks or the -m option of the showleaks command. If the call stack at the time of allocation for two or more leaks matches to m frames to the exact program counter level, these leaks are reported in a single combined leak report.
Fixing Memory Leaks
Once you have obtained a memory leak report, there are some general guidelines for fixing the memory leaks. The most important thing is to determine where the leak is. The leak report tells you the allocation trace of the leaked block, the place where the leaked block was allocated. You can then look at the execution flow of your program and see how the block was used. If it is obvious where the pointer was lost, the job is easy; otherwise you can use showleaks to narrow your leak window. showleaks by default gives you only the new leaks created since the last showleaks command. You can run showleaks repeatedly to narrow the window where the block was leaked.
Using Memory Use Checking
A memory leak is a dynamically allocated block of memory that has no pointers pointing to it anywhere in the data space of the program. Such blocks are orphaned memory. Because there are no pointers to the blocks, the program cannot even reference them, much less free them. RTC finds and reports such blocks.
The following is the corresponding verbose memory use report:
You can ask for a memory use report any time with the showmemuse command.
Suppressing Errors
RTC provides a powerful error suppression facility that allows great flexibility in limiting the number and types of errors reported. If an error occurs that you have suppressed, then no report is given, and the program continues as if no error had occurred.
suppress mel in foo |
Do not report memory leaks whose allocation occurs in function foo
Suppress reporting blocks in use allocated from libc.so.1
suppress biu in libc.so.1
suppress rui in a.out
suppress rua in main.cc
suppress duf at main.cc:10 |
Suppress duplicate free at line 10 of main.cc
suppress all in bar
Default Suppressions
To detect all errors RTC does not require the program be compiled using the -g option (symbolic). However, symbolic information is sometimes needed to guarantee the correctness of certain errors, mostly rui. For this reason certain errors, rui for a.out and rui, aib, and air for shared libraries, are suppressed by default if no symbolic information is available. This behavior can be changed by using the -d option of the suppress and unsuppress commands.
unsuppress -d rui |
Using Suppression to Manage Errors
For the initial run on a large program, the number of errors may be so large as to be overwhelming. In this case, it may be better to take a phased approach. This can be done using the suppress command to reduce the reported errors to a manageable number, fixing just those errors, and repeating the cycle; suppressing fewer and fewer errors with each iteration.
Using RTC on a Child Process
dbx supports Runtime Checking of a child process if RTC is enabled for the parent and the dbxenv variable follow_fork_mode is set to child. When a fork happens, dbx automatically performs RTC on the child. If the program does an exec(), the RTC settings of the program calling exec() are passed on to the program.
Using RTC on an Attached Process
RTC works with attached processes as well. However, to use RTC on an attached process, that process must be started with librtc.so preloaded. librtc.so resides in the lib directory of the product (../lib from the path of dbx; if the product is installed in /opt, it is /opt/SUNWspro/lib/librtc.so).
% setenv LD_PRELOAD path-to-librtc/librtc.so |
% setenv LD_PRELOAD... % start-your-application % unsetenv LD_PRELOAD |
Once you attach to the process, you can enable RTC.
Using Fix & Continue With RTC
You can use RTC along with Fix & Continue to rapidly isolate and fix programming errors. Fix & Continue provides a powerful combination that can save you a lot of debugging time. Here is an example:
Runtime Checking Application Programming Interface
Both leak detection and access checking require that the standard heap management routines in the shared library libc.so be used. This is so that RTC can keep track of all the allocations and deallocations in the program. Many applications write their own memory management routines either on top of malloc-free or from scratch. When you use your own allocators (referred to as private allocators), RTC cannot automatically track them, thus you do not learn of leak and memory access errors resulting from their improper use.
Using RTC in Batch Mode
bcheck(1) is a convenient batch interface to the RTC feature of dbx. It runs a program under dbx and by default places the RTC error output in the default file program.errs.
bcheck [-access | -all | -leaks | -memuse] [-o logfile] [-q] [-
s script] program [args]
-s script option before executing the program to read in the dbx commands contained in the file script. The script file typically contains commands like suppress and dbxenv to tailor the error output of bcheck.
bcheck hello |
Perform only access checking on mach with the argument 5:
bcheck -access mach 5 |
Perform memory use checking on cc quietly and exit with normal exit status:
bcheck -memuse -q cc -c prog.c |
The program does not stop when runtime errors are detected in batch mode. All error output is redirected to your error log file logfile. But the program stops when breakpoints are encountered or if the program is interrupted.
(dbx) dbxenv rtc_auto_continue on |
With these settings, the program does not stop when runtime errors are detected, and all error output is redirected to your error log file.
Troubleshooting Tips
After error checking has been enabled for a program and the program is run, one of the following errors may be detected:
librtc.so and dbx version mismatch; Error checking disabled |
patch area too far (8mb limitation); Access checking disabled |
RTC was unable to find patch space close enough to a load object for access checking to be enabled. See rtc_patch_area later in this chapter.
RTC's 8 Megabyte Limit
When access checking, dbx replaces each load and store instruction with a branch instruction that branches to a patch area. This branch instruction has an 8 megabytes range. This means that if the debugged program has used up all the address space within 8 megabytes of the particular load/store instruction being replaced, there is no place to put the patch area. Working Around the 8 Megabyte Limit
dbx provides some possible workarounds to users who have run into this limit. These workarounds require the use of a utility called rtc_patch_area which is included with WorkShop. Case 1
In Case 1, you can use rtc_patch_area to make one or more object files to serve as patch areas and link them into the a.out.
rtc_patch_area -o patch.o -size 6490432
After you have seen a message like the following::
rtc_patch_area -o patch.o -size 563332
After you have seen a message like the following:
The -size flag is optional; the default value is 8000000.
The object file (or shared library) created contains one RTC patch area of the specified size or 8000000 if size is not supplied.
The name of the resulting object file (or shared library) is written to the standard output. Either the -o or -so option must be used.
Specify the name of the shared library to be created. This name is then written to the standard output:
-so sharedlibname |
Specify the name of the object file to be created. This name is then written to the standard output:
-o objectname |
Create a patch area of size bytes (default and reasonable maximum is 8000000):
-size size |
Use compiler instead of cc to build the object file:
-cc compiler |
Examples
Generate a standard 8 megabytes patch area object file:
rtc_patch_area -o patch.o |
Generate an object file containing a 100,000 byte patch:
rtc_patch_area -size 100000 -o patch.o |
Generate a 1 megabyte patch area shared library:
rtc_patch_area -so rtc1M.so -size 1000000 |
Command Reference
check|uncheck
All forms of the check and uncheck commands are described below.
check
check -access |
check -leaks [-frames n] [-match m] |
The default value of n is 8 or the value of m (whichever is larger), with a maximum value of 16. The default value of m is 2.
check -memuse [-frames n] [-match m] |
To turn on memory use checking:
The default value of n is 8 or the value of m (whichever is larger), with a maximum value of 16. The default value of m is 2.
check -all [-frames n] [-match m] check -access ; check -memuse [-frames n] [-match m] |
uncheck -access |
uncheck -leaks |
To turn off memory use checking (leak checking is also turned off):
uncheck -memuse |
The following two commands are equivalent:
uncheck -all
uncheck -access; uncheck -memuse
uncheck [funcs] [files] [loadobjects]
suppress all in funcs files loadobjects
check function* file* loadobject* |
This command is equivalent to:
suppress all unsuppress all in function* file* loadobject* |
check main check foo check f.c suppress all unsuppress all in main unsuppress all in foo unsuppress all in f.c |
Notice that the suppress all command is only applied once, leaving checking turned on for main, foo, and f.c.
uncheck function* file* loadobject suppress all in function* file* loadobject* |
showblock
When memory use checking or memory leak checking is turned on, showblock shows the details about the heap block at address addr. The details include the location of the block's allocation and its size.
showblock -a addr
showleaks
To report new memory leaks since the last showleaks command:
showleaks [-a] [-m m] [-n num] [-v]
showmemuse
This command generates a report showing all blocks of memory in use:
showmemuse [-a] [-m m] [-n num] [-v] |
When memory use checking is on, at program exit an implicit showmemuse -a -n 20 is performed. You can get a verbose output at the program exit time by using the rtc_biu_at_exit dbxenv variable.
suppress|unsuppress
Some or all files in a loadobject may not be compiled with the -g switch. This implies that there is no debugging information available for functions that belong in these files. RTC uses some default suppression in these cases.
{suppress | unsuppress} -d |
To change the defaults for one loadobject:
{suppress | unsuppress} -d [error type] [in loadobject] |
To change the defaults for all loadobjects:
{suppress | unsuppress} -d [error type] |
To reset these defaults to the original settings:
suppress -reset |
{suppress | unsuppress} -last |
To display the history of the suppress commands not including the -d and
-reset commands:
{suppress | unsuppress} |
Turn error reports on or off for the specified error types for the specified location.
{suppress | unsuppress} [error type... [location_specifier]] |
To remove the suppress or unsuppress events as given by the id(s).
suppress -r id... |
To remove all the suppress and unsuppress events as given by suppress.
suppress -r [0 | all | -all] |
Error Type Location Specifier
The table shows the error type location specifier.
in loadobject |
All functions in the designated program or library |
in file |
All functions in file |
in function |
Named function |
at line specifier |
At source line |
addr address |
At hex address |
To see a list of the loadobjects, type loadobjects at the dbx prompt. If the line specifier is blank, the command applies globally to the program. Only one line specifier may be given per command.
Problem: Attempt to free memory that has never been allocated.
Possible causes: Passing a non-heap data pointer to free() or realloc().
Problem: Attempt to read from uninitialized memory.
Possible causes: Reading local or heap data that has not been initialized.
RTC Errors
Address in Block (aib)
Address in Register (air)
Bad Free (baf)
char a[4];
char *b = &a[0];
free(b); /* Bad free (baf) */
Duplicate Free (duf)
Misaligned Free (maf)
Misaligned Read (mar)
Misaligned Write (maw)
Memory Leak (mel)
Out of Memory (oom)
Read from Unallocated Memory (rua)
Read from Uninitialized Memory (rui)
foo()
{ int i, j;
j = i; /* Read from uninitialized memory (rui)
*/
}
Write to Read-Only Memory (wro)
Write to Unallocated Memory (wua)
dbxenv Variables
The following dbxenv variables control the operation of RTC. If you want to permanently change any of these variables from their default values, place the dbxenv commands in the $HOME/.dbxrc file.Then, your preferred values are used whenever you use RTC. dbxenv rtc_auto_continue {on | off}
rtc_auto_continue on causes RTC not to stop upon finding an error, but to continue running.. It also causes all errors to be redirected to the rtc_error_log_file_name.The default is: off. dbxenv rtc_auto_suppress {on | off}
rtc_auto_suppress on causes a particular access error at a particular location to be reported only the first time it is encountered and suppressed thereafter. This is useful, for example, for preventing multiple copies of the same error report when an error occurs in a loop that is executed many times. The default is: on. dbxenv rtc_biu_at_exit {on | off | verbose}
This variable is used when memory use checking is on. If the value of the variable is on, a non-verbose memory use (blocks in use) report is produced at program exit. The default is: on. dbxenv rtc_error_log_file_name filename
rtc_error_log_file_name redirects RTC error messages to the designated file instead of to the standard output of dbx. The default is: /tmp/dbx.errlog.uniqueid. dbxenv rtc_error_limit n
n is the maximum number of errors that RTC reports. The error limit is used separately for access errors and leak errors. For example, if the error limit is set to 5, then a maximum of 5 access errors and 5 memory leaks are shown in both the leak report at the end of the run and for each showleaks command you issue. The default is: 1000. dbxenv rtc_mel_at_exit {on | off | verbose}
This variable is used when leak checking is on. If the value of the variable is on, a non-verbose memory leak report is produced at program exit. If the value is verbose, a verbose memory leak report is produced at program exit. The value off causes no output. This variable has no effect on the showleaks command. The default is: on.