diff options
Diffstat (limited to 'tester/covoar/covmerge.cc')
-rw-r--r-- | tester/covoar/covmerge.cc | 541 |
1 files changed, 541 insertions, 0 deletions
diff --git a/tester/covoar/covmerge.cc b/tester/covoar/covmerge.cc new file mode 100644 index 0000000..e283774 --- /dev/null +++ b/tester/covoar/covmerge.cc @@ -0,0 +1,541 @@ +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <netdb.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <errno.h> + +#include "app_common.h" +#include "CoverageFactory.h" +#include "CoverageMap.h" +#include "CoverageRanges.h" +#include "Explanations.h" +#include "ObjdumpProcessor.h" +#include "Reports.h" + +/* + * Variables to control global behavior + */ +int verbose = 0; +Coverage::CoverageFormats_t coverageFormat; +char *mergedCoverageFile = NULL; +char *branchReportFile = NULL; +char *coverageReportFile = NULL; +char *sizeReportFile = NULL; +uint32_t lowAddress = 0xffffffff; +uint32_t highAddress = 0xffffffff; + +char *target = NULL; +char *executable = NULL; +char *explanations = NULL; +char *noExplanations = NULL; +char *progname; + +/* + * Global variables for the program + */ +Coverage::CoverageMapBase *CoverageMap = NULL; +Coverage::CoverageReaderBase *CoverageReader = NULL; +Coverage::CoverageWriterBase *CoverageWriter = NULL; +Coverage::ObjdumpProcessor *ObjdumpProcessor = NULL; +Coverage::CoverageRanges *Ranges = NULL; +Coverage::Explanations *Explanations = NULL; + +int BranchesAlwaysTaken = 0; +bool BranchesFound = false; +int BranchesNeverTaken = 0; +int UncoveredRanges = 0; + +/* + * Set of addresses we need source line number for + */ +std::list<uint32_t> AddressesNeedingSourceLine; + +/* + * Convert string to int with status out + */ + +bool stringToUint32( + const char *s, + int base, + uint32_t *n +) +{ + long long result; + + if ( !n ) + return false; + + errno = 0; + *n = 0; + + result = strtoll( s, NULL, base ); + + if ( (result == 0) && errno ) + return false; + + if ( (result == LLONG_MAX) && (errno == ERANGE)) + return false; + + if ( (result == LLONG_MIN) && (errno == ERANGE)) + return false; + + *n = (uint32_t)result; + return true; +} + +/* + * Print program usage message + */ +void usage() +{ + fprintf( + stderr, + "Usage: %s [-v] [-t] [-m file] -T TARGET [-e EXECUTABLE]-l ADDRESS -h ADDRESS coverage1... coverageN\n" + "\n" + " -l low address - low address of range to merge\n" + " -l high address - high address of range to merge\n" + " -f format - coverage files are in <format> " + "(RTEMS, TSIM or Skyeye)\n" + " -m FILE - optional merged coverage file to write\n" + " -r REPORT - optional coverage report to write\n" + " -s REPORT - optional size report to write\n" + " -T TARGET - target name\n" + " -e EXECUTABLE - name of executable to get symbols from\n" + " -E EXPLANATIONS - name of file with explanations\n" + " -v - verbose at initialization\n" + "\n", + progname + ); +} + +/* + * Look over the coverage map and compute uncovered ranges and branches + */ +void ComputeUncovered(void) +{ + uint32_t a, la, ha; + std::list<Coverage::CoverageRange>::iterator it; + + a = lowAddress; + while (a < highAddress) { + + /* + * Find all the unexecuted addresses and add them to the range. + */ + if (!CoverageMap->wasExecuted( a )) { + la = a; + for (ha=a+1; ha<=highAddress && !CoverageMap->wasExecuted(ha); ha++) + ; + ha--; + + UncoveredRanges++; + Ranges->add( la, ha ); + AddressesNeedingSourceLine.push_back( la ); + AddressesNeedingSourceLine.push_back( ha ); + a = ha + 1; + } + + else if (CoverageMap->isBranch( a )) { + BranchesFound = true; + la = a; + for (ha=a+1; + ha<=highAddress && !CoverageMap->isStartOfInstruction(ha); + ha++) + ; + ha--; + + if (CoverageMap->wasAlwaysTaken( la )) { + BranchesAlwaysTaken++; + AddressesNeedingSourceLine.push_back( la ); + } + else if (CoverageMap->wasNeverTaken( la )) { + BranchesNeverTaken++; + AddressesNeedingSourceLine.push_back( la ); + } + a = ha + 1; + } + else + a++; + } +} + +/* + * Find source lines for addresses + */ +void FindSourceForAddresses(void) +{ + FILE *tmpfile; + std::list<uint32_t>::iterator it; + + /* + * Write a temporary file with ranges + */ + if ( verbose ) + fprintf( stderr, "Writing ranges.tmp input to addr2line\n" ); + + tmpfile = fopen( "ranges.tmp", "w" ); + if ( !tmpfile ) { + fprintf( stderr, "Unable to open %s\n\n", "ranges.tmp" ); + exit(-1); + } + + for (it = AddressesNeedingSourceLine.begin() ; + it != AddressesNeedingSourceLine.end() ; + it++ ) { + fprintf(tmpfile, "0x%08x\n", *it); + } + + fclose( tmpfile ); + + /* + * Generate a file with the addr2line mapping + */ + if ( verbose ) + fprintf( stderr, "Running addr2line\n" ); + + { + char command[512]; + sprintf( + command, + "%s -e %s <%s | dos2unix >%s", + Tools->getAddr2line(), + executable, + "ranges.tmp", + "ranges01.tmp" + ); + if ( system( command ) ) { + fprintf( stderr, "addr2line command (%s) failed\n", command ); + exit( -1 ); + } + } + + /* + * Go back over the ranges, read the addr2line output, and correlate it. + */ + if ( verbose ) + fprintf( stderr, "Merging addr2line output into range\n" ); + + tmpfile = fopen( "ranges01.tmp", "r" ); + if ( !tmpfile ) { + fprintf( stderr, "Unable to open %s\n\n", "ranges01.tmp" ); + exit(-1); + } + + for (it = AddressesNeedingSourceLine.begin() ; + it != AddressesNeedingSourceLine.end() ; + it++ ) { + char buffer[512]; + char *cStatus; + + cStatus = fgets( buffer, 512, tmpfile ); + if ( cStatus == NULL ) { + fprintf( stderr, "Out of sync in addr2line output\n" ); + exit( -1 ); + } + buffer[ strlen(buffer) - 1] = '\0'; + + CoverageMap->setSourceLine( *it, std::string( buffer ) ); + } + fclose( tmpfile ); +} + +#define PrintableString(_s) \ + ((!(_s)) ? "NOT SET" : (_s)) + +int main( + int argc, + char **argv +) +{ + int opt; + int i; + char *format = NULL; + + progname = argv[0]; + + while ((opt = getopt(argc, argv, "b:e:E:f:h:l:m:r:s:T:v")) != -1) { + switch (opt) { + case 'b': branchReportFile = optarg; break; + case 'e': executable = optarg; break; + case 'E': explanations = optarg; break; + case 'm': mergedCoverageFile = optarg; break; + case 'r': coverageReportFile = optarg; break; + case 's': sizeReportFile = optarg; break; + case 'T': target = optarg; break; + case 'v': verbose = 1; break; + case 'f': + coverageFormat = Coverage::CoverageFormatToEnum(optarg); + format = optarg; + break; + case 'l': + if ( ! stringToUint32( optarg, 16, &lowAddress ) ) { + fprintf( stderr, "Low address is not a hexadecimal number\n" ); + usage(); + exit(-1); + } + break; + case 'h': + if ( ! stringToUint32( optarg, 16, &highAddress ) ) { + fprintf( stderr, "High address is not a hexadecimal number\n" ); + usage(); + exit(-1); + } + break; + default: /* '?' */ + usage(); + exit( -1 ); + } + } + if ( verbose ) { + fprintf( stderr, "verbose : %d\n", verbose ); + fprintf( stderr, "Coverage Format : %s\n", format ); + fprintf( stderr, "low address : 0x%08x\n", lowAddress ); + fprintf( stderr, "high address : 0x%08x\n", highAddress ); + fprintf( stderr, "Target : %s\n", PrintableString(target) ); + fprintf( stderr, "executable : %s\n", PrintableString(executable) ); + fprintf( stderr, "merged coverage : %s\n", + PrintableString(mergedCoverageFile) ); + fprintf( stderr, "\n" ); + } + + /* + * Target name must be set + */ + if ( !target ) { + fprintf( stderr, "target must be given.\n\n" ); + usage(); + exit(-1); + } + + /* + * Validate format + */ + if ( !format ) { + fprintf( stderr, "coverage format report must be given.\n\n" ); + usage(); + exit(-1); + } + + /* + * Validate address range + */ + if ( lowAddress == 0xffffffff ) { + fprintf( stderr, "Low address not specified.\n\n" ); + usage(); + exit(-1); + } + + if ( highAddress == 0xffffffff ) { + fprintf( stderr, "High address not specified.\n\n" ); + usage(); + exit(-1); + } + + if ( lowAddress >= highAddress ) { + fprintf( stderr, "Low address >= high address.\n\n" ); + usage(); + exit(-1); + } + + /* + * Create toolnames based on target + */ + TargetInfo = Target::TargetFactory( target ); + + /* + * Create a ranges set + */ + Ranges = new Coverage::CoverageRanges(); + Explanations = new Coverage::Explanations(); + + Explanations->load( explanations ); + + /* + * Create coverage map + */ + CoverageMap = new Coverage::CoverageMap( lowAddress, highAddress ); + if ( !CoverageMap ) { + fprintf( stderr, "Unable to create coverage map.\n\n" ); + exit(-1); + } + + /* + * Create input + */ + CoverageReader = Coverage::CreateCoverageReader(coverageFormat); + if ( !CoverageReader ) { + fprintf( stderr, "Unable to create coverage file reader.\n\n" ); + exit(-1); + } + + /* + * Create the objdump processor + */ + ObjdumpProcessor = new Coverage::ObjdumpProcessor(); + + /* + * Create writer + * + * NOTE: We ALWAYS write the merged coverage in RTEMS format. + */ + CoverageWriter = Coverage::CreateCoverageWriter( + Coverage::COVERAGE_FORMAT_RTEMS + ); + if ( !CoverageWriter ) { + fprintf( stderr, "Unable to create coverage file writer.\n\n" ); + exit(-1); + } + + /* + * Add in the objdump before reading the coverage information. We may + * want to take advantage of the information line where instructions + * begin. + */ + if ( executable ) { + if ( verbose ) + fprintf( stderr, "Reading objdump of %s\n", executable ); + ObjdumpProcessor->initialize( executable, CoverageMap ); + } + + /* + * Now get to some real work + */ + if ( verbose ) + fprintf( stderr, "Processing coverage files\n" ); + for ( i=optind ; i < argc ; i++ ) { + //fprintf( stderr, "Processing %s\n", argv[i] ); + CoverageReader->ProcessFile( argv[i], CoverageMap ); + } + + /* + * Now to write some output + */ + if ( mergedCoverageFile ) { + if ( verbose ) + fprintf( + stderr, + "Writing merged coverage file (%s)\n", + mergedCoverageFile + ); + CoverageWriter->writeFile( + mergedCoverageFile, + CoverageMap, + lowAddress, + highAddress + ); + } + + /* + * Marks nops as executed when they are surrounded by executed instructions. + */ + ObjdumpProcessor->markNopsAsExecuted( CoverageMap ); + + /* + * Iterate over the coverage map and determine the uncovered + * ranges and branches. + */ + ComputeUncovered(); + + /* + * Look up the source file and line number for the addresses + * of interest. + */ + FindSourceForAddresses(); + + /* + * Generate report of ranges not executed + */ + if ( coverageReportFile ) { + if ( verbose ) + fprintf( stderr, "Writing coverage report (%s)\n", coverageReportFile ); + WriteCoverageReport( coverageReportFile ); + + /* + * Let the user know how many cases there were + */ + printf( "%d uncovered ranges found\n", UncoveredRanges ); + } + + /* + * Generate report of branches taken/not taken + */ + if ( branchReportFile ) { + if ( verbose ) + fprintf( stderr, "Writing branch report (%s)\n", branchReportFile ); + WriteBranchReport( branchReportFile, lowAddress, highAddress ); + + /* + * Let the user know how many branch cases were found + */ + if (!BranchesFound) + printf( "No branch information found\n" ); + else { + printf( + "%d uncovered branches found\n", + BranchesAlwaysTaken + BranchesNeverTaken + ); + printf( + " %d branches always taken\n", BranchesAlwaysTaken + ); + printf( + " %d branches never taken\n", BranchesNeverTaken + ); + } + } + + /* + * Simple formatted report of size of ranges + */ + if ( sizeReportFile ) { + if ( verbose ) + fprintf( stderr, "Writing size report (%s)\n", sizeReportFile ); + WriteSizeReport( sizeReportFile ); + } + + /* + * Generate annotated assembly file + */ + if ( verbose ) + fprintf( stderr, "Writing annotated report (%s)\n", "annotated.txt" ); + WriteAnnotatedReport( "annotated.txt", lowAddress, highAddress ); + + /* + * write explanations that were not found + */ + std::string str = explanations; + str = str + ".NotFound"; + if ( verbose ) + fprintf( stderr, "Writing Not Found Report (%s)\n", str.c_str() ); + Explanations->writeNotFound(str.c_str()); + + /* + * Calculate coverage percentage + */ + { + uint32_t a; + uint32_t notExecuted = 0; + double percentage; + + for ( a=lowAddress ; a < highAddress ; a++ ) { + if ( !CoverageMap->wasExecuted( a ) ) + notExecuted++; + } + + percentage = (double) notExecuted; + percentage /= (double) (highAddress - lowAddress); + percentage *= 100.0; + printf( "Bytes Analyzed : %d\n", highAddress - lowAddress ); + printf( "Bytes Not Executed : %d\n", notExecuted ); + printf( "Percentage Executed : %5.4g\n", 100.0 - percentage ); + printf( "Percentage Not Executed : %5.4g\n", percentage ); + } + + return 0; +} |