/*! @file GcovData.cc
* @brief GcovData Implementation
*
* This file contains the implementation of the functions supporting
* reading *.gcno and writing *.gcda files for gcov support
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//#include <sys/stat.h>
//#include "app_common.h"
#include "GcovData.h"
//#include "ExecutableInfo.h"
//#include "CoverageMap.h"
//#include "qemu-traces.h"
#include "rtems-utils.h"
namespace Gcov {
GcovData::GcovData( Coverage::DesiredSymbols& symbolsToAnalyze ):
numberOfFunctions( 0 ),
gcnoPreamble(),
symbolsToAnalyze_m( symbolsToAnalyze )
{
}
GcovData::~GcovData()
{
}
bool GcovData::readGcnoFile( const std::string& fileName )
{
int status;
std::ifstream gcovFile;
std::string tempString;
std::string tempString2;
std::string tempString3;
size_t index;
if ( fileName.length() >= FILE_NAME_LENGTH ) {
std::cerr << "ERROR: File name is too long to be correctly stored: "
<< fileName.length() << std::endl;
return false;
}
gcnoFileName = fileName;
gcdaFileName = fileName;
textFileName = fileName;
cFileName = fileName;
tempString = gcdaFileName;
tempString2 = textFileName;
tempString3 = cFileName;
index = tempString.find( ".gcno" );
if ( index == std::string::npos ) {
std::cerr << "ERROR: incorrect name of *.gcno file" << std::endl;
return false;
} else {
// construct gcda file name
tempString = tempString.replace( index, strlen( ".gcno" ), ".gcda" );
// construct report file name
tempString2 = tempString2.replace( index, strlen( ".gcno" ), ".txt" );
// construct source file name
tempString3 = tempString3.replace( index, strlen( ".gcno" ), ".c" );
}
// Debug message
// std::cerr << "Reading file: " << gcnoFileName << std::endl;
// Open the notes file.
gcovFile.open( gcnoFileName );
if ( !gcovFile ) {
std::cerr << "Unable to open " << gcnoFileName << std::endl;
return false;
}
// Read and validate the gcnoPreamble (magic, version, timestamp) from the file
status = readFilePreamble( &gcnoPreamble, gcovFile, GCNO_MAGIC );
if ( status <= 0 ) {
std::cerr << "Unable to read " << gcnoFileName << std::endl;
return false;
}
//Read all remaining frames from file
while( readFrame( gcovFile ) ) {}
return true;
}
bool GcovData::writeGcdaFile()
{
gcov_preamble preamble;
gcov_frame_header header;
std::ofstream gcdaFile;
functions_iterator_t currentFunction;
arcs_iterator_t currentArc;
uint32_t buffer;
uint32_t countersFound;
uint32_t countersFoundSum;
uint64_t countersSum;
uint64_t countersMax;
uint64_t llBuffer[4096]; // TODO: Use common buffer
gcov_statistics objectStats;
gcov_statistics programStats;
std::ofstream::pos_type bytes_before;
// Debug message
//std::cerr << "Writing file: " << gcdaFileName << std::endl;
// Lets clear counters sumators
countersSum = 0;
countersMax = 0;
countersFoundSum = 0;
// Open the data file.
gcdaFile.open( gcdaFileName );
if ( !gcdaFile ) {
std::cerr << "Unable to create " << gcdaFileName << std::endl;
return false;
}
//Form preamble
preamble.magic = GCDA_MAGIC;
preamble.version = gcnoPreamble.version;
preamble.timestamp = gcnoPreamble.timestamp;
//Write preamble
gcdaFile.write( (char *) &preamble , sizeof( preamble ) );
if ( gcdaFile.fail() ) {
std::cerr << "Error while writing gcda preamble to a file "
<< gcdaFileName << std::endl;
}
//Write function info and counter counts
for (
currentFunction = functions.begin();
currentFunction != functions.end();
currentFunction++
) {
//Write function announcement frame header (length always equals 2)
header.tag = GCOV_TAG_FUNCTION;
header.length = 2;
gcdaFile.write( (char *) &header, sizeof( header ) );
if ( gcdaFile.fail() ) {
std::cerr << "Error while writing function announcement to a file "
<< gcdaFileName << std::endl;
}
//Write function id
buffer = (*currentFunction).getId();
gcdaFile.write( (char *) &buffer, sizeof( buffer ) );
if ( gcdaFile.fail() ) {
std::cerr << "Error while writing function id to a file "
<< gcdaFileName << std::endl;
}
//Write function checksum
buffer = (*currentFunction).getChecksum();
gcdaFile.write( (char *) &buffer, sizeof( buffer ) );
if ( gcdaFile.fail() ) {
std::cerr << "Error while writing function checksum to a file "
<< gcdaFileName << std::endl;
}
// Determine how many counters there are
// and store their counts in buffer
countersFound = 0;
(*currentFunction).getCounters(
llBuffer,
countersFound,
countersSum,
countersMax
);
countersFoundSum += countersFound;
//Write info about counters
header.tag = GCOV_TAG_COUNTER;
header.length = countersFound * 2;
gcdaFile.write( (char *) &header, sizeof( header ) );
if ( gcdaFile.fail() ) {
std::cerr << "Error while writing counter header to a file "
<< gcdaFileName << std::endl;
}
bytes_before = gcdaFile.tellp();
gcdaFile.write( (char *) llBuffer, sizeof( uint64_t ) * countersFound );
if ( gcdaFile.tellp() - bytes_before != countersFound ) {
std::cerr << "Error while writing counter data to a file "
<< gcdaFileName << std::endl;
}
}
// Prepare frame with object file statistics
header.tag = GCOV_TAG_OBJECT_SUMMARY;
header.length = 9;
objectStats.checksum = 0; // TODO: have no idea hov to calculates it :)
objectStats.counters = countersFoundSum;
objectStats.runs = 1; // We are lying for now, we have no means of figuring this out
objectStats.sum = countersSum; // Sum of all counters
objectStats.max = countersMax; // max value for counter on last run, we have no clue
objectStats.sumMax = countersMax; // we have no clue
// Write data
gcdaFile.write( (char *) &header, sizeof( header ) );
if ( gcdaFile.fail() ) {
std::cerr << "Error while writing stats header to a file "
<< gcdaFileName << std::endl;
}
gcdaFile.write( (char *) &objectStats, sizeof( objectStats ) );
if ( gcdaFile.fail() ) {
std::cerr << "Error while writing object stats to a file "
<< gcdaFileName << std::endl;
}
// Prepare frame with program statistics
header.tag = GCOV_TAG_PROGRAM_SUMMARY;
header.length = 9;
programStats.checksum = 0; // TODO: have no idea hov to calculate it :)
programStats.counters = countersFoundSum;
programStats.runs = 1; // We are lying for now, we have no clue
programStats.sum = countersSum; // Sum of all counters
programStats.max = countersMax; // max value for counter on last run, we have no clue
programStats.sumMax = countersMax; // we have no clue
// Write data
gcdaFile.write( (char *) &header, sizeof( header ) );
if ( gcdaFile.fail() ) {
std::cerr << "Error while writing stats header to a file "
<< gcdaFileName << std::endl;
}
gcdaFile.write( (char *) &programStats, sizeof( programStats ) );
if ( gcdaFile.fail() ) {
std::cerr << "Error while writing program stats to a file "
<< gcdaFileName << std::endl;
}
return true;
}
bool GcovData::readFrame( std::ifstream& gcovFile )
{
gcov_frame_header header;
char buffer[512];
char intBuffer[16384];
uint32_t tempBlockId;
blocks_iterator_t tempBlockIterator;
int status;
status = readFrameHeader( &header, gcovFile );
if ( status <= 0 ) {
// Not printing error message because this
// happenns at the end of each file
return false;
}
switch ( header.tag ) {
case GCOV_TAG_FUNCTION:
{
numberOfFunctions++;
GcovFunctionData newFunction;
if ( !readFunctionFrame(header, gcovFile, &newFunction) ) {
std::cerr << "Error while reading FUNCTION from gcov file..."
<< std::endl;
return false;
}
functions.push_back( newFunction );
}
break;
case GCOV_TAG_BLOCKS:
gcovFile.read( intBuffer, header.length );
if ( gcovFile.gcount() != (int) header.length ) {
std::cerr << "Error while reading BLOCKS from gcov file..."
<< std::endl
<< "Header length is " << header.length
<< " instead of " << gcovFile.gcount()
<< std::endl;
return false;
}
for( uint32_t i = 0; i < header.length; i++ ) {
functions.back().addBlock( i, intBuffer[i], "" );
}
break;
case GCOV_TAG_ARCS:
gcovFile.read( intBuffer, header.length );
if ( gcovFile.gcount() != (int) header.length ) {
return false;
}
for ( int i = 1; i < (int) header.length; i += 2 ) {
functions.back().addArc(intBuffer[0], intBuffer[i], intBuffer[i+1]);
}
break;
case GCOV_TAG_LINES:
gcovFile.read( intBuffer, 2 );
if ( gcovFile.gcount() != 2 || intBuffer[1] != 0 ) {
std::cerr << "Error while reading block id for LINES from gcov "
<< "file..." << std::endl;
return false;
}
tempBlockId = intBuffer[0];
header.length -= 2;
// Find the right block
tempBlockIterator =functions.back().findBlockById( tempBlockId );
header.length -= readString( buffer, gcovFile );
functions.back().setBlockFileName( tempBlockIterator, buffer );
gcovFile.read( intBuffer, header.length );
if ( gcovFile.gcount() != (int) header.length ) {
std::cerr << "Error while reading LINES from gcov file..."
<< std::endl;
return false;
} else {
for ( int i = 0; i < (int) (header.length - 2); i++ ) {
functions.back().addBlockLine( tempBlockIterator, intBuffer[i] );
}
}
break;
default:
std::cerr << std::endl << std::endl
<< "ERROR - encountered unknown *.gcno tag : 0x"
<< std::hex << header.tag << std::dec << std::endl;
break;
}
return true;
}
int GcovData::readString( char* buffer, std::ifstream& gcovFile )
{
int length;
gcovFile.read( (char *) &length, sizeof( int ) );
if ( gcovFile.gcount() != sizeof( int ) ) {
std::cerr << "ERROR: Unable to read string length from gcov file"
<< std::endl;
return -1;
}
gcovFile.read( buffer, length * 4 );
if ( gcovFile.gcount() != length * 4 ) {
std::cerr << "ERROR: Unable to read string from gcov file"
<< std::endl;
return -1;
}
buffer[length * 4] = '\0';
return length +1;
}
int GcovData::readFrameHeader(
gcov_frame_header* header,
std::ifstream& gcovFile
)
{
int length;
length = sizeof( gcov_frame_header );
gcovFile.read( (char *) header, length );
if ( gcovFile.gcount() != length ) {
std::cerr << "ERROR: Unable to read frame header from gcov file"
<< std::endl;
return -1;
}
return length / 4;
}
int GcovData::readFilePreamble(
gcov_preamble* preamble,
std::ifstream& gcovFile,
uint32_t desiredMagic
)
{
rtems::utils::ostream_guard old_state( std::cerr );
// Read the gcov preamble and make sure it is the right length and has the
// magic number
gcovFile.read( (char *) preamble, sizeof( gcov_preamble ) );
if ( gcovFile.gcount() != sizeof( gcov_preamble ) ) {
std::cerr << "Error while reading file preamble" << std::endl;
return -1;
}
if ( preamble->magic != GCNO_MAGIC ) {
std::cerr << "File is not a valid *.gcno output (magic: 0x"
<< std::hex << std::setw( 4 ) << preamble->magic
<< ")" << std::endl;
return -1;
}
return sizeof( gcov_preamble ) / 4;
}
bool GcovData::readFunctionFrame(
gcov_frame_header header,
std::ifstream& gcovFile,
GcovFunctionData* function
)
{
char buffer[512];
char intBuffer[16384];
gcovFile.read( (char *) intBuffer, 8 );
if ( gcovFile.gcount() != 8 ) {
std::cerr << "ERROR: Unable to read Function ID & checksum" << std::endl;
return false;
}
header.length -= 2;
function->setId( intBuffer[0] );
function->setChecksum( intBuffer[1] );
header.length -= readString( buffer, gcovFile );
function->setFunctionName( buffer, symbolsToAnalyze_m );
header.length -= readString( buffer, gcovFile );
function->setFileName( buffer );
gcovFile.read( (char*) intBuffer, 4 * header.length );
if (gcovFile.gcount() != 4 * header.length ) {
std::cerr << "ERROR: Unable to read Function starting line number"
<< std::endl;
return false;
}
function->setFirstLineNumber( intBuffer[0] );
return true;
}
bool GcovData::writeReportFile()
{
functions_iterator_t currentFunction;
uint32_t i = 1; //iterator
std::ofstream textFile;
// Debug message
// std::cerr << "Writing file: " << textFileName << std::endl;
// Open the data file.
textFile.open( textFileName );
if ( !textFile.is_open() ) {
std::cerr << "Unable to create " << textFileName << std::endl;
return false;
}
printGcnoFileInfo( textFile );
for (
currentFunction = functions.begin();
currentFunction != functions.end();
currentFunction++
) {
(*currentFunction).printFunctionInfo( textFile, i );
(*currentFunction).printCoverageInfo( textFile, i );
i++;
}
return true;
}
void GcovData::printGcnoFileInfo( std::ofstream& textFile )
{
textFile << std::endl << "FILE:\t\t\t" << gcnoFileName
<< std::endl << std::hex
<< "magic:\t\t\t" << gcnoPreamble.magic << std::endl
<< "version:\t\t" << gcnoPreamble.version << std::endl
<< "timestamp:\t\t" << gcnoPreamble.timestamp << std::endl
<< std::dec
<< "functions found: \t"
<< std::endl << std::endl << gcnoPreamble.timestamp
<< std::endl << std::endl;
}
void GcovData::writeGcovFile()
{
//std::cerr << "Attempting to run gcov for: " << cFileName << std::endl;
std::ostringstream command;
command << "( cd " << rld::path::dirname( cFileName )
<< " && gcov " << rld::path::basename( cFileName )
<< " &>> gcov.log)";
//std::cerr << "> " << command << std::endl;
system( command.str().c_str() );
}
bool GcovData::processCounters()
{
functions_iterator_t currentFunction;
bool status = true;
for (
currentFunction = functions.begin();
currentFunction != functions.end();
currentFunction++
) {
if ( !(*currentFunction).processFunctionCounters() ) {
status = false;
}
}
return status;
}
}