ROSE Compiler Framework/call graph analysis

source files
src/midend/programAnalysis/CallGraphAnalysis/CallGraph.h

rose/tutorial/callGraphAnalysis.C and buildCallGraph.C

class interface class CallGraphBuilder {    public: CallGraphBuilder( SgProject *proj ); void buildCallGraph;

template void buildCallGraph(Predicate pred);

CallGraphCreate *getGraph; void classifyCallGraph; private: SgProject *project; CallGraphCreate *graph; };

Data structures
SgGraph
 * FunctionData: for a function decl, stores (callee?) function declaration list(functionList)***, hasDefinition , findPointsToVirtualFunction:
 * FunctionData::FunctionData find all callees and fill functionList for each node(function)
 * FuntionData * list --> callGraphData
 * CallGraphCreate typedef DAGCreate CallGraphCreate;
 * CallGraphNode * nodeList: label, hasDefinition, functionDeclaration,
 * CallGraphEdge  : label

graph node
SgGraphNode* graphNode = new SgGraphNode(functionName); graphNode->set_SgNode(currentFunction.functionDeclaration);

// maintain an extra hash table between AST function and SgGraphNode graphNodes[currentFunction.functionDeclaration] = graphNode;

// add a node into a graph returnGraph->addNode(graphNode);

graph edge
SgGraphNode *startingNode, *endNode; ... if (returnGraph->checkIfDirectedGraphEdgeExists(startingNode, endNode) == false) {      ROSE_ASSERT(startingNode != NULL && endNode != NULL); returnGraph->addDirectedEdge(startingNode, endNode); }

Driver: CallGraphBuilder
get the first nondefining declaration only
 * CallGraphBuilder::buildCallGraph CallGraph.C L1553
 * queryMemoryPool for all function declarations, including member function declarations
 * Create FunctionData for each of them
 * add FunctionData into callGraphData list.

FunctionData::FunctionData //CallGraph.C L1096
 * get defining declaration
 * querySubTree(def, V_SgFunctionCallExp)
 * several cases : how about function pointers passed as parameters?
 * dotStartOp : CallTargetSet::solveMemberFunctionPointerCall **
 * .func and ->func: CallTargetSet::solveMemberFunctionCall**
 * pointer dereference exp: CallTargetSet::solveFunctionPointerCall **
 * memberfunctionRefExp: very simple
 * functionRefExp: very simple

CallGraph.h class CallGraphBuilder { public: CallGraphBuilder( SgProject *proj); //! Default builder filtering nothing in the call graph void buildCallGraph; //! Builder accepting user defined predicate to filter certain functions template void buildCallGraph(Predicate pred); //! Grab the call graph built SgIncidenceDirectedGraph *getGraph; //void classifyCallGraph;

//We map each function to the corresponding graph node boost::unordered_map& getGraphNodesMapping{ return graphNodes; }

private: SgProject *project; SgIncidenceDirectedGraph *graph; //We map each function to the corresponding graph node boost::unordered_map graphNodes;

};

function nodes
first nondefining declaration has precedence compared to defining declaration.

+++ b/src/midend/programAnalysis/CallGraphAnalysis/CallGraph.h @@ -192,6 +192,7 @@ CallGraphBuilder::buildCallGraph(Predicate pred) else {            // we need to have only one declaration for regular functions as well +           // first nondefining declaration has precedence compared to defining declaration SgFunctionDeclaration *nonDefDecl = isSgFunctionDeclaration(functionDeclaration->get_firstNondefiningDeclaration); assert(!isSgTemplateFunctionDeclaration(nonDefDecl)); if (nonDefDecl)

create a call graph
You can pass a predicate to filter out some functioins

// A Function object used as a predicate that determines which functions are // to be represented in the call graph. struct keepFunction : public unary_function{ public: bool operator(SgFunctionDeclaration* funcDecl){ bool returnValue = true; ROSE_ASSERT(funcDecl != NULL); string filename = funcDecl->get_file_info->get_filename;

//Filter out functions from the ROSE preinclude header file if(filename.find("rose_edg_required_macros_and_functions")!=string::npos) returnValue = false;

//Filter out compiler generated functions if(funcDecl->get_file_info->isCompilerGenerated==true) returnValue=false; //Filter out prototypes when defining function declarations exist at the same time // This is now necessary since we always generate the first nondefining declaration in ROSE using EDG 4.4. //Liao 1/23/2013 if (funcDecl->get_definingDeclaration != NULL) if (funcDecl->get_firstNondefiningDeclaration == funcDecl) returnValue = false;

return returnValue; } };

CallGraphBuilder builder(project); builder.buildCallGraph(keepFunction);

No predicate is fine also:

CallGraphBuilder CGBuilder( project ); CGBuilder.buildCallGraph;

CGBuilder.classifyCallGraph; //? Classify subgraphs within call graph

//generate dot graph !! GenerateDotGraph(CGBuilder.getGraph,"callgraph.dot");


 * src/3rdPartyLibraries/MSTL/DOTGraphInterface.h

(gdb) p *i $5 = (CallGraphNode &) @0x9abc060: { = { = {_vptr.GraphElem = 0x65d1d00, gc = 0x0, count = 1}, }, label = {static npos = 4294967295, _M_dataplus = { = {<__gnu_cxx::new_allocator > = {}, }, _M_p = 0x9abbecc "__builtin_copysign"}}, functionDeclaration = 0xb7e60008, hasDefinition = false}

AST to Call Graph
class CallGraphBuilder { public: //We map each function to the corresponding graph node, a hash table boost::unordered_map& getGraphNodesMapping{ return graphNodes; } };

traverse the graph
see SgGraph

class hierarchy * SgIncidenceDirectedGraph // directional o SgBidirectionalGraph // bidirectional, in/out edges + SgIntKeyedBidirectionalGraph + SgStringKeyedBidirectionalGraph * SgIncidenceUndirectedGraph // undirected graph
 * 1) SgGraph


 * 1) SgGraphNode:  an index as a unique id for each node

* SgDirectedGraphEdge * SgUndirectedGraphEdge
 * 1) SgGraphEdge:


 * 1) SgGraphEdgeList


 * 1) SgGraphNodeList

obtain nodes
A function can have multiple call graph nodes: why? // initializes the cgNodes set void CGFunction::initCGNodes {

// get all the nodes set nodes = graph->computeNodeSet;

// iterate through the nodes

for(set::iterator itn = nodes.begin; itn != nodes.end; itn++) { SgNode* n = (*itn)->get_SgNode; ROSE_ASSERT(isSgFunctionDeclaration(n)); SgFunctionDeclaration* cfgDecl = getCanonicalDecl(isSgFunctionDeclaration(n));

// if the given SgGraphNode refers to this function if(cfgDecl == decl) cgNodes.insert(*itn); } }

obtain edges
std::set::iterator itn; ...

if(dir == fw) edges = func->graph->computeEdgeSetOut(*itn); // get out edges else         edges = func->graph->computeEdgeSetIn(*itn);   // get in edges

std::set<SgDirectedGraphEdge*>::iterator ite; // The current edge in edges

// get destination or source of an edge SgGraphNode* target = (dir == fw ? (*ite)->get_to : (*ite)->get_from);

dot graph generation
./tutorial/buildCallGraph.C

using namespace std;
 * 1) include "rose.h"
 * 2) include <CallGraph.h>
 * 3) include

// A Function object used as a predicate that determines which functions are // to be represented in the call graph. struct keepFunction : public unary_function<bool,SgFunctionDeclaration*>{ public: bool operator(SgFunctionDeclaration* funcDecl){ bool returnValue = true; ROSE_ASSERT(funcDecl != NULL); string filename = funcDecl->get_file_info->get_filename;

//Filter out functions from the ROSE preinclude header file if(filename.find("rose_edg_required_macros_and_functions")!=string::npos) returnValue = false;

//Filter out compiler generated functions if(funcDecl->get_file_info->isCompilerGenerated==true) returnValue=false;

return returnValue; } };

int main( int argc, char * argv[] ) { SgProject* project = new SgProject(argc, argv); ROSE_ASSERT (project != NULL);

if (project->get_fileList.size >=1) {   //Construct a call Graph CallGraphBuilder CGBuilder( project); CGBuilder.buildCallGraph(keepFunction);

// Output to a dot file AstDOTGeneration dotgen; SgFilePtrList file_list = project->get_fileList; std::string firstFileName = StringUtility::stripPathFromFileName(file_list[0]->getFileName); dotgen.writeIncidenceGraphToDOTFile( CGBuilder.getGraph, firstFileName+"_callGraph.dot"); }

return 0; // backend(project); }