Analyzing Program Performance With Sun WorkShop

Using tcov for Statement-Level Analysis

tcov gives line-by-line information on how a program executes. It produces a copy of the source file, annotated to show which lines are used and how often. It also summarizes information about basic blocks. tcov does not product any time-based data.

Using tcov involves three basic steps:

  1. Compiling the program to produce a tcov experiment

  2. Running the experiment

  3. Using tcov to create summaries of execution counts for each statement in the program

Compiling for tcov

To compile a program for code coverage, use the -xa option to the C compiler. Using a program named index.assist as an example, you compile for use with tcov with this command:

% cc -xa -o index.assist index.assist.c

You use the -a compiler option with the C++ or f77 compilers.

The C compiler generates an index.assist.d file, containing database entries for the basic blocks present in index.assist.c. When the program index.assist is run until completion, the compiler updates the index.assist.d file.


Note -

tcov works with both C and C++ programs, but tcov does not support files that contain #line or #file directives. tcov does not enable test coverage analysis of the code in the #include header files. Applications compiled with -xa (C), -a (other compilers), and +d (C++) run slower than normal. The +d option inhibits expansion of C++ inline functions, and updating the .d file for each execution takes considerable time.


The index.assist.d file is created in the directory specified by the environment variable TCOVDIR. If TCOVDIR is not set, index.assist.d is created in the current directory.

Having compiled index.assist.c, you could run index.assist:


% index.assist 
% ls *.d
index.assist.d

Now you could run tcov to produce a file containing the summaries of execution counts for each statement in the program. tcov uses the index.assist.d file to generate an index.assist.tcov file containing an annotated list of your code. The output shows the number of times each source statement is executed. At the end of the file, there is a short summary.


% tcov index.assist.c 
% ls *.tcov
index.assist.tcov

This small fragment of the C code from one of the modules of index.assist shows the insert_index_entry function, which is called so recursively.


			struct index_entry * 
11152		->	insert_index_entry(node, entry) 
			structindex_entry *node; 
			struct index_entry *entry; 
			{ 
				int result; 
				int level; 

				result = compare_entry(node, entry); 
				if (result == 0) {								/* exact match */ 
													/* Place the page entry for the duplicate */ 
												/* into the list of pages for this node */ 
59		->				insert_page_entry(node, entry->page_entry); 
						free(entry); 
						return(node); 
				} 

11093		->		if (result > 0)						/* node greater than new entry -- */ 
											/* move to lesser nodes */ 
3956		->				if (node->lesser != NULL) 
3626		->						insert_index_entry(node->lesser, entry); 
						else { 
330		->						node->lesser = entry; 
								return (node->lesser); 
						}
					else					/* node less than new entry -- */ 
										/* move to greater nodes */ 
7137		->				if (node->greater != NULL) 
6766		->						insert_index_entry(node->greater, entry); 
						else { 
371		->						node->greater = entry; 
								return (node->greater); 
						} 
			} 

The numbers to the side of the C code show how many times each statement was executed. The insert_index_entry function is called 11,152 times.

tcov places a summary like this at the end of the annotated program listing for index.assist.tcov:

   Top 10 Blocks
   
  Line  Count
  240  21563
  241  21563
  245  21563
  251  21563
  250  21400
  244  21299
  255  20612
  257  16805
  123  12021
  124  11962
   
 77 Basic blocks in this file
 55 Basic blocks executed
 71.43 Percent of the file executed
   
  439144 Total basic block executions
  5703.17 Average executions per basic block

A program compiled for code coverage analysis can be run multiple times (with potentially varying input); tcov can be used on the program after each run to compare behavior.

Creating tcov Profiled Shared Libraries

It is possible to create a tcov profiled shareable library and use it in place of one where binaries have already been linked. Include the -xa (C) or -a (other compilers) option when creating the shareable libraries. For example:

 %cc -G -xa -o foo.so.1 foo.o

This command includes a copy of the tcov profiling subroutines in the shareable libraries, so that clients of the library do not need to relink. If a client of the library is also linked for profiling, then the version of the tcov subroutines used by the client is used to profile the shareable library.

Locking Files

tcov uses a simple file-locking mechanism for updating the block coverage database in the .d files. It employs a single file, /tmp/tcov.lock, for this purpose. Consequently, only one executable compiled with -xa (C) or -a (other compilers) should be running on the system. If the execution of the program compiled with the -xa (or -a) option is manually terminated, then the /tmp/tcov.lock file must be deleted manually.

Files compiled with the -xa or -a option call the profiling tools subroutines automatically when a program is linked for tcov profiling. At program exit, these subroutines combine the information collected at runtime for file xyz.f with the existing profiling information stored in file xyz.d. To ensure this information is not corrupted by several people simultaneously running a profiled binary, a xyz.d.lock lock file is created for xyz.d for the duration of the update. If there are any errors in opening or reading xyz.d or its lock file, or if there are inconsistencies between the runtime information and the stored information, then the information stored in xyz.d is not changed.

An edit and recompile of xyz.d may change the number of counters in xyz.d. This is detected if an old profiled binary is run.

If too many people are running a profiled binary, the lock cannot be obtained. An error message similar to the following is displayed after a delay of several seconds:


tcov_exit: Failed to create lock file '/tmp_mnt/net/rbbb/export/home/src/newpattern/foo.d.lock' for coverage data file '/tmp_mnt/net/rbbb/export/home/src/newpattern/foo.d' after 5 tries. Is somebody else running this binary?

The stored information is not updated. This locking is safe across a network. Since locking is performed on a file-by-file basis, other files may be correctly updated.

The profiling subroutines attempt to deal with automounted file systems that have become inaccessible. They still fail if the file system containing a coverage data file is mounted with different names on different machines, or if the user running the profiled binary does not have permission to write to either the coverage data file or the directory containing it. Be sure all the directories are uniformly named and writable by anyone expected to run the binary.

Errors Reported by tcov Runtime

The following error messages may be reported by the tcov runtime routines:


tcov_exit: Could not open coverage data file 'coverage data file name' because 'system error message string'.

The user running the binary lacks permission to read or write to the coverage data file. The problem also occurs if the coverage data file has been deleted.


tcov_exit: Could not write coverage data file 'coverage data file name' because 'system error message string'.

The user running the binary lacks permission to write to the directory containing the coverage data file. The problem also occurs if the directory containing the coverage data file is not mounted on the machine where the binary is being run.


tcov_exit: Failed to create lock file 'lock file name' for coverage data file 'coverage data file name' after 5 tries. Is someone else running this executable?

Too many users are trying to update a coverage data file at the same time. The problem also occurs if a machine has crashed while a coverage data file is being updated, leaving behind a lock file. In the event of a crash, the longer of the two files should be used as the post-crash coverage data file. Manually remove the lock file.


tcov_exit: Stdio failure, probably no memory left.

No memory is available, and the standard I/O package will not work. You cannot update the coverage data file at this point.


tcov_exit: Coverage data file path name too long (length characters) 'coverage data file name'.

The lock file name contains six more characters than the coverage data file name; therefore, the derived lock file name may not be legal.


tcov_exit: Coverage data file 'coverage data file name' is too short. Is it out of date?

A library or binary with tcov profiling enabled is simultaneously being run, edited, and recompiled. The old binary expects a coverage data file of a certain size, but the editing often changes that. If the compiler creates a new coverage data file at the same time the old binary is trying to update the old coverage data file, the binary may see an apparently empty or corrupt coverage file.