Chapter Contents

Previous

Next
opcmd

New Coverage Support Feature

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.  [cautionend]

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.  [cautionend]


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.