/*
* TODO: use strings instead of cstrings for reliability and saving memory
* TODO: use global buffers
*
*/
/*! @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"
namespace Gcov {
GcovData::GcovData()
{
numberOfFunctions = 0;
}
GcovData::~GcovData()
{
}
bool GcovData::readGcnoFile( const char* const fileName )
{
int status;
FILE* gcovFile;
char* tempString;
char* tempString2;
char* tempString3;
if ( strlen(fileName) >= FILE_NAME_LENGTH ){
fprintf(
stderr,
"ERROR: File name is too long to be correctly stored: %u\n",
(unsigned int) strlen(fileName)
);
return false;
}
strcpy( gcnoFileName, fileName );
strcpy( gcdaFileName, fileName );
strcpy( textFileName, fileName );
strcpy( cFileName, fileName );
tempString = strstr( gcdaFileName,".gcno" );
tempString2 = strstr( textFileName,".gcno" );
tempString3 = strstr( cFileName,".gcno" );
if ( (tempString == NULL) && (tempString2 == NULL) ){
fprintf(stderr, "ERROR: incorrect name of *.gcno file\n");
}
else
{
strcpy( tempString, ".gcda"); // construct gcda file name
strcpy( tempString2, ".txt"); // construct report file name
strcpy( tempString3, ".c"); // construct source file name
}
// Debug message
// fprintf( stderr, "Readning file: %s\n", gcnoFileName);
// Open the notes file.
gcovFile = fopen( gcnoFileName, "r" );
if ( !gcovFile ) {
fprintf( stderr, "Unable to open %s\n", gcnoFileName );
return false;
}
// Read and validate the gcnoPreamble (magic, version, timestamp) from the file
status = readFilePreamble( &gcnoPreamble, gcovFile, GCNO_MAGIC );
if ( status <= 0 ){
fprintf( stderr, "Unable to read %s\n", gcnoFileName );
fclose( gcovFile );
return false;
}
//Read all remaining frames from file
while( readFrame(gcovFile) ){}
fclose( gcovFile );
return true;
}
bool GcovData::writeGcdaFile ()
{
gcov_preamble preamble;
gcov_frame_header header;
FILE* 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;
size_t status;
// Debug message
// fprintf( stderr, "Writing file: %s\n", gcdaFileName);
// Lets clear counters sumators
countersSum = 0;
countersMax = 0;
countersFoundSum = 0;
// Open the data file.
gcdaFile = fopen( gcdaFileName, "w" );
if ( !gcdaFile ) {
fprintf( stderr, "Unable to create %s\n", gcdaFileName );
return false;
}
//Form preamble
preamble.magic = GCDA_MAGIC;
preamble.version = gcnoPreamble.version;
preamble.timestamp = gcnoPreamble.timestamp;
//Write preamble
status = fwrite (&preamble , sizeof( preamble ), 1 , gcdaFile );
if ( status != 1 )
fprintf( stderr, "Error while writing gcda preamble to a file %s\n", gcdaFileName );
//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;
status = fwrite (&header, sizeof(header), 1, gcdaFile );
if ( status != 1 )
fprintf( stderr, "Error while writing function announcement to a file %s\n", gcdaFileName );
//Write function id
buffer = (*currentFunction)->getId();
status = fwrite (&buffer, sizeof( buffer ), 1, gcdaFile );
if ( status != 1 )
fprintf( stderr, "Error while writing function id to a file %s\n", gcdaFileName );
//Write function checksum
buffer = (*currentFunction)->getChecksum();
status = fwrite (&buffer, sizeof( buffer ), 1, gcdaFile );
if ( status != 1 )
fprintf( stderr, "Error while writing function checksum to a file %s\n", gcdaFileName );
// 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;
status = fwrite (&header, sizeof( header ), 1, gcdaFile );
if ( status != 1 )
fprintf( stderr, "Error while writing counter header to a file %s\n", gcdaFileName );
status = fwrite (llBuffer, sizeof( uint64_t ), countersFound , gcdaFile );
if ( status != countersFound )
fprintf( stderr, "Error while writing counter data to a file %s\n", gcdaFileName );
}
// 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
status = fwrite (&header, sizeof( header ), 1, gcdaFile );
if ( status != 1 )
fprintf( stderr, "Error while writing stats header to a file %s\n", gcdaFileName );
status = fwrite (&objectStats, sizeof( objectStats ), 1, gcdaFile );
if ( status != 1 )
fprintf( stderr, "Error while writing object stats to a file %s\n", gcdaFileName );
// 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
status = fwrite (&header, sizeof( header ), 1, gcdaFile );
if ( status != 1 )
fprintf( stderr, "Error while writing stats header to a file %s\n", gcdaFileName );
status = fwrite (&programStats, sizeof( programStats ), 1, gcdaFile );
if ( status != 1 )
fprintf( stderr, "Error while writing program stats to a file %s\n", gcdaFileName );
fclose( gcdaFile );
return true;
}
bool GcovData::readFrame(
FILE* gcovFile
)
{
gcov_frame_header header;
char buffer[512];
uint32_t intBuffer[4096];
uint32_t tempBlockId;
blocks_iterator_t tempBlockIterator;
int status;
GcovFunctionData* newFunction;
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++;
newFunction = new GcovFunctionData;
if ( !readFunctionFrame(header, gcovFile, newFunction) ){
fprintf( stderr, "Error while reading FUNCTION from gcov file...\n" );
return false;
}
functions.push_back(newFunction);
break;
case GCOV_TAG_BLOCKS:
status = fread( &intBuffer, 4, header.length, gcovFile );
if ( status != (int) header.length){
fprintf(
stderr, "Error while reading BLOCKS from gcov file...\n"
"Header lenght is %u instead of %u\n",
header.length,
status
);
return false;
}
for( uint32_t i = 0; i < header.length; i++ )
functions.back()->addBlock(i, intBuffer[i], "");
break;
case GCOV_TAG_ARCS:
status = fread( &intBuffer, 4, header.length, gcovFile );
if (status != (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:
status = fread( &intBuffer, 4, 2, gcovFile );
if (status != 2 || intBuffer[1] != 0){
fprintf(
stderr,
"Error while reading block id for LINES from gcov file..."
);
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 );
status = fread( &intBuffer, 4, header.length, gcovFile );
if (status != (int) header.length){
fprintf( stderr, "Error while reading LINES from gcov file..." );
return false;
}
else
for (int i = 0; i < (int) (header.length - 2); i++)
functions.back()->addBlockLine( tempBlockIterator, intBuffer[i] );
break;
default:
fprintf( stderr, "\n\nERROR - encountered unknown *.gcno tag : 0x%x\n", header.tag );
break;
}
return true;
}
int GcovData::readString(
char* buffer, //TODO: use global buffer here
FILE* gcovFile
)
{
int status;
int length;
status = fread( &length, sizeof(int), 1, gcovFile );
if (status != 1){
fprintf( stderr, "ERROR: Unable to read string length from gcov file\n" );
return -1;
}
status = fread( buffer, length * 4 , 1, gcovFile );
if (status != 1){
fprintf( stderr, "ERROR: Unable to read string from gcov file\n" );
return -1;
}
buffer[length * 4] = '\0';
return length +1;
}
int GcovData::readFrameHeader(
gcov_frame_header* header,
FILE* gcovFile
)
{
int status;
int length;
length = sizeof(gcov_frame_header);
status = fread( header, length, 1, gcovFile );
if (status != 1){
//fprintf( stderr, "ERROR: Unable to read frame header from gcov file\n" );
return -1;
}
return length / 4;
}
int GcovData::readFilePreamble(
gcov_preamble* preamble,
FILE* gcovFile,
uint32_t desiredMagic
)
{
int status;
int length;
length = sizeof( gcov_preamble );
status = fread( preamble, sizeof( gcov_preamble), 1, gcovFile );
if (status <= 0) {
fprintf( stderr, "Error while reading file preamble\n" );
return -1;
}
if ( preamble->magic != GCNO_MAGIC ) {
fprintf( stderr, "File is not a valid *.gcno output (magic: 0x%4x)\n", preamble->magic );
return -1;
}
return length / 4;
}
bool GcovData::readFunctionFrame(
gcov_frame_header header,
FILE* gcovFile,
GcovFunctionData* function
)
{
char buffer[512]; //TODO: use common buffers
uint32_t intBuffer[4096];
int status;
status = fread( &intBuffer, 8, 1, gcovFile );
if (status != 1){
fprintf( stderr, "ERROR: Unable to read Function ID & checksum\n" );
return false;
}
header.length -= 2;
function->setId( intBuffer[0] );
function->setChecksum( intBuffer[1] );
header.length -= readString( buffer, gcovFile );
function->setFunctionName( buffer );
header.length -= readString( buffer, gcovFile );
function->setFileName( buffer );
status = fread( &intBuffer, 4, header.length, gcovFile );
if (status <= 0){
fprintf( stderr, "ERROR: Unable to read Function starting line number\n" );
return false;
}
function->setFirstLineNumber( intBuffer[0] );
return true;
}
bool GcovData::writeReportFile()
{
functions_iterator_t currentFunction;
uint32_t i = 1; //iterator
FILE* textFile;
// Debug message
// fprintf( stderr, "Writing file: %s\n", textFileName);
// Open the data file.
textFile = fopen( textFileName, "w" );
if ( !textFile ) {
fprintf( stderr, "Unable to create %s\n", textFileName );
return false;
}
printGcnoFileInfo( textFile );
for (
currentFunction = functions.begin();
currentFunction != functions.end();
currentFunction++
)
{
(*currentFunction)->printFunctionInfo( textFile, i );
(*currentFunction)->printCoverageInfo( textFile, i );
i++;
}
fclose ( textFile );
return true;
}
void GcovData::printGcnoFileInfo( FILE * textFile )
{
fprintf(
textFile,
"\nFILE:\t\t\t%s\n"
"magic:\t\t\t%x\n"
"version:\t\t%x\n"
"timestamp:\t\t%x\n"
"functions found: \t%u\n\n",
gcnoFileName,
gcnoPreamble.magic,
gcnoPreamble.version,
gcnoPreamble.timestamp,
numberOfFunctions
);
}
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;
}
}