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] 
 
\n \                            -b: Basic block level code coverage\n \                            -p: Print all functions (including functions that are never executed)\n \                            -s: Instrument shared libraries also\n \                            -a: Sort results alphabetically by function name\n";static const char *OPT_STR = "bpsa";// configuration optionschar *inBinary = NULL;char *outBinary = NULL;bool includeSharedLib = false;int printAll = 0;bool bbCoverage = false;int alphabetical = 0;set < string > skipLibraries;void initSkipLibraries (){    /* List of shared libraries to skip instrumenting */    /* Do not instrument the instrumentation library */    skipLibraries.insert ("libInst.so");    skipLibraries.insert ("libc.so.6");    skipLibraries.insert ("libc.so.7");    skipLibraries.insert ("ld-2.5.so");    skipLibraries.insert ("ld-linux.so.2");    skipLibraries.insert ("ld-lsb.so.3");    skipLibraries.insert ("ld-linux-x86-64.so.2");    skipLibraries.insert ("ld-lsb-x86-64.so");    skipLibraries.insert ("ld-elf.so.1");    skipLibraries.insert ("ld-elf32.so.1");    skipLibraries.insert ("libstdc++.so.6");    return;}bool parseArgs (int argc, char *argv[]){    int c;    while ((c = getopt (argc, argv, OPT_STR)) != -1) {        switch ((char) c) {            case 'b':                bbCoverage = true;                break;            case 'p':                printAll = 1;                break;            case 's':                /* if includeSharedLib is set,                 * all libraries linked to the binary will also be instrumented */                includeSharedLib = true;                break;            case 'a':                alphabetical = 1;                break;            default:                cerr << "Usage: " << argv[0] << USAGE;                return false;        }    }    int endArgs = optind;    if (endArgs >= argc) {        cerr << "Input binary not specified." << endl            << "Usage: " << argv[0] << USAGE;        return false;    }    /* Input Binary */    inBinary = argv[endArgs];    endArgs++;    if (endArgs >= argc) {        cerr << "Output binary not specified." << endl            << "Usage: " << argv[0] << USAGE;        return false;    }    /* Rewritten Binary */    outBinary = argv[endArgs];    return true;}BPatch_function *findFuncByName (BPatch_p_w_picpath * appImage, char *funcName){    /* fundFunctions returns a list of all functions with the name 'funcName' in the binary */    BPatch_Vector < BPatch_function * >funcs;    if (NULL == appImage->findFunction (funcName, funcs) || !funcs.size ()            || NULL == funcs[0]) {        cerr << "Failed to find " << funcName <<            " function in the instrumentation library" << endl;        return NULL;    }    return funcs[0];}bool insertFuncEntry (BPatch_binaryEdit * appBin, BPatch_function * curFunc,        char *funcName, BPatch_function * instIncFunc,        int funcId){    /* Find the instrumentation points */    vector < BPatch_point * >*funcEntry = curFunc->findPoint (BPatch_entry);    if (NULL == funcEntry) {        cerr << "Failed to find entry for function " << funcName << endl;        return false;    }    cout << "Inserting instrumention at function entry of " << funcName << endl;    /* Create a vector of arguments to the function     * incCoverage function takes the function name as argument */    BPatch_Vector < BPatch_snippet * >instArgs;    BPatch_constExpr id (funcId);    instArgs.push_back (&id);    BPatch_funcCallExpr instIncExpr (*instIncFunc, instArgs);    /* Insert the snippet at function entry */    BPatchSnippetHandle *handle =        appBin->insertSnippet (instIncExpr, *funcEntry, BPatch_callBefore,                BPatch_lastSnippet);    if (!handle) {        cerr << "Failed to insert instrumention at function entry of " << funcName            << endl;        return false;    }    return true;}bool insertBBEntry (BPatch_binaryEdit * appBin, BPatch_function * curFunc,        char *funcName, const char *moduleName,        BPatch_function * instBBIncFunc,        BPatch_function * registerBB, int *bbIndex,        BPatch_Vector < BPatch_snippet * >*registerCalls){    BPatch_flowGraph *appCFG = curFunc->getCFG ();    BPatch_Set < BPatch_basicBlock * >allBlocks;    BPatch_Set < BPatch_basicBlock * >::iterator iter;    if (!appCFG) {        cerr << "Failed to find CFG for function " << funcName << endl;        return EXIT_FAILURE;    }    if (!appCFG->getAllBasicBlocks (allBlocks)) {        cerr << "Failed to find basic blocks for function " << funcName << endl;        return EXIT_FAILURE;    } else if (allBlocks.size () == 0) {        cerr << "No basic blocks for function " << funcName << endl;        return EXIT_FAILURE;    }    /* Instrument the entry of every basic block */    for (iter = allBlocks.begin (); iter != allBlocks.end (); iter++) {        unsigned long address = (*iter)->getStartAddress ();        cout << "Instrumenting Basic Block 0x" << hex << address << " of " <<            funcName << endl;        BPatch_Vector < BPatch_snippet * >instArgs;        BPatch_constExpr bbId (*bbIndex);        instArgs.push_back (&bbId);        BPatch_point *bbEntry = (*iter)->findEntryPoint ();        if (NULL == bbEntry) {            cerr << "Failed to find entry for basic block at 0x" << hex << address                << endl;            return false;        }        BPatch_funcCallExpr instIncExpr (*instBBIncFunc, instArgs);        BPatchSnippetHandle *handle =            appBin->insertSnippet (instIncExpr, *bbEntry, BPatch_callBefore,                    BPatch_lastSnippet);        if (!handle) {            cerr << "Failed to insert instrumention in basic block at 0x" << hex <<                address << endl;            return false;        }        /* Create a call to the registration function for this basic block */        BPatch_Vector < BPatch_snippet * >regArgs;        BPatch_constExpr bbIdReg (*bbIndex);        regArgs.push_back (&bbIdReg);        BPatch_constExpr coverageFunc (funcName);        regArgs.push_back (&coverageFunc);        BPatch_constExpr coverageModule (moduleName);        regArgs.push_back (&coverageModule);        BPatch_constExpr addrArg (address);        regArgs.push_back (&addrArg);        BPatch_funcCallExpr *regCall =            new BPatch_funcCallExpr (*registerBB, regArgs);        registerCalls->push_back (regCall);        (*bbIndex)++;    }    return true;}int main (int argc, char *argv[]){    if (!parseArgs (argc, argv))        return EXIT_FAILURE;    /* Initialize list of libraries that should not be instrumented - relevant only if includeSharedLib is true */    initSkipLibraries ();    /* Every Dyninst mutator needs to declare one instance of BPatch */    BPatch bpatch;    /* Open the specified binary for binary rewriting.      * When the second parameter is set to true, all the library dependencies      * as well as the binary are opened */    BPatch_binaryEdit *appBin = bpatch.openBinary (inBinary, true);    if (appBin == NULL) {        cerr << "Failed to open binary" << endl;        return EXIT_FAILURE;    }    /* Open the instrumentation library.     * loadLibrary loads the instrumentation library into the binary p_w_picpath and     * adds it as a new dynamic dependency in the rewritten library */    const char *instLibrary = "./libInst.so";    if (!appBin->loadLibrary (instLibrary)) {        cerr << "Failed to open instrumentation library" << endl;        return EXIT_FAILURE;    }    BPatch_p_w_picpath *appImage = appBin->getImage ();    /* Find code coverage functions in the instrumentation library */    BPatch_function *instInitFunc =        findFuncByName (appImage, (char *) "initCoverage");    BPatch_function *registerFunc =        findFuncByName (appImage, (char *) "registerFunc");    BPatch_function *instIncFunc =        findFuncByName (appImage, (char *) "incFuncCoverage");    BPatch_function *registerBB =        findFuncByName (appImage, (char *) "registerBB");    BPatch_function *instBBIncFunc =        findFuncByName (appImage, (char *) "incBBCoverage");    BPatch_function *instExitFunc =        findFuncByName (appImage, (char *) "exitCoverage");    if (!instInitFunc || !instIncFunc || !instExitFunc || !instBBIncFunc            || !registerFunc || !registerBB) {        return EXIT_FAILURE;    }    /* To instrument every function in the binary     * --> iterate over all the modules in the binary      * --> iterate over all functions in each modules */    vector < BPatch_module * >*modules = appImage->getModules ();    vector < BPatch_module * >::iterator moduleIter;    BPatch_module *defaultModule;    BPatch_Vector < BPatch_snippet * >registerCalls;    int bbIndex = 0;    int funcIndex = 0;    for (moduleIter = modules->begin (); moduleIter != modules->end ();            ++moduleIter) {        char moduleName[1024];        (*moduleIter)->getName (moduleName, 1024);        /* if includeSharedLib is not set, skip instrumenting dependent libraries */        if ((*moduleIter)->isSharedLib ()) {            if (!includeSharedLib                    || skipLibraries.find (moduleName) != skipLibraries.end ()) {                cout << "Skipping library: " << moduleName << endl;                continue;            }        }        /* Every binary has one default module.         * code coverage initialize and finalize functions should be called only once.         * Hence call them from the default module */        if (string (moduleName).find ("DEFAULT_MODULE") != string::npos) {            defaultModule = (*moduleIter);        }        cout << "Instrumenting module: " << moduleName << endl;        vector < BPatch_function * >*allFunctions =            (*moduleIter)->getProcedures ();        vector < BPatch_function * >::iterator funcIter;        /* Insert snippets at the entry of every function */        for (funcIter = allFunctions->begin (); funcIter != allFunctions->end ();                ++funcIter) {            BPatch_function *curFunc = *funcIter;            char funcName[1024];            curFunc->getName (funcName, 1024);            /*             * Replace DEFAULT_MODULE with the name of the input binary in the output              */            string passedModName (moduleName);            if (passedModName.find ("DEFAULT_MODULE") != string::npos) {                // Strip the directory                passedModName = inBinary;                passedModName =                    passedModName.substr (passedModName.find_last_of ("\\/") + 1);            }            insertFuncEntry (appBin, curFunc, funcName, instIncFunc, funcIndex);            /* Create a call to the registration function */            BPatch_Vector < BPatch_snippet * >regArgs;            BPatch_constExpr funcIdReg (funcIndex);            regArgs.push_back (&funcIdReg);            BPatch_constExpr coverageFunc (funcName);            regArgs.push_back (&coverageFunc);            BPatch_constExpr coverageModule (passedModName.c_str ());            regArgs.push_back (&coverageModule);            BPatch_funcCallExpr *regCall =                new BPatch_funcCallExpr (*registerFunc, regArgs);            registerCalls.push_back (regCall);            funcIndex++;            if (bbCoverage) {                insertBBEntry (appBin, curFunc, funcName, passedModName.c_str (),                        instBBIncFunc, registerBB, &bbIndex, &registerCalls);            }        }    }    /* Create argument list for initCoverage function      * with the number of functions the number of basic blocks */    BPatch_Vector < BPatch_snippet * >instInitArgs;    BPatch_constExpr numFuncs (funcIndex);    instInitArgs.push_back (&numFuncs);    BPatch_constExpr numBBs (bbIndex);    instInitArgs.push_back (&numBBs);    BPatch_funcCallExpr instInitExpr (*instInitFunc, instInitArgs);    // Execute the function registration and basic block registration calls after    // the call to the initialization function, i.e.,    // initCoverage()    // registerFunc()    // ...    // registerBB()    // ...    BPatch_sequence registerSequence (registerCalls);    BPatch_Vector < BPatch_snippet * >initSequenceVec;    initSequenceVec.push_back (&instInitExpr);    initSequenceVec.push_back (&registerSequence);    BPatch_sequence initSequence (initSequenceVec);    /* Locate _init */    BPatch_Vector
 funcs;    appImage->findFunction("_init", funcs);    if (funcs.size()) {        BPatch_Vector
::iterator fIter;        for (fIter = funcs.begin(); fIter != funcs.end(); ++fIter) {            BPatch_module * mod = (*fIter)->getModule();            char modName[1024];            mod->getName(modName, 1024);             if (!mod->isSharedLib ()) {                mod->insertInitCallback(initSequence);                cerr << "insertInitCallback on " << modName << endl;            }        }    }    /* Insert initCoverage function in the init section of the default module      * to be executed exactly once when the module is loaded *///    if (!defaultModule->insertInitCallback (initSequence)) {//        cerr << "Failed to insert init function in the module" << endl;//        return EXIT_FAILURE;//    }    /* Insert exitCoverage function in the fini section of the default module      * to be executed exactly once when the module is unloaded */    BPatch_Vector < BPatch_snippet * >instExitArgs;    BPatch_constExpr varPrint (printAll);    instExitArgs.push_back (&varPrint);    BPatch_constExpr varBBPrint (bbCoverage ? 1 : 0);    instExitArgs.push_back (&varBBPrint);    BPatch_constExpr varAlpha (alphabetical);    instExitArgs.push_back (&varAlpha);    BPatch_funcCallExpr instExitExpr (*instExitFunc, instExitArgs);        /* Locate _fini */    funcs.clear();    appImage->findFunction("_fini", funcs);    if (funcs.size()) {        BPatch_Vector
::iterator fIter;        for (fIter = funcs.begin(); fIter != funcs.end(); ++fIter) {            BPatch_module * mod = (*fIter)->getModule();            char modName[1024];            mod->getName(modName, 1024);             if (!mod->isSharedLib ()) {                mod->insertFiniCallback(instExitExpr);                cerr << "insertFiniCallback on " << modName << endl;            }        }    }    //    if (!defaultModule->insertFiniCallback (instExitExpr)) {//        cerr << "Failed to insert exit function in the module" << endl;//        return EXIT_FAILURE;//    }    // Output the instrumented binary    if (!appBin->writeFile (outBinary)) {        cerr << "Failed to write output file: " << outBinary << endl;        return EXIT_FAILURE;    }    return EXIT_SUCCESS;}

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] 
 
    -b: Basic block level code coverage    -p: Print all functions (including functions that are never executed)    -s: Instrument shared libraries also    -a: Sort results alphabetically by function nameNow, pass the testcc executable as input, instrumenting basic blocks aswell as a shared library used by testcc, libtestcc.so.% ./codeCoverage -sb ./testcc testcc.inst[ Lots of output stating what the tool is doing ]You may notice that the tool skips some shared libraries. The default behaviorof the tool is to not instrument standard libraries such as libc.The last command will output a rewritten version of testcc, testcc.inst. It willalso overwrite the existing libtestcc.so file with a rewritten version. Thedefault Dyninst behavior when rewritting the shared libraries of an executableis to output the shared libraries in the same directory as the rewritten executable.Now, run the testcc.inst program to generate code coverage data. An abridgedversion of the output follows.% ./testcc.inst ************************** Code Coverage *************************     1 : _init, testcc    1 : __do_global_dtors_aux, testcc    1 : frame_dummy, testcc    1 : __do_global_ctors_aux, testcc    1 : _fini, testcc    1 : two, testcc.c    1 : one, testcc.c    1 : main, testcc.c ************** Code Coverage 8 out of 17 functions **************  ************************** Basic Block Coverage ************************* [...snip...] (two, testcc.c)      1 : 0x4006ab   (one, testcc.c)      1 : 0x4006c0        1 : 0x4006d6   (__do_global_dtors_aux, testcc)      1 : 0x40063c   (one, testcc.c)      1 : 0x4006e4   (main, testcc.c)      1 : 0x4006f0   (__do_global_dtors_aux, testcc)      1 : 0x400643   (main, testcc.c)      1 : 0x40071a[...snip...] ************** Basic Block Coverage 23 out of 69 blocks ************** END OUTPUTThe first set of data contains counts for the number of times each function wascalled. The second set of data contains counts for the number of times eachbasic block was entered.If you run the program again with a few command line arguments, you will seethat the 'three' function in testcc.c was now called as well as functions inlibtestcc.so.% ./testcc.inst 1 2 ************************** Code Coverage *************************     1 : _init, testcc    1 : three, testcc.c    1 : two, testcc.c    1 : one, testcc.c    1 : main, testcc.c    1 : otherFunction, libtestcc.so    1 : libFooFunction, libtestcc.so    1 : __do_global_dtors_aux, testcc    1 : frame_dummy, testcc    1 : __do_global_ctors_aux, testcc    1 : _fini, testcc ************** Code Coverage 11 out of 17 functions ************** [...snip...] basic block output not includedEND OUTPUTThis output shows that more functions have been called with this new input set.