/*! @file GcovFunctionData.cc
* @brief GcovFunctionData Implementation
*
* This file contains the implementation of the class storing information
* about single function.
*/
#include <cstdio>
#include <cstring>
#include <cinttypes>
#include "GcovFunctionData.h"
#include "ObjdumpProcessor.h"
#include "CoverageMapBase.h"
#include "DesiredSymbols.h"
#include "rtems-utils.h"
namespace Gcov {
GcovFunctionData::GcovFunctionData()
{
id = 0;
checksum = 0;
firstLineNumber = 0;
numberOfBlocks = 0;
numberOfArcs = 0;
coverageMap = NULL;
symbolInfo = NULL;
}
GcovFunctionData::~GcovFunctionData()
{
}
void GcovFunctionData::setChecksum( const uint32_t chk )
{
checksum = chk;
}
void GcovFunctionData::setId( const uint32_t idNumber )
{
id = idNumber;
}
void GcovFunctionData::setFirstLineNumber( const uint32_t lineNo )
{
firstLineNumber = lineNo;
}
bool GcovFunctionData::setFunctionName(
const std::string& fcnName,
Coverage::DesiredSymbols& symbolsToAnalyze
)
{
std::string symbolName;
symbolName = fcnName;
if ( fcnName.length() >= FUNCTION_NAME_LENGTH ) {
std::cerr << "ERROR: Function name is too long to be correctly stored: "
<< fcnName.length() << std::endl;
return false;
}
functionName = fcnName;
// Tie function to its coverage map
symbolInfo = symbolsToAnalyze.find( symbolName );
if ( symbolInfo != NULL ) {
coverageMap = symbolInfo->unifiedCoverageMap;
}
#if 0
if ( coverageMap == NULL ) {
std::cerr << "ERROR: Could not find coverage map for: " << symbolName
<< std::endl;
} else {
std::cerr << "SUCCESS: Found coverage map for: " << symbolName
<< std::endl;
}
#endif
return true;
}
bool GcovFunctionData::setFileName( const std::string& fileName ) {
if ( fileName.length() >= FILE_NAME_LENGTH ) {
std::cerr << "ERROR: File name is too long to be correctly stored: "
<< fileName.length() << std::endl;
return false;
}
sourceFileName = fileName;
return true;
}
arcs_t GcovFunctionData::getArcs() const
{
return arcs;
}
uint32_t GcovFunctionData::getChecksum() const
{
return checksum;
}
uint32_t GcovFunctionData::getId() const
{
return id;
}
void GcovFunctionData::getCounters(
uint64_t* counterValues,
uint32_t& countersFound,
uint64_t& countersSum,
uint64_t& countersMax
)
{
arcs_iterator_t currentArc;
int i;
countersFound = 0;
countersSum = 0;
countersMax = 0;
// Locate relevant counters and copy their values
i = 0;
for ( currentArc = arcs.begin(); currentArc != arcs.end(); currentArc++ ) {
if (
currentArc->flags == 0 ||
currentArc->flags == 2 ||
currentArc->flags == 4
) {
countersFound++;
countersSum += currentArc->counter;
counterValues[i] = currentArc->counter;
if ( countersMax <= currentArc->counter ) {
countersMax = currentArc->counter;
}
i++;
}
}
}
blocks_t GcovFunctionData::getBlocks() const
{
return blocks;
}
void GcovFunctionData::addArc(
uint32_t source,
uint32_t destination,
uint32_t flags
)
{
gcov_arc_info arc;
numberOfArcs++;
arc.sourceBlock = source;
arc.destinationBlock = destination;
arc.flags = flags;
arc.counter = 0;
arcs.push_back( arc );
}
void GcovFunctionData::addBlock(
const uint32_t id,
const uint32_t flags,
const std::string& sourceFileName
)
{
gcov_block_info block;
numberOfBlocks++;
block.id = id;
block.flags = flags;
block.numberOfLines = 0;
block.counter = 0;
block.sourceFileName = sourceFileName;
blocks.push_back( block );
}
void GcovFunctionData::printFunctionInfo(
std::ofstream& textFile,
uint32_t function_number
)
{
blocks_iterator_t currentBlock;
arcs_iterator_t currentArc;
textFile << std::endl << std::endl
<< "=========================="
<< "FUNCTION " << std::setw( 3 ) << function_number
<< "=========================="
<< std::endl << std::endl
<< "Name: " << functionName << std::endl
<< "File: " << sourceFileName << std::endl
<< "Line: " << firstLineNumber << std::endl
<< "Id: " << id << std::endl
<< "Checksum: 0x" << std::hex << checksum << std::dec
<< std::endl << std::endl;
// Print arcs info
for ( currentArc = arcs.begin(); currentArc != arcs.end(); currentArc++ ) {
printArcInfo( textFile, currentArc );
}
textFile << std::endl;
// Print blocks info
for (
currentBlock = blocks.begin();
currentBlock != blocks.end();
currentBlock++
) {
printBlockInfo( textFile, currentBlock );
}
}
void GcovFunctionData::printCoverageInfo(
std::ofstream& textFile,
uint32_t function_number
)
{
uint32_t baseAddress = 0;
uint32_t baseSize;
uint32_t currentAddress;
std::list<Coverage::objdumpLine_t>::iterator instruction;
if ( coverageMap != NULL ) {
for (
instruction = symbolInfo->instructions.begin();
instruction != symbolInfo->instructions.end();
instruction++
) {
if( instruction->isInstruction ) {
baseAddress = instruction->address;
break;
}
}
baseSize = coverageMap->getSize();
textFile << std::endl << "Instructions (Base address: 0x"
<< std::setfill( '0' ) << std::setw( 8 )
<< std::hex << baseAddress << std::dec << std::setfill( ' ' )
<< ", Size: " << std::setw( 4 ) << baseSize
<< "):" << std::endl << std::endl;
for (
instruction = symbolInfo->instructions.begin();
instruction != symbolInfo->instructions.end();
instruction++
)
{
if ( instruction->isInstruction ) {
currentAddress = instruction->address - baseAddress;
textFile << std::left << "0x" << std::setw( 70 )
<< instruction->line.c_str() << " " << std::right
<< "| 0x" << std::hex << std::setfill( '0' )
<< std::setw( 8 ) << currentAddress << " "
<< std::dec << std::setfill( ' ' )
<< "*| exec: " << std::setw( 4 )
<< coverageMap->getWasExecuted( currentAddress )
<< " | taken/not: " << std::setw( 4 )
<< coverageMap->getWasTaken( currentAddress )
<< "/" << std::setw( 4 )
<< coverageMap->getWasNotTaken( currentAddress )
<< " ";
if ( instruction->isBranch )
textFile << "| Branch ";
else
textFile << " ";
if ( instruction->isNop )
textFile << "| NOP(" << std::setw( 3 ) << instruction->nopSize
<< ") " << std::endl;
else
textFile << " " << std::endl;
}
}
}
}
void GcovFunctionData::setBlockFileName(
const blocks_iterator_t block,
const std::string& fileName
)
{
block->sourceFileName = fileName;
}
void GcovFunctionData::addBlockLine(
const blocks_iterator_t block,
const uint32_t line
)
{
block->lines.push_back( line );
(block->numberOfLines)++;
}
blocks_iterator_t GcovFunctionData::findBlockById( const uint32_t id )
{
blocks_iterator_t blockIterator;
if ( !blocks.empty() ) {
blockIterator = blocks.begin();
while ( blockIterator != blocks.end() ) {
if ( blockIterator->id == id) {
break;
}
blockIterator++;
}
} else {
std::cerr << "ERROR: GcovFunctionData::findBlockById() failed"
<< ", no blocks present" << std::endl;
}
return blockIterator;
}
void GcovFunctionData::printArcInfo(
std::ofstream& textFile,
arcs_iterator_t arc
)
{
textFile << " > ARC " << std::setw( 3 ) << arc->sourceBlock << " -> "
<< arc->destinationBlock << " ";
textFile << "\tFLAGS: ";
switch ( arc->flags ) {
case 0:
textFile << "( ___________ ____ _______ )";
break;
case 1:
textFile << "( ___________ ____ ON_TREE )";
break;
case 2:
textFile << "( ___________ FAKE _______ )";
break;
case 3:
textFile << "( ___________ FAKE ON_TREE )";
break;
case 4:
textFile << "( FALLTHROUGH ____ _______ )";
break;
case 5:
textFile << "( FALLTHROUGH ____ ON_TREE )";
break;
default:
textFile << "( =======FLAGS_ERROR====== )";
std::cerr << " ERROR: Unknown arc flag: 0x"
<< std::hex << arcs.back().flags << std::endl
<< std::dec;
break;
}
textFile << "\tTaken: " << std::setw( 5 ) << arc->counter << std::endl;
}
void GcovFunctionData::printBlockInfo(
std::ofstream& textFile,
blocks_iterator_t block
)
{
std::list<uint32_t>::iterator line;
rtems::utils::ostream_guard old_state( textFile );
textFile << " > BLOCK " << std::setw( 3 ) << block->id
<< " from " << block->sourceFileName << std::endl
<< " -counter: " << std::setw( 5 ) << block->counter
<< std::endl
<< " -flags: 0x" << std::hex << block->flags << std::endl
<< " -lines: ";
if ( !block->lines.empty() )
for ( line = block->lines.begin(); line != block->lines.end(); line++ ) {
textFile << *line << ", ";
}
textFile << std::endl;
}
bool GcovFunctionData::processFunctionCounters() {
uint32_t baseAddress = 0;
uint32_t currentAddress = 0;
blocks_iterator_t blockIterator;
blocks_iterator_t blockIterator2;
arcs_iterator_t arcIterator;
arcs_iterator_t arcIterator2;
std::list<uint64_t> taken; // List of taken counts for branches
std::list<uint64_t> notTaken; // List of not taken counts for branches
std::list<Coverage::objdumpLine_t>::iterator instruction;
//std::cerr << "DEBUG: Processing counters for file: " << sourceFileName
// << std::endl;
if (
blocks.empty() ||
arcs.empty() ||
coverageMap == NULL ||
symbolInfo->instructions.empty()
) {
//std::cerr << "DEBUG: sanity check returned false for function: "
// << functionName << " from file: " << sourceFileName
// << std::endl;
return false;
}
// Reset iterators and variables
blockIterator = blocks.begin();
arcIterator = arcs.begin();
arcIterator2 = arcIterator;
arcIterator2++;
instruction = symbolInfo->instructions.begin();
baseAddress = coverageMap->getFirstLowAddress(); //symbolInfo->baseAddress;
currentAddress = baseAddress;
// Find taken/not taken values for branches
if ( !processBranches( &taken , ¬Taken ) )
{
//std::cerr << "ERROR: Failed to process branches for function: "
// << functionName << " from file: " << sourceFileName
// << std::endl;
return false;
};
// Process the branching arcs
while ( blockIterator != blocks.end() ) {
//std::cerr << "DEBUG: Processing branches" << std::endl;
while ( arcIterator->sourceBlock != blockIterator->id ) {
if ( arcIterator == arcs.end() ) {
//std::cerr << "ERROR: Unexpectedly runned out of arcs to analyze"
// << std::endl;
return false;
}
arcIterator++;
arcIterator2++;
}
// If no more branches break;
if ( arcIterator2 == arcs.end() ) {
break;
}
// If this is a branch without FAKE arcs process it
if (
( arcIterator->sourceBlock == arcIterator2->sourceBlock ) &&
!( arcIterator->flags & FAKE_ARC_FLAG ) &&
!( arcIterator2->flags & FAKE_ARC_FLAG )
) {
if ( taken.empty() || notTaken.empty() ) {
std::cerr << "ERROR: Branches missing for function: "
<< functionName << " from file: " << sourceFileName
<< std::endl;
return false;
}
//std::cerr << "DEBUG: Found true branching arc "
// << std::setw( 3 ) << arcIterator->sourceBlock << " -> "
// << std::setw( 3 ) << arcIteratior->destinationBlock
// << std::endl;
if ( arcIterator->flags & FALLTHROUGH_ARC_FLAG ) {
arcIterator->counter = notTaken.front();
notTaken.pop_front();
arcIterator2->counter = taken.front();
taken.pop_front();
} else {
arcIterator2->counter = notTaken.front();
notTaken.pop_front();
arcIterator->counter = taken.front();
taken.pop_front();
}
blockIterator2 = blocks.begin();
//TODO: ADD FAILSAFE
while ( arcIterator->destinationBlock != blockIterator2->id ) {
blockIterator2++;
}
blockIterator2->counter += arcIterator->counter;
blockIterator2 = blocks.begin();
//TODO: ADD FAILSAFE
while ( arcIterator2->destinationBlock != blockIterator2->id ) {
blockIterator2++;
}
blockIterator2->counter += arcIterator2->counter;
}
blockIterator++;
}
// Reset iterators and variables
blockIterator = blocks.begin();
arcIterator = arcs.begin();
arcIterator2 = arcIterator;
arcIterator2++;
// Set the first block
blockIterator->counter = coverageMap->getWasExecuted( currentAddress );
// Analyze remaining arcs and blocks
while ( blockIterator != blocks.end() ) {
while ( arcIterator->sourceBlock != blockIterator->id ) {
if ( arcIterator == arcs.end() ) {
std::cerr << "ERROR: Unexpectedly runned out of arcs to analyze"
<< std::endl;
return false;
}
arcIterator++;
arcIterator2++;
}
// If this is the last arc, propagate counter and exit
if ( arcIterator2 == arcs.end() ) {
//std::cerr << "DEBUG: Found last arc " << std::setw( 3 )
// << arcIterator->sourceBlock << " -> " << std::setw( 3 )
// << arcIterator->destinationBlock << std::endl;
arcIterator->counter = blockIterator->counter;
blockIterator2 = blocks.begin();
while ( arcIterator->destinationBlock != blockIterator2->id ) { //TODO: ADD FAILSAFE
blockIterator2++;
}
blockIterator2->counter += arcIterator->counter;
return true;
}
// If this is not a branch, propagate counter and continue
if ( arcIterator->sourceBlock != arcIterator2->sourceBlock ) {
//std::cerr << "DEBUG: Found simple arc " << std::setw( 3 )
// << arcIterator->sourceBlock << " -> " << std::setw( 3 )
// << arcIterator->destinationBlock << std::endl;
arcIterator->counter = blockIterator->counter;
blockIterator2 = blocks.begin();
while ( arcIterator->destinationBlock != blockIterator2->id ) { //TODO: ADD FAILSAFE
blockIterator2++;
}
blockIterator2->counter += arcIterator->counter;
}
// If this is a branch with FAKE arc
else if (
( arcIterator->sourceBlock == arcIterator2->sourceBlock ) &&
( arcIterator2->flags & FAKE_ARC_FLAG )
) {
//std::cerr << "DEBUG: Found fake branching arc " << std::setw( 3 )
// << arcIterator->sourceBlock << " -> " << std::setw( 3 )
// << arcIterator->destinationBlock << std::endl;
arcIterator->counter = blockIterator->counter;
blockIterator2 = blocks.begin();
while ( arcIterator->destinationBlock != blockIterator2->id ) { //TODO: ADD FAILSAFE
blockIterator2++;
}
blockIterator2->counter += arcIterator->counter;
}
// If this is a legitimate branch
blockIterator++;
}
return true;
}
bool GcovFunctionData::processBranches(
std::list<uint64_t>* taken ,
std::list<uint64_t>* notTaken
)
{
uint32_t baseAddress = 0;
uint32_t currentAddress;
std::list<Coverage::objdumpLine_t>::iterator instruction;
if ( coverageMap == NULL ) {
return false;
}
//baseAddress = coverageMap->getFirstLowAddress(); //symbolInfo->baseAddress;
for (
instruction = symbolInfo->instructions.begin();
instruction != symbolInfo->instructions.end();
instruction++
) {
if( instruction->isInstruction ) {
baseAddress = instruction->address;
break;
}
}
// std::cerr << "DEBUG: Processing instructions in search of branches"
// << std::endl;
for (
instruction = symbolInfo->instructions.begin();
instruction != symbolInfo->instructions.end();
instruction++
) {
if ( instruction->isInstruction ) {
currentAddress = instruction-> address - baseAddress;
if ( instruction->isBranch ) {
taken->push_back(
(uint64_t) coverageMap->getWasTaken( currentAddress )
);
notTaken->push_back(
(uint64_t) coverageMap->getWasNotTaken( currentAddress )
);
//std::cerr << "Added branch to list taken/not: " << std::setw( 4 )
// << coverageMap->getWasTaken( currentAddress )
// << "/" << std::setw( 4 )
// << coverageMap->getWasNotTaken( currentAddress )
// << std::endl;
}
}
}
return true;
}
}