TOC PREV NEXT

2

Using Rational Purify

Rational Purify: What it does

Rational® Purify® is the most comprehensive run-time error detection tool available. It checks all the code in your program, including any application, system, and third-party libraries. Purify works with complex software applications, including multi-threaded and multi-process applications.

Purify checks every memory access operation, pinpointing where errors occur and providing detailed diagnostic information to help you analyze why the errors occur. Among the many errors that Purify helps you locate and understand are:

With Purify, you can develop clean code from the start, rather than spending valuable time debugging problem code later.

This chapter introduces the basic concepts involved in using Purify. For complete information, see the Purify online help system.

Finding errors in Hello World

This chapter shows you how to use Purify to find memory errors in an example Hello World program. If you run the example yourself, you should expect minor platform-related differences in program output from what is shown here.

Before you begin:

  1. Create a new working directory. Go to the new directory and copy the hello_world.c program and related files from the <purifyhome>/example directory. For example:
    % mkdir /usr/home/chris/pwork
    % cd /usr/home/chris/pwork
    % cp <purifyhome>/example/hello*
    
  2. Examine the code in hello_world.c. The version of hello_world.c provided with Purify is slightly different from the traditional version.
     1  /*
     2   * Copyright (c) 1992-1997 Rational Software Corp.
         ...
     9   * This is a test program used in Purifying Hello World
    10   */
    11
    12  #include <stdio.h>
    13  #include <malloc.h>
    14
    15  static char *helloWorld = "Hello, World";
    16
    17  main()
    18  {
    19     char *mystr = malloc(strlen(helloWorld));
    20
    21     strncpy(mystr, helloWorld, 12);
    22     printf("%s\n", mystr);
    23  }
    
    At first glance there are no obvious errors, yet the program actually contains a memory access error and leaked memory that Purify will help you to identify.

Instrumenting a program

  1. Compile and link the Hello World program, then run the program to verify that it produces the expected output:

  2. Instrument the program by adding purify to the front of the compile/link command line. To get the maximum amount of detail in Purify messages, use the -g option:
    % purify cc -g hello_world.c
    
    Note: On IRIX, you can add purify in front of the compile/link command line, or you can Purify the executable:
    % purify a.out
    
    On IRIX, Purify caches Dynamic Shared Objects (DSOs), not object files. Ignore all references to linkers and link-line options in this book. These do not apply to Purify on IRIX.

Compiling and linking in separate stages

If you compile and link your program in separate stages, specify purify only on the link line. For example:

On the compile line, use:

% cc -c -g hello_world.c
On the link line, use:
% purify cc -g hello_world.o

Running the instrumented program

Run the instrumented Hello World program:
% a.out
On IRIX, if you use purify on the executable instead of on the compile/link line, type:
% a.out.pure
This prints "Hello, World" in the current window and displays the Purify Viewer.


Notice that the instrumented Hello World program starts, runs, and exits normally. Purify does not stop the program when it finds an error.

Seeing all your errors at a glance

The Purify Viewer displays the results of the run of the instrumented Hello World program. You can expand each message to see additional details.

Note: The Viewer displays messages for a single executable only. It is specific to the name of the executable, the directory containing the executable, and the user ID.

Finding and correcting errors

Purify reports an array bounds read (ABR) memory access error in the Hello World program. You can expand the ABR message to see the exact location of the error.

Note: To make debugging easier, Purify reports line numbers, source filenames, and local variable names whenever possible if you use the -g compiler option when you instrument the program. If you do not use the -g option, Purify reports only function names and object filenames.

On IRIX, system libraries retain their source file and line number information; therefore, the can appear next to a system library function whose source file is not available. When you click the for such a line, Purify prompts you for the location of the source file. Enter the location of the file if you know it, and then click OK to expand the line.

Understanding the cause of the error

To understand the cause of the ABR error, look at the code in hello_world.c again.

On line 22, the program requests printf to display mystr, which is initialized by strncpy on line 21 for the 12 characters in "Hello, World". However, _doprnt is accessing one byte more than it should. It is looking for a NULL byte to terminate the string. The extra byte for the string's NULL terminating character has not been allocated and initialized.

For more information, see How Purify finds memory-access errors.

Correcting the ABR error

To correct this ABR error:
  1. Click the Edit tool to open an editor.

    Note: By default, Purify displays seven lines of the source code file in the Viewer. You can change the number of lines of source code displayed by setting an X resource.

  2. Change lines 19 and 21 as follows:
    19  char *mystr = malloc(strlen(helloWorld)+1) ;
    20
    21  strncpy(mystr, helloWorld,13) ;
    

Finding leaked memory

When a program exits, Purify searches for memory leaks and reports all memory blocks that were allocated but for which no pointers exist.

Note: When you run longer-running instrumented programs, you can click the New Leaks tool to generate a new leaks summary while the program is running.

  1. Expand the memory-leaked summary for Hello World.

    The memory-leaked summary shows the number of leaked bytes as a percentage of the total heap size. If there is more than one memory leak, Purify sorts them by the number of leaked bytes, displaying the largest leaks first.

  2. Expand the MLK message.

Correcting the MLK error

It is not immediately obvious why this memory was leaked. If you look closer, however, you can see that this program does not have an exit statement at the end. Because of this omission, the main function returns rather than calls exit, thereby making mystr --the only reference to the allocated memory--go out of scope.

If main called exit at the end, mystr would remain in scope at program termination, retaining a valid pointer to the start of the allocated memory block. Purify would then have reported it as memory in use rather than memory leaked. Alternatively, main could free mystr before returning, deallocating the memory so it is no longer in use or leaked.

To correct this MLK error:

  1. Click the Edit tool to open an editor.
  2. Add a call to exit(0) at the end of the Hello World program.

Looking at the heap analysis

Purify distinguishes between three memory states, reporting both the number of blocks in each state and the sum of their sizes:

The exit status message provides information about:

Comparing program runs

To verify that you have corrected the ABR and MLK errors, recompile the program with purify, and run it again.

Purify displays the results of the new run in the same Viewer as the previous run so it's easy to compare them. In this simple Hello World program, you can quickly see that the new run no longer contains the ABR and MLK errors.

Congratulations! You have successfully Purify'd the Hello World program.

Suppressing Purify messages

A large program can generate hundreds of error messages. To quickly focus on the most critical ones, you can suppress the less critical messages based on their type and source. For example, you might want to hide all informational messages, or hide all messages that originate in a specific file.

You can suppress messages in the Viewer either during or after a run of your program. To suppress a message in the Viewer:

  1. Select the message you want to suppress.
  2. Select Options > Suppressions.

Purify displays the Suppressions dialog, containing information about the selected message.

You can also specify suppressions directly in a .purify file. Suppressions created in the Viewer take precedence over suppressions in .purify files; however, they apply only to the current Purify session. Unless you click Make permanent, they do not remain when you restart the Viewer.

Saving Purify output to a view file

A view file is a binary representation of all messages generated in a Purify run that you can browse with the Viewer or use to generate reports independent of a Purify run. You can save a run to a view file to compare the results of one run with the results of subsequent runs, or to share the file with other developers.

Saving a run to a view file from the Viewer

To save a program run to a view file from the Viewer:
  1. Wait until the program finishes running, then click the run to select it.
  2. Select File > Save As.
  3. Type a filename, using the .pv extension to identify the run as a Purify view file.

Opening a view file

To open a view file from the Viewer:
  1. Select File > Open.
  2. Select the view file you want to open.
Purify displays the run from the view file in the Viewer. You can work with the run just as you would if you had run the program from the Viewer.

You can also use the -view option to open a view file. For example:

% purify -view <filename>.pv
This opens the <filename>.pv view file in a new Viewer.

Using your debugger with Purify

You can run an instrumented program directly under your debugger so that when Purify finds an error, you can investigate it immediately.

Alternatively, you can enable Purify's just-in-time (JIT) debugging feature to have Purify start your debugger only when it encounters an error - and you can specify which types of errors trigger the debugger. JIT debugging is useful for errors that appear only once in a while. When you enable JIT debugging, Purify suspends execution of your program just before the error occurs, making it easier to analyze the error.

Using Purify with PureCoverage

Purify is designed to work closely with PureCoverage, Rational Software's runtime test coverage tool. PureCoverage identifies the parts of your program that have not yet been tested so you can tell whether you're exercising your program sufficiently for Purify to find all the memory errors in your code.

To use Purify with PureCoverage, add both product names to the front of your link line. Include all options with the program to which they refer. For example:

% purify <purifyoptions> purecov <purecovoptions> \
    cc -g hello_world.c -o hello_world

To start PureCoverage from the Purify Viewer, click the PureCoverage icon in the toolbar.

For more information, see Using Rational PureCoverage.

Purify API functions

You can call Purify's API functions from your source code or from your debugger to gain more control over Purify's error checking. By calling these functions from your debugger, you get additional control without modifying your source code. You can use Purify's API functions to check memory state and to search for memory and file-descriptor leaks.

For example, by default Purify reports memory leaks only when you exit your program. However, if you call the API function purify_new_leaks at key points throughout your program, Purify reports the memory leaks that have occurred since the last time the function was called. This periodic checking enables you to locate and track memory leaks more effectively.

To use Purify API functions, include <purifyhome>/purify.h in your code and link with <purifyhome>/purify_stubs.a.

 
Commonly used functions
Description
int  purify_describe (char *addr)
Prints specific details about memory
int  purify_is_running (void)
Returns "TRUE" if the program is instrumented
int  purify_new_inuse (void)
Prints a message on all memory newly in use
int  purify_new_leaks (void)
Prints a message on all new leaks
int  purify_new_fds_inuse (void)
Lists the new open file descriptors
int  purify_printf (char *format, ...)
Prints formatted text to the Viewer or log-file
int  purify_watch (char *addr)
Watches for memory write, malloc, free
int  purify_watch_n (char *addr, int size, char *type)
Watches memory: type = "r", "w", "rw"
int  purify_watch_info (void)
Lists active watchpoints
int  purify_watch_remove (int watchno)
Removes a specified watchpoint
int  purify_what_colors (char *addr, int size)
Prints the color coding of memory

Build-time options

Specify build-time options on the link line when you instrument a program with Purify. For example:
% purify -cache-dir=$HOME/cache -always-use-cache-dir cc ...
 
Commonly used build-time options
Default
-always-use-cache-dir

Forces all instrumented object files to be written to the global cache directory

no
-cache-dir

Specifies the global directory where Purify caches instrumented object files

<purifyhome>/cache
-collector

Specifies the collect program to handle static constructors (for use with gcc, g++)

none
-ignore-runtime-environment

Prevents the runtime Purify environment from overriding the option values used in building the program

no
-linker

Sets the alternative linker to build the executables instead of the system default

system-dependent
-print-home-dir

Prints the name of the directory where Purify is installed, then exits

 

Conversion characters for filenames

Use these conversion characters when specifying filenames for options such as -log-file and -view-file.
 
Character
Converts to
%V
Full pathname of program with "/" replaced by "_"
%v
Program name
%p
Process id (pid)
qualified filenames (./%v.pv)
Absolute or relative to current working directory
unqualified filenames (no "/")
Directory containing the program

Runtime options

Specify runtime options on the link line or by using the PURIFYOPTIONS environment variable. For example:
% setenv PURIFYOPTIONS "-log-file=mylog.%v.%p 'printenv PURIFYOPTIONS'"
 
Commonly used runtime options
Default
-auto-mount-prefix

Removes the prefix used by file system auto-mounters

/tmp_mnt
-chain-length

Sets the maximum number of stack frames to print in a report

6
-fds-in-use-at-exit

Specifies that the file descriptor in use message be displayed at program exit

yes
-follow-child-processes

Controls whether Purify monitors child processes in an instrumented program

no
-jit-debug

Enables just-in-time debugging

none
-leaks-at-exit

Reports all leaked memory at program exit

yes
-log-file1

Writes Purify output to a log file instead of the Viewer window

stderr
-messages

Controls display of repeated messages: "first", "all", or in a "batch" at program exit

first
-program-name

Specifies the full pathname of the instrumented program if argv[0] contains an undesirable or incorrect value

argv[0]
-show-directory

Shows the directory path for each file in the call chain, if the information is available

no
-show-pc

Shows the full pc value in each frame of the call chain

no
-show-pc-offset

Appends a pc-offset to each function name in the call chain

no
-view-file1

Saves Purify output to a view file (.pv) instead of the Viewer.

none
-user-path

Specifies a list of directories in which to search for programs and source code

none
-windows

Redirects Purify output to stderr instead of the Viewer if -windows=no

none
1  Can use the conversion characters listed in Conversion characters for filenames.

Purify messages

Purify reports the following messages:
 
Message
Description
Severity*
Message
Description
Severity*
ABR
Array Bounds Read
W
NPR
Null Pointer Read
F
ABW
Array Bounds Write
C
NPW
Null Pointer Write
F
BRK
Misuse of Brk or Sbrk
C
PAR
Bad Parameter
W
BSR
Beyond Stack Read
W
PLK
Potential Leak
W
BSW
Beyond Stack Write
W
PMR
Partial UMR
W
COR
Core Dump Imminent
F
SBR
Stack Array Bounds Read
W
FIU
File Descriptors In Use
I
SBW
Stack Array Bounds Write
C
FMM
Freeing Mismatched Memory
C
SIG
Signal
I
FMR
Free Memory Read
W
SOF
Stack Overflow
W
FMW
Free Memory Write
C
UMC
Uninitialized Memory Copy
W
FNH
Freeing Non Heap Memory
C
UMR
Uninitialized Memory Read
W
FUM
Freeing Unallocated Memory
C
WPF
Watchpoint Free
I
IPR
Invalid Pointer Read
F
WPM
Watchpoint Malloc
I
IPW
Invalid Pointer Write
F
WPN
Watchpoint Entry
I
MAF
Malloc Failure
I
WPR
Watchpoint Read
I
MIU
Memory In-Use
I
WPW
Watchpoint Write
I
MLK
Memory Leak
W
WPX
Watchpoint Exit
I
MRE
Malloc Reentrancy Error
C
ZPR
Zero Page Read
F
MSE
Memory Segment Error
W
ZPW
Zero Page Write
F
Message severity: F=Fatal, C=Corrupting, W=Warning, I=Informational

How Purify finds memory-access errors

Purify monitors every memory operation in your program, determining whether it is legal. It keeps track of memory that is not allocated to your program, memory that is allocated but uninitialized, memory that is both allocated and initialized, and memory that has been freed after use but is still initialized.

Purify maintains a table to track the status of each byte of memory used by your program. The table contains two bits that represent each byte of memory. The first bit records whether the corresponding byte has been allocated. The second bit records whether the memory has been initialized. Purify uses these two bits to describe four states of memory: red, yellow, green, and blue.

Purify checks each memory operation against the color state of the memory block to determine whether the operation is valid. If the program accesses memory illegally, Purify reports an error.

Since Purify keeps track of memory at the byte level, it catches all memory-access errors. For example, it reports an uninitialized memory read (UMR) if an int or long (4 bytes) is read from a location previously initialized by storing a short (2 bytes).

How Purify checks statically allocated memory

In addition to detecting access errors in dynamic memory, Purify detects references beyond the boundaries of data in global variables and static variables; that is, data allocated statically at link time as opposed to dynamically at run time.

Here is an example of data that is handled by the static checking feature:

int array[10];
main() {
  array[11] = 1;
}

In this example, Purify reports an array bounds write (ABW) error at the assignment to array[11] because it is 4 bytes beyond the end of the array.

Purify inserts red zones around each variable in your program's static-data area. If the program attempts to read from or write to one of these red zones, Purify reports an array bounds error (ABR or ABW).

Purify inserts red zones into the data section only if all data references are to known data variables. If Purify finds a data reference that is relative to the start of the data section as opposed to a known data variable, Purify is unable to determine which variable the reference involves. In this case, Purify inserts red zones at the beginning and end of the data section only, not between data variables.

Purify provides several command-line options and directives to aid in maximizing the benefits of static checking.

Copyright© 1992-2003 Rational Software Corporation. All rights reserved.


TOC PREV NEXT