summaryrefslogtreecommitdiff
path: root/tester/covoar/covmerge.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tester/covoar/covmerge.cc')
-rw-r--r--tester/covoar/covmerge.cc541
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;
+}