/* $Id$ */ /* mergecov - a program for merging NC-COV output MERGECOV takes as input a file that contains a list of NC-COV coverage files. One filename per line. All files must be for the same RTL database, and the files must be in context format. MERGECOV reads all the files, and generates a list of lines that show up as uncovered in ALL the files. This output list is in the input format, so don't-care lines can be removed and the merge can be re-run with the reduced output file as the first input file, thus limiting the results to lines of interest. The advantage of MERGECOV is that, unlike the native NC-COV merging tool, MERGECOV does not require matching binary databases. As long as the RTL-of-interest is the same for all test runs, the results (relative to those RTL files) can be merged using MERGECOV, even if they are only partial results. By Jeff Winston http://www.kwcpa.com/tools Rev. 1.2, 1/3/04 Copyright (C) 2004 type mergecov -h for more information --------------------------------------------------------------------- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. or check out http://www.gnu.org/copyleft/gpl.html --------------------------------------------------------------------- To compile: gcc mergecov.c -o mergecov */ # define MAXLINES 100000 /* maximum number of uncovered lines in first file */ # define MAXNAMES 5000 /* maximum number of module names in first file */ # include # include char rtl_filename[255], /* module filename from input file */ module_name[255], /* module name from input file */ instr[255], /* line from coverage file */ listfile[255], /* input and output filenames */ exclfile[255], infile[255], outfile[255], logfile[255], lines[MAXLINES][255], /* store all the uncovered lines for display */ modnames[MAXNAMES][64], /* store all the complete module names */ exclnames[MAXNAMES][64]; /* file pointers */ FILE *exclfile_ptr, *listfile_ptr, *infile_ptr, *outfile_ptr, *logfile_ptr; int modlines[MAXLINES]; /* store the lines in each module */ int modseen[MAXNAMES][2]; /* count each time a module is seen 0= count 1= flag*/ int db[MAXLINES][4]; /* Database. 0 = module name, 1 = line, 2 = count 3= marked flag */ int excl[MAXNAMES]; /* marked for excluded */ float dbmaxf,ratio,uncovered,total_statements; /* used to calculate coverage */ int progress, /* progress counter */ x,y,first, /* temps */ filecnt, /* files processed count */ dbmax, /* db max row */ ntk, /* retval for get_tokens */ fntk, /* retval for get_filename_tokens */ modmax, /* max row of modnames */ exclmax, /* max row of exclude file */ modnum, /* index into modnames */ partial, /* input file contains partial data */ line, /* line read from file */ linecnt, /* used for final stats */ file_found, /* flags */ line_found, excluded_name, lines_in_module; /* Number of Statements in module */ char tokens[64][80]; /* returned tokens from parser */ char ftokens[64][64]; /* returned tokens from parser */ int get_tokens(char *str); int get_filename_tokens(char *str); main(argc,argv) int argc; /* Parse input and output file from command line */ char *argv[]; { printf("\nMERGECOV Rev 1.2 by J. Winston\n"); exclmax=-1; if (argc == 2) { ++argv; strcpy(instr,*argv); /* help information */ if (strcmp(instr,"-h")==0) { printf("\nUSAGE: mergcov [-e ] \n\n"); printf("MERGECOV takes as input a file that contains a list of NC-COV\n"); printf("coverage files. One filename per line. All files must be for\n"); printf("the same RTL database, and the files must be in context format.\n"); printf("To generate the proper input file from NC-COV, use the command\n"); printf("\nnccov -batch -stmt -detail context -view module .acv\n\n"); printf("In the MERGECOV input file, use a # in the first column for comments.\n"); printf("Blank lines are permitted.\n\n"); printf("The second input file, which is optional, is an excludefile which\n"); printf("lists modules and files that are to be ignored in their entirety. The\n"); printf("file should have one entry per line. For files, use just the root filename\n"); printf("without the .v extension. Blank lines and comments with # in column 1\n"); printf("are also permitted.\n\n"); printf("MERGECOV reads all the files, and generates two files: The first\n"); printf("(called .rpt) is a a list of lines that show up as\n"); printf("uncovered in ALL the files. This output file is in the input format\n"); printf("so that it can be used as a starting point for an incremental run.\n"); printf("Alternately, don't-care lines can be removed and the merge can be\n"); printf("re-run with the reduced output file as the first input file, thus\n"); printf("thus limiting the results to lines of interest. The second file is\n"); printf("called _stat.log. It contains a list of all the files,\n"); printf("with total lines and lines covered.\n\n"); printf("The command line output of MERGECOV shows the incremental coverage\n"); printf("provided by each test.\n\n"); printf("In the you can add the word PARTIAL after any filename. If\n"); printf("you do this, MERGECOV will NOT assume that any missing modules are \n"); printf("covered for that file. Thus, section-test-bench coverage files can\n"); printf("be added to the coverage data.\n\n"); printf("Note that there is a problem with MERGECOV when using files that \n"); printf("include other files. The lines in the included files are fully \n"); printf("counted each time they are included. This will incorrectly increase \n"); printf("the Number of Statements reported by NC-COV, and thus MERGECOV.\n"); printf("To remedy this, MERGECOV considers the number of uncovered \n"); printf("statements in the first file as the total number of uncovered \n"); printf("statements. This initial file should be generated with as little \n"); printf("simulation time as possible (e.g., 1fs). It may show some lines as \n"); printf("covered, but these are lines that typically are really covered \n"); printf("statically, and thus should be excluded. MERGECOV also reports \n"); printf("total coverage as a proportion of \"Number of Statements\", but these\n"); printf("values should be ignored if files are included in others.\n\n"); printf("The advantage of MERGECOV is that, unlike the native NC-COV\n"); printf("merging tool, MERGECOV does not require matching binary databases.\n"); printf("As long as the RTL-of-interest is the same for all test runs, the\n"); printf("results (relative to those RTL files) can be merged using MERGECOV.\n\n"); printf("Note: To mark a line or module as covered, remove any mention of it\n"); printf("from the first file in the file list. (If you remove an entire module,\n"); printf("be sure to leave all the \"Number of Statements:\" lines intact).\n\n"); printf("Note: When code is included in more than one module, NC-COV\n"); printf("considers each copy of the code as separate. MERGECOV considers\n"); printf("any included code covered once as covered.\n\n"); printf("Finally, remember that modules fully covered in every file\n"); printf("don't have their lines added to the total, because MERGECOV\n"); printf("never sees any mention of them in the input files.\n\n"); exit(0); } } if ((argc !=3) && (argc !=5)) { printf("Error: Incorrect number of parameters on command line\n"); printf("Type mergecov -h for help\n"); exit(0); } ++argv; strcpy(listfile,*argv); /* open input file */ if ((listfile_ptr=fopen(listfile, "r")) ==NULL) { printf("Error: Error opening input file /%s/\n\n",listfile); exit(1); } ++argv; strcpy(outfile,*argv); /* open exclude file */ if (strcmp(outfile,"-e")==0) { ++argv; strcpy(exclfile,*argv); /* open input file */ if ((exclfile_ptr=fopen(exclfile, "r")) ==NULL) { printf("Error: Error opening exclude file /%s/\n\n",exclfile); exit(1); } exclmax=0; ++argv; } strcpy(outfile,*argv); /* open output file */ strcpy(logfile,outfile); strcat(outfile,".rpt"); strcat(logfile,"_stat.log"); if ((outfile_ptr=fopen(outfile, "w")) ==NULL) { printf("Error: Error opening output file /%s/\n\n",outfile); exit(1); } if ((logfile_ptr=fopen(logfile, "w")) ==NULL) { printf("Error: Error opening output file /%s/\n\n",logfile); exit(1); } filecnt=0; dbmax=0;modmax=0;total_statements=0;lines_in_module=0; /* init everything */ /* MERGECOV uses a big array with 4 elements. The first is a pointer into a lookaside list of module names. The second is a line number. The third is a count, and the fourth is a flag. For the first file it reads, it builds the database from all uncovered lines, checking to make sure no line is entered twice. Each line is entered with a count of 1. The flag makes sure that no line is entered twice from the same file. For each subsequent file, MERGECOV clears the flags and looks for lines that appear both in the original and current file. For these lines the count is incremented. When all files are processed, any lines that have counts equal to the number of files processed were uncovered in all files. The assumption, of course, is that all files cover the same database, so that any line NOT listed in ANY file is covered. */ /* read exclude file */ if (exclmax == 0) { while (fgets(instr,255,exclfile_ptr) != NULL) /* Keep getting lines until no more files */ { if ((strlen(instr) > 0) && (instr[0] != '#')) { ntk = get_tokens(instr); strcpy(exclnames[exclmax],tokens[0]); exclmax++; } } printf("Read %d entries from Exclusion File %s\n",exclmax,exclfile); } while (fgets(infile,255,listfile_ptr) != NULL) /* Keep getting lines until no more files */ { ntk = get_tokens(infile); /* parse line */ if ((infile[0] != '#') && (ntk > 0)) /* if there's something there, and it's not a comment, better be a filename */ { if ((infile_ptr=fopen(tokens[0], "r")) ==NULL) /* open the file */ { printf("Error: Error opening input file /%s/\n\n",tokens[0]); exit(1); } partial = 0; if ((ntk > 1) && (strcmp(tokens[1],"PARTIAL")==0)) partial=1; uncovered = 0; for (x=0; x< dbmax; x++) /* clear the flags */ { db[x][3] = 0; y=db[x][0]; if (db[x][2] == modseen[y][0]) uncovered++; } for (x=0; x< modmax; x++) /* clear flags */ { modseen[x][1] = 0; } if (filecnt > 0) /* print incremental coverage ( after first file) */ { dbmaxf = dbmax; ratio = (100*(dbmaxf - uncovered))/dbmaxf; /* do stats */ printf("\n%9.0f Lines Uncovered out of %9.0f total. Coverage = %7.2f%%\n",uncovered,dbmaxf,ratio); } if (partial ==0) printf("\nProcessing file %4d %s...",filecnt+1,tokens[0]); /* and announce it */ else printf("\nProcessing file %4d (P) %s ...",filecnt+1,tokens[0]); /* and announce it */ progress=0; /* clear the line counter */ while (fgets(instr,255,infile_ptr) != NULL) /* Keep getting lines until no more data */ { progress++; /* print . every 5k lines */ if (progress==5000) { printf(".");fflush(stdout); progress=0; } ntk = get_tokens(instr); /* parse line */ if (ntk > 2) /* if it has stuff on it */ { if (strcmp(tokens[0],"Module:")==0) { if (ntk != 5) { printf("\nError: Bad Line: %s\n\n",instr); exit(1); } fntk = get_filename_tokens(tokens[1]); /* parse name of module to see if we include statements in total */ strcpy(module_name, ftokens[1]); /* parse out module name */ excluded_name =0; for (x=0; x< exclmax; x++) { if (strcmp(exclnames[x],module_name)==0) /* check if on exclude list */ { excluded_name = 1; x = exclmax + 2; } } } else if ((filecnt == 0) && (strcmp(tokens[0],"Number")==0) && /* grab number of statements */ (strcmp(tokens[1],"of")==0) && (strcmp(tokens[2],"Statements:")==0)) { sscanf(tokens[3],"%d",&lines_in_module); if (excluded_name == 0) total_statements=total_statements+lines_in_module; } else if (strcmp(tokens[0],"PROB")==0) /* grab module name */ { if (ntk !=7) { printf("\nError: Bad Line: %s\n\n",instr); exit(1); } fntk = get_filename_tokens(tokens[6]); /* grab full file name of module */ strcpy(rtl_filename, ftokens[fntk-2]); /* parse out module name */ file_found=0; /* see if it's new */ for (x=0; x