codeCoverage.c/* * A simple code coverage tool using DyninstAPI * * This tool uses DyninstAPI to instrument the functions and basic blocks in * an executable and its shared libraries in order to record code coverage * data when the executable is run. This code coverage data is output when the * rewritten executable finishes running. * * The intent of this tool is to demonstrate some capabilities of DyninstAPI; * it should serve as a good stepping stone to building a more feature-rich * code coverage tool on top of Dyninst. */#include #include #include #include using namespace std;// Command line parsing#include // DyninstAPI includes#include "BPatch.h"#include "BPatch_binaryEdit.h"#include "BPatch_flowGraph.h"#include "BPatch_function.h"#include "BPatch_point.h"using namespace Dyninst;static const char *USAGE = " [-bpsa]
libInst.C
/* * The instrumentation library for the codeCoverage tool. Provides * functions for initialization, registering functions and basic * blocks for coverage tracking, and outputting the results. */#include #include #include #include #include #include using namespace std;class bbRecord {public: string funcName; string modName; unsigned long address; unsigned long count; bbRecord() : funcName(""), modName(""), address(0), count(0) {}};class funcRecord {public: string funcName; string modName; unsigned long count; funcRecord() : funcName(""), modName(""), count(0) {}};// Used to records via qsortstatic int compareFuncRecordByName(const void *left, const void *right) { funcRecord *leftRecord = (funcRecord *)left; funcRecord *rightRecord = (funcRecord *)right; return leftRecord->funcName.compare(rightRecord->funcName);}static int compareFuncRecordByCount(const void *left, const void *right) { funcRecord *leftRecord = (funcRecord *)left; funcRecord *rightRecord = (funcRecord *)right; if( leftRecord->count < rightRecord->count ) return 1; if( leftRecord->count > rightRecord->count ) return -1; return 0;}static int compareBBRecordByName(const void *left, const void *right) { bbRecord *leftRecord = (bbRecord *)left; bbRecord *rightRecord = (bbRecord *)right; return leftRecord->funcName.compare(rightRecord->funcName);}static int compareBBRecordByCount(const void *left, const void *right) { bbRecord *leftRecord = (bbRecord *)left; bbRecord *rightRecord = (bbRecord *)right; if( leftRecord->count < rightRecord->count ) return 1; if( leftRecord->count > rightRecord->count ) return -1; return 0;}// For efficency in instrumentation, indexed by idstatic bbRecord *bbs;static funcRecord *funcs;int numFuncs = 0;int numBBs = 0;int enabled = 0;// Allocates space for all tracked functions and basic blocksvoid initCoverage(int totalFuncs, int totalBBs) { numFuncs = totalFuncs; numBBs = totalBBs; funcs = new funcRecord[numFuncs]; bbs = new bbRecord[numBBs]; enabled = 1;}// Populates a record for a functionvoid registerFunc(int id, char *name, char *modName) { if( !enabled ) return; funcs[id].funcName = name; funcs[id].modName = modName; funcs[id].count = 0;}// Populates a record for a basic blockvoid registerBB(int id, char *name, char *modName, unsigned long addr) { if( !enabled ) return; bbs[id].funcName = name; bbs[id].modName = modName; bbs[id].address = addr; bbs[id].count = 0;}// Should be called on function entry void incFuncCoverage(int id) { if( !enabled ) return; funcs[id].count++;}// Should be called on basic block entryvoid incBBCoverage(int id) { if( !enabled ) return; bbs[id].count++;}// Prints the code coverage stats. to standard out, also disables any more trackingvoid exitCoverage(int printAll, int printBasicBlocks, int sortAlphabetical) { if( !enabled ) return; printf("\n\n ************************** Code Coverage ************************* \n\n"); int count = 0; if( sortAlphabetical ) qsort(funcs, numFuncs, sizeof(funcRecord), &compareFuncRecordByName); else qsort(funcs, numFuncs, sizeof(funcRecord), &compareFuncRecordByCount); for(int i = 0; i < numFuncs; ++i) { if( funcs[i].count > 0 ) count++; if( printAll || (funcs[i].count > 0) ) printf(" %4lu : %s, %s\n", funcs[i].count, funcs[i].funcName.c_str(), funcs[i].modName.c_str()); } printf("\n ************** Code Coverage %d out of %d functions ************** \n\n", count, numFuncs); if (printBasicBlocks) { int bbCount = 0; printf("\n\n ************************** Basic Block Coverage ************************* \n\n"); if( sortAlphabetical ) qsort(bbs, numBBs, sizeof(bbRecord), &compareBBRecordByName); else qsort(bbs, numBBs, sizeof(bbRecord), &compareBBRecordByCount); string curFunc; string curMod; for(int i = 0; i < numBBs; ++i) { if( bbs[i].count > 0 ) bbCount++; else if( !printAll ) continue; if( curFunc != bbs[i].funcName || curMod != bbs[i].modName ) { curFunc = bbs[i].funcName; curMod = bbs[i].modName; printf(" (%s, %s)\n", bbs[i].funcName.c_str(), bbs[i].modName.c_str()); printf(" \t %4lu : 0x%-8lx\n", bbs[i].count, bbs[i].address); }else{ printf(" \t %4lu : 0x%-8lx\n", bbs[i].count, bbs[i].address); } } printf("\n ************** Basic Block Coverage %d out of %d blocks ************** \n\n", bbCount, numBBs); } enabled = 0;}
libtestcc.c
/* * A toy library to demonstrate the codeCoverage tool */#include "libtestcc.h"static int otherFunctionCalled = 0;static void otherFunction() { otherFunctionCalled = 1;}void libFooFunction(int callOtherFunction) { if( callOtherFunction ) otherFunction();}
libtestcc.h
/* * Header for libtestcc */#ifndef __LIB_TESTCC_H__#define __LIB_TESTCC_H__extern void libFooFunction(int callOtherFunction);#endif
Makefile
README
This directory contains a simple code coverage tool built with DyninstAPI. Thetool uses Dyninst to instrument every function in a program binary as well asoptionally instrumenting every basic block to record code coverage data. The goal of this tool is to demonstrate some of the capabilities of DyninstAPI.It does very little processing of the raw code coverage data, just some basicsorting when outputting the data. This tool should serve as the basis forcreating a more feature-rich code coverage tool using Dyninst.The presence of debugging information in the program binary is recommended toacquire the most useful information about code coverage, but it is notrequired. Without debugging information, source file information will not beavailable in the code coverage output.This tool makes use of an instrumentation library, libInst, to collect the codecoverage data. The tool adds this library as a dependency to the inputexecutable and also inserts calls into this library at function entries andbasic block entries.The provided Makefile can be used to build the program. The DYNINST_ROOTenvironment variable should be set to the directory where Dyninst wasbuilt/installed. This directory should contain an include directory with theDyninst headers and a lib directory with the Dyninst libraries. Also, make sureto set the LD_LIBRARY_PATH environment variable to include the librarydirectory and set the DYNINSTAPI_RT_LIB environment variable to${DYNINST_LIBRARY_DIRECTORY}/libdyninstAPI_RT.so.1Also included in this directory is a toy program that demonstrates a use of thecodeCoverage tool: testcc and libtestcc. They can be built with the providedMakefile.An example usage of the codeCoverage tool with this program follows.It is assumed that the following binaries have been built: codeCoverage,libtestcc, testcc. Also, this example assumes you are using csh/tcsh, butshould easily transfer to another shell.Before starting, the testcc program will not work unless the libtestcc.solibrary can be found by the dynamic linker. Among other solutions, thiscommand should make testcc execute successfully.% setenv LD_LIBRARY_PATH ${LD_LIBRARY_PATH}:.First, execute codeCoverage without any arguments to see the usage.% ./codeCoverageInput binary not specified.Usage: ./codeCoverage [-bpsa]