#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;
}