/* * 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 #include #include #include //#include //#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( ) { char path[512]; char command[512]; //fprintf (stderr, "Attempting to run gcov for: %s\n", cFileName ); strcpy( path, cFileName ); dirname( path ); sprintf( command, "( cd %s && gcov %s &>> gcov.log)", path, basename( cFileName ) ); //fprintf (stderr, "> %s\n", command ); system( command ); } 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; } }