Chapter Contents |
Previous |
Next |
opcmd |
The coverage feature provides information about which lines of source code written in C (C source code) were executed during a given compilation. The coverage feature has four parts:
The compiler provides the first part of the coverage feature by generating additional inline code that flags each line of code when it is executed. The flag is located in a data element that is also created by the compiler and added to the generated object.
COOL implements the second part of the line coverage feature by producing a global list that addresses all the compiler-generated coverage data elements encountered in its input.
The third part of the coverage feature is the application upon which coverage analysis is to be performed.
The fourth part of the coverage feature is a C routine that you provide,
named _ _cvgtrm
, that extracts and processes
the coverage data elements at the end of program execution.
The Compiler Part of the Coverage Feature |
When the coverage
option is specified,
the compiler activates the coverage feature by creating an internal data element
that is used to track which lines of C source code are executed. You must
also specify the compiler's extended name option, extname
,
for COOL to correctly process the data element created by the compiler.
Note: The object created by the compiler when the coverage option is specified
will not be reentrant, regardless of whether you specify the rent
option.
The following table describes the nine components in the data element that are created by the compiler.
Field Name | Type | Boundary | Description |
---|---|---|---|
Reserved | int |
Fullword | |
FELNO |
int |
Fullword | Line number of first line of executable C source code. |
LELNO |
int |
Fullword | Line number of last line of executable C source code. |
Reserved | int |
Fullword | |
SOURCE_FN_SIZE |
int |
Fullword | Number of characters in the SOURCE_FN . |
Reserved | int |
Fullword | |
EXE_INDICATOR |
char[?] |
Fullword | Variable Length; each element represents a C souce code line.
Element 0 is FELNO, and the last character is LELNO. |
SOURCE_FN |
char[?] |
Fullword | A string that contains the input C source filename. It starts on the
first fullword boundary that follows the EXE_INDICATOR . |
SNAME |
char[8] |
Char | Section name of the compilation unit. |
SOURCE_COMPDATE |
char[17+1] |
Char | A string containing the creation date of the object. |
SOURCE_SNAME |
char[7+1] |
Char | A string containing the section name of the compilation unit. The maximum length is 7 bytes. |
The header file cvg.h
defines structure
type USAGE_DATA
which maps the line coverage
table. Because fields SOURCE_FN
, SOURCE_COMPDATE
and SOURCE_SNAME
are at variable offsets from the start of the structure, they are accessed
through macros rather than as structure members. For instance, if ptr
is the address of the line coverage table, ptr->FELNO
addresses the
FELNO
field, but SOURCE_SNAME(ptr)
is used to reference
the SOURCE_SNAME
data.
When the coverage option is specified, the compiler searches for the first C source code line (or any portion of a C source code line) in the compilation unit that generates executable code and flags it as the first line of C source code. All previous lines are ignored. All subsequent lines (including white space and comment lines) are flagged as lines that generate executable code or lines that do not generate executable code.
The EXE_INDICATOR
is an array of characters.
Each element of the array represents a C source code line. The first element
of the array (FELNO
) represents the first C
source code line that has been flagged as a line that generates executable
code within the compilation unit. Each subsequent element represents a C
source code line, regardless of whether it has been flagged as a line that
generates executable code or not. The last element (LELNO
) represents the last C source code line that has been flagged
as a line that generates executable code.
At compile time, elements of the array are initialized to 0x01 if the line has been flagged as a line that generates executable code and 0x02 if not. At run-time, the 0x01 elements are changed to 0x00 when the corresponding line is executed.
The C source filename is provided in SOURCE_FN
,
which follows the EXE_INDICATOR
array and starts
on the next fullword boundary. The length of the filename that contains the
C source code is in SOURCE_FN_SIZE
. The section
name for the compilation unit immediately follows the SOURCE_FN
and is an 8-byte null-terminated string.
Immediately following
SOURCE_FN
is SOURCE_COMPDATE
, which is the date and time on which the object
was created. SOURCE_COMPDATE
is a null terminated
string which is a total of 18 bytes long (including null terminator). The
format is mm/dd/yy hh:mm:ss
.
Following SOURCE_COMPDATE
is the SOURCE_SNAME
, which is a
null-terminated string, with a maximum
length of 8 bytes (including the null-terminator).
The COOL Part of the Coverage Feature |
Objects compiled with the coverage option must be prelinked with COOL by using COOL's coverage option. COOL constructs the line coverage table pointer lists that are used by the data extraction routine. See Extracting Coverage Data for more information on the data extraction routine. The data extraction routine object should be prelinked with the object module that contains the application's primary entry point, along with any other application objects.
COOL creates a list of pointers to line coverage tables found in each object. A pointer to this list will be passed to the data extraction routine as an argument.
Note: The object created by COOL will not be reentrant,
even if you specify the RENT
option at link
time.
The Coverage Feature Application |
With the exception of the data extraction routine, all routines that
require coverage data must be compiled with the coverage
and extname
options. There are no special
or unique coding requirements for the coverage feature.
Extracting Coverage Data |
To extract coverage data, create a replacement for the dummy library
routine called _ _cvgtrm
. The routine
you write should be in a source file separate from the application, and it
must be coded in C because _ _cvgtrm
is called after destructors have been called for all C++ objects. _ _cvgtrm
should not be compiled with the coverage
option because
any data that is generated will not
be correct.
As noted in The COOL Part of the Coverage Feature, _ _cvgtrm
should be linked with the application's
object
that contains the primary entry point. It should not be included with any
objects that are part of dynamically loaded modules. A dynamically loadable
module is one that contains a _dynamn
function.
_ _cvgtrm
is called once for the
primary entry-point module during library termination. For dynamically-loaded
modules, _ _cvgtrm
is called when the
module is explicitly unloaded (by using unloadm
),
or during library termination when the module is unloaded by the library.
The library will pass the following three parameters on each call to
_ _cvgtrm
:
void _ _cvgtrm (char *sname_ep, char *load_module, struct USAGE_DATA *** cvg_list );
Where the parameters have the characteristics described in the following table.
Data Name | Description |
---|---|
char * pgmname |
Pointer to the string with the name of the application program, if it
can be determined. This is the same value that is passed to main as the first element of the argv
array. |
char * load_module |
Pointer to the name of the load module to which this coverage data applies.
For the initial load module of an application (the one containing the main function), load_module
will be the string "" . |
struct USAGE_DATA *** cvg_list |
Pointer to a list of pointers to line coverage tables. |
JCL for Coverage Sample |
The following JCL builds the application which produced the sample output in Sample Output:
//* Compile the Sample C Main cvgmain.c //* Note: 'coverage' option specified //CVGMAIN EXEC LC370C, // PARM.C='EXTNAME,SNAME(CVGSAMP),COVERAGE' //C.SYSIN DD DISP=SHR,DSN=COVERAGE.C(CVGMAIN) //C.SYSLIN DD DISP=SHR,DSN=COVERAGE.OBJ(CVGMAIN) //* Compile the subroutine called by the C main //* Note: 'coverage' option specified //CVGMAIN2 EXEC LC370C, // PARM.C='EXTNAME,SNAME(CVGMN2),COVERAGE' //C.SYSIN DD DISP=SHR,DSN=COVERAGE.C(CVGMAIN2) //C.SYSLIN DD DISP=SHR,DSN=COVERAGE.OBJ(CVGMAIN2) //* Compile the module dynamically loaded by the C Main //* Note: 'coverage' option specified //CVGDYNM EXEC LC370C, // PARM.C='EXT,SNAME(CVGDYNM),COVERAGE' //C.SYSIN DD DISP=SHR,DSN=COVERAGE.C(CVGDYNM) //C.SYSLIN DD DISP=SHR,DSN=COVERAGE.OBJ(CVGDYNM) //* Compile the Data Extraction function called by the library //* as _ _cvgrtm() Note: NO 'coverage' option specified! //CVGDUMP EXEC LC370C, // PARM.C='EXTNAME,SNAME(CVGDUMP)' //C.SYSIN DD DISP=SHR,DSN=COVERAGE.C(CVGDUMP1) //C.SYSLIN DD DISP=SHR,DSN=COVERAGE.OBJ(CVGDUMP1) //* Linkedit the sample application //* Note: 'coverage' option specified //LKED EXEC LC370LR,PARM.LKED=('LIST,MAP,COVERAGE') //LKED.OBJINPT DD DISP=SHR,DSN=COVERAGE.OBJ //LKED.SYSLMOD DD DISP=SHR,DSN=COVERAGE.LOAD //LKED.SYSIN DD * INCLUDE OBJINPT(CVGDUMP1) INCLUDE OBJINPT(CVGMAIN) INCLUDE OBJINPT(CVGMAIN2) ENTRY MAIN Standard Entry Point NAME CVGMAIN(R) Member Name /* //* Linkedit the dynamically loaded module //* Note: 'coverage' option specified //LKED EXEC LC370LR,PARM.LKED=('LIST,MAP,COVERAGE') //LKED.SYSLIN DD DISP=SHR,DSN=COVERAGE.OBJ(CVGDYNM) //LKED.OBJINPT DD DISP=SHR,DSN=COVERAGE.OBJ //LKED.SYSLMOD DD DISP=SHR,DSN=COVERAGE.LOAD //LKED.SYSIN DD * INCLUDE OBJINPT(CVGDYNM) ENTRY #DYNAMNR Standard Entry Point NAME CVGDYNM(R) Member Name /*
Sample MAIN (cvgmain.c); |
#include <stdio.h> int main() { /* Executed, start up. */ /*================================== Non-Executable. 'coverage' defines two types of C source code lines, executable, and all others. All others can be white-space lines, comment lines, or lines of code that do not result in executable code. ============*/ int exitrc =0; /* Executed, initialized.*/ int notableentry; /* Non-executable. */ int (*ep)(void); /* Non-executable. */ /* No doubt this is a comment line. Non-executable. */ notableentry=1; /* Executed. */ if (notableentry = 1) printf("=1\n");/* Executed. */ if (notableentry = 1) /* Executed. */ printf("=1\n"); /* Executed. */ if (notableentry > 1) printf(">3\n");/* Executed, if () only. */ if (notableentry > 1) /* Executed. */ { /* Non-executable. */ printf(">1\n"); /* Not executed. */ notableentry=3; /* Not executed. */ printf("notableentry = 3\n"); /* Not executed. */ }; /* Non-executable. */ Label: /* Non-executable */ printf("Hello World\n"); /* Executed. */ loadm("CVGDYNM", &ep); /* Executed. */ if (ep != NULL) /* Executed. */ { /* Non-executable. */ (*ep)(); /* Executed. */ unloadm(ep); /* Executed. */ }; /* Non-executable. */ cvgmain2(); /* Executed. */ exit(exitrc); /* Executed. */ } /* Not executed. A */ /* return was added by */ /* the compiler because */ /* exit() was called */ /* instead of return(). */
Sample Subroutine called by MAIN (cvgmain2.c); |
int cvgmain2() { /* Executed, start up. */ printf("main2\n"); /* Executed. */ return(0); /* Executed. */ }
Sample Dynamically-Loaded Module (cvgdynm.c); |
int _dynamn() { /* Executed, start up. */ printf("_dynamn is here\n"); /* Executed. */ return(0); /* Executed. */ } /* Non-executable. */
Sample Coverage Routine (cvgdump.c); |
#include <stdio.h> #include <string.h> #include <cvg.h> /*-------------------------------------------------------------------+ | Track number of data elements processed. | +-------------------------------------------------------------------*/ static int ndx = 0; static void rewind_usage(){ ndx = 0; } /*-------------------------------------------------------------------+ | Extract pointer to line coverage data | +-------------------------------------------------------------------*/ static USAGE_DATA * next_usage(USAGE_DATA **CVGTBL[]){ USAGE_DATA * result = 0; if ( CVGTBL[ndx] ) { result = *CVGTBL[ndx]; ndx++; } return result; } /*-------------------------------------------------------------------+ | Open the source file for a given data element. | +-------------------------------------------------------------------*/ static FILE *opnf(char *name){ FILE *fp; quiet(1); fp = fopen(name,"r"); quiet(0); return fp; } /*-------------------------------------------------------------------+ | Process data elements. | +-------------------------------------------------------------------*/ void _ _cvgtrm (char * pgm_name, char * load_module, USAGE_DATA *** cvg_list){ FILE *fp; char buffer[4096]; int i, j, data_size; USAGE_DATA *udp; fprintf(stderr,"\n\npgm_name: %s load_module: %s\n", pgm_name, load_module); fprintf (stderr,"\nLegend: " "(*=executed; .=not_executed, blank=comment/white space" "/non-executable)\n\n"); rewind_usage(); while ( udp = next_usage(cvg_list) ) { data_size = DATA_SIZE(udp); fprintf(stderr, "SOURCE_FN : %s SOURCR_FN_SIZE : %d \n" "SOURCE_COMPDATE: %s \nSOURCE_SNAME : %s \n", SOURCE_FN(udp), udp->SOURCE_FN_SIZE, SOURCE_COMPDATE(udp),SOURCE_SNAME(udp)); fp = opnf(SOURCE_FN(udp)); if (fp == NULL) { strcpy (buffer, "Unavailable\n"); return; } fprintf (stderr, " Line Covered Source \n"); fprintf (stderr, " --------------------\n"); /* Print source up to FELNO */ for (i = 1; i < udp->FELNO; i++){ if (fp) fgets (buffer, sizeof buffer, fp); fprintf (stderr, "%5d: ", i); fputs (buffer, stderr); } /* Print source from FELNO through LELNO */ for (j = 0; j < DATA_SIZE(udp); j++) { if (fp) fgets (buffer, sizeof buffer, fp); switch (udp->EXE_INDICATOR[j]) { case 0: fprintf (stderr, "%5d: * ", i + j); break; case 1: fprintf (stderr, "%5d: . ", i + j); break; case 2: fprintf (stderr, "%5d: ", i + j); break; default: printf ("ERROR: illegal code %d\n", udp->EXE_INDICATOR[j]); return; } fputs (buffer, stderr); } /* Print source from LELNO+12 through EOF O */ if (fp) { while (fgets (buffer, sizeof buffer, fp)) { fprintf (stderr, "%5d: ", i + j); fputs (buffer, stderr); i++; } fclose (fp); } } }
cvg.h Header |
#ifndef _ _IncCVG #define _ _IncCVG #include <string.h> /*-------------------------------------------------------------------+ | SAS/C Line Coverage Feature | | | | Structure mapping data elements through first byte of coverage | | data, after that point the length is variable. | | | | There are 3 elements that are not defined in the structure, | | source filename, object creation date, and sname. There are | | macros below to assist with locating each of them correctly. | +-------------------------------------------------------------------*/ typedef struct usage_data { int reserved0; int FELNO; /* First executable line number */ int LELNO; /* Last executable line number */ int reserved1; int SOURCE_FN_SIZE; /* Length of source filename */ int reserved2; char EXE_INDICATOR[1]; /* Variable length character array */ }USAGE_DATA; #define DATA_SIZE(udp) ((udp->LELNO - udp->FELNO) + 1) #define SOURCE_FN(udp) \ ( udp->EXE_INDICATOR+((DATA_SIZE(udp) + 3) & ~ 3)) #define SOURCE_COMPDATE(udp) \ ( SOURCE_FN(udp) + (udp->SOURCE_FN_SIZE) ) #define SOURCE_SNAME(udp) \ ( SOURCE_FN(udp) + udp->SOURCE_FN_SIZE + \ strlen(SOURCE_COMPDATE(udp)) + 1 \ ) #endif
Sample Output |
pgm_name:,cvgsamp load_module: CVGDYNM Legend: (*=executed; .=not_executed, blank=comment/white space/non-executable) SOURCE_FN : //DSN:USER.DEV.C(CVGDYNM) SOURCR_FN_SIZE : 28 SOURCE_COMPDATE: 10/09/02 10:05:31 SOURCE_SNAME : CVGDYNM Line Covered Source -------------------- 1: int _dynamn() 2: * { /* Executed, start up. */ 3: * printf("_dynamn is here\n"); /* Executed. */ 4: * return(0); /* Executed. */ 5: } /* Non-executable. */ pgm_name: cvgsamp ,load_module:, Legend: (*=executed; .=not_executed, blank=comment/white space/non-executable) SOURCE_FN : //DSN:USER.DEV.C(CVGMAIN) SOURCR_FN_SIZE : 28 SOURCE_COMPDATE: 10/09/02 10:05:28 SOURCE_SNAME : CVGSAMP Line Covered Source -------------------- 1: #include 2: int main() 3: * { /* Executed, start up. */ 4: 5: /*================================== Non-Executable. 6: 'coverage' defines two types of C source code 7: lines, executable, and all others. All others 8: can be white-space lines, comment lines, or lines 9: of code that do not result in executable code. ============*/ 10: 11: * int exitrc =0; /* Executed, initialized.*/ 12: int notableentry; /* Non-executable. */ 13: int (*ep)(void); /* Non-executable. */ 14: /* No doubt this is a comment line. Non-executable. */ 15: * notableentry=1; /* Executed. */ 16: * if,(notableentry,= 1) printf("=1\n");/* Executed. */ 17: * if (notableentry = 1) /* Executed. */ 18: * printf("=1\n"); /* Executed. */ 19: * if (notableentry > 1) printf(">3\n");/* Executed, if () only. */ 20: * if (notableentry > 1) /* Executed. */ 21: { /* Non-executable. */ 22: . printf(">1\n"); /* Not executed. */ 23: . notableentry=3; /* Not executed. */ 24: . printf("notableentry = 3\n"); /* Not executed. */ 25: }; /* Non-executable. */ 26: Label: /* Non-executable */ 27: * printf("Hello World\n"); /* Executed. */ 28: * loadm("CVGDYNM", &ep); /* Executed. */ 29: * if (ep != NULL) /* Executed. */ 30: { /* Non-executable. */ 31: * (*ep)(); /* Executed. */ 32: * unloadm(ep); /* Executed. */ 33: }; /* Non-executable. */ 34: * cvgmain2(); /* Executed. */ 35: * exit(exitrc); /* Executed. */ 36: . } /* Not executed. A */ 37: /* return was added by */ 38: /* the compiler because */ 39: /* exit() was called */ 40: /* instead of,return()., */ SOURCE_FN : //DSN:USER.DEV.C(CVGMAIN2) SOURCR_FN_SIZE : 29 SOURCE_COMPDATE: 10/09/02 10:05:29 SOURCE_SNAME : CVGMN2 Line Covered Source -------------------- 1: int cvgmain2() 2: * { /* Executed, start up. */ 3: * printf("main2\n"); /* Executed. */ 4: * return(0); /* Executed. */ 5: }
Chapter Contents |
Previous |
Next |
Top of Page |
Copyright © 2004 by SAS Institute Inc., Cary, NC, USA. All rights reserved.