diff --git a/src/analyses/call_graph.cpp b/src/analyses/call_graph.cpp index 4413d56c205..a4a1ff65ec1 100644 --- a/src/analyses/call_graph.cpp +++ b/src/analyses/call_graph.cpp @@ -6,10 +6,12 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ +#include "call_graph.h" #include #include +#include +#include -#include "call_graph.h" /*******************************************************************\ @@ -70,11 +72,31 @@ void call_grapht::add( { const exprt &function_expr=to_code_function_call(i_it->code).function(); if(function_expr.id()==ID_symbol) - add(function, to_symbol_expr(function_expr).get_identifier()); + add(function, to_symbol_expr(function_expr).get_identifier(), {i_it}); } } } +/*******************************************************************\ + +Function: call_grapht::swap + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void call_grapht::swap(call_grapht &other) +{ + std::swap(graph, other.graph); + std::swap(map_from_edges_to_call_locations, + other.map_from_edges_to_call_locations); +} + + /*******************************************************************\ Function: call_grapht::add @@ -96,7 +118,7 @@ void call_grapht::add( /*******************************************************************\ -Function: call_grapht::output_dot +Function: call_grapht::add Inputs: @@ -106,37 +128,60 @@ Function: call_grapht::output_dot \*******************************************************************/ -void call_grapht::output_dot(std::ostream &out) const +void call_grapht::add( + const irep_idt &caller, + const irep_idt &callee, + const map_from_edges_to_call_locationst::mapped_type &call_sites) { - out << "digraph call_graph {\n"; - - for(const auto &edge : graph) + bool exists=false; + const call_grapht::call_edges_ranget range=out_edges(caller); + for(auto it=range.first; it!=range.second; ++it) { - out << " \"" << edge.first << "\" -> " - << "\"" << edge.second << "\" " - << " [arrowhead=\"vee\"];" - << "\n"; + if(it->second==callee) + { + exists=true; + break; + } } - - out << "}\n"; + if(!exists) + add(caller, callee); + std::copy( + call_sites.cbegin(), call_sites.cend(), + std::back_inserter(map_from_edges_to_call_locations[{caller, callee}])); } -void call_grapht::output_dot( - const goto_functionst &functions, - std::ostream &out) const +/*******************************************************************\ + +Function: call_grapht::output_dot + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void call_grapht::output_dot(std::ostream &out) const { - out << "digraph call_graph {\n"; - for(const auto &elem : functions.function_map) - out << " \"" << elem.first << "\";\n"; - for(grapht::const_iterator it=graph.begin(); - it!=graph.end(); - it++) + out << "digraph call_graph {\n" + << " node [fontsize=12 shape=box];\n"; + for(const auto &edge : graph) { - out << " \"" << it->first << "\" -> " - << "\"" << it->second << "\" " - << " [arrowhead=\"vee\"];" - << "\n"; + out << " \"" << edge.first << "\" -> " + << "\"" << edge.second << "\" " + << " [label=\"{"; + bool first=true; + for(const auto instr_it : + get_map_from_edges_to_call_locations().at({edge.first, edge.second})) + { + if(!first) + out << ","; + out << instr_it->location_number; + first=false; + } + out << "}\"];\n"; } out << "}\n"; } @@ -382,7 +427,10 @@ void compute_inverted_call_graph( { assert(output_inverted_call_graph.graph.empty()); for(const auto &elem : original_call_graph.graph) - output_inverted_call_graph.add(elem.second, elem.first); + output_inverted_call_graph.add( + elem.second, elem.first, + original_call_graph.get_map_from_edges_to_call_locations().at( + {elem.first, elem.second})); } /*******************************************************************\ @@ -443,3 +491,91 @@ void find_leaves_below_function( std::unordered_set to_avoid; find_leaves_below_function(call_graph, function, to_avoid, output); } + +/*******************************************************************\ + +Function: find_direct_or_indirect_callees_of_function + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void find_direct_or_indirect_callees_of_function( + const call_grapht &call_graph, + const irep_idt &function, + std::unordered_set &output) +{ + std::unordered_set leaves; + find_leaves_below_function(call_graph, function, output, leaves); + output.insert(leaves.cbegin(), leaves.cend()); +} + +/*******************************************************************\ + +Function: find_nearest_common_callees + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void find_nearest_common_callees( + const call_grapht &call_graph, + const std::set &functions, + std::set &output) +{ + if(functions.empty()) + return; + if(functions.size()==1UL) + { + output.insert(*functions.cbegin()); + return; + } + + std::map counting; + for(const auto &elem : call_graph.graph) + { + counting[elem.first]=0U; + counting[elem.second]=0U; + } + for(const auto &fn : functions) + { + std::unordered_set callees; + find_direct_or_indirect_callees_of_function(call_graph, fn, callees); + assert(callees.count(fn)==1U); + for(const auto &callee : callees) + ++counting[callee]; + } + + std::set leaves; + for(const auto &elem : counting) + if(elem.second!=0U) + { + const call_grapht::call_edges_ranget range= + call_graph.out_edges(elem.first); + if(range.first==range.second) + leaves.insert(elem.first); + } + + for(auto &elem : counting) + if(leaves.count(elem.first)!=0UL) + output.insert(elem.first); + else if(elem.second!=0U && elem.secondsecond); + if(cit->second==functions.size()) + output.insert(cit->first); + } + } +} diff --git a/src/analyses/call_graph.h b/src/analyses/call_graph.h index f852272b20e..5f43aa680e0 100644 --- a/src/analyses/call_graph.h +++ b/src/analyses/call_graph.h @@ -23,23 +23,31 @@ class call_grapht explicit call_grapht(const goto_functionst &); void output_dot(std::ostream &out) const; - - /** - * It writes this into the passed stream in the Graphviz's DOT format. - * The method accepts also functions, because the callgraph does not - * store funtions (nodes). It only stores edges (from caller to callee). - * So, the resulting graph would not show not-called functions. - */ - void output_dot(const goto_functionst &functions, std::ostream &out) const; - void output(std::ostream &out) const; void output_xml(std::ostream &out) const; typedef std::multimap grapht; grapht graph; + void swap(call_grapht &other); + void add(const irep_idt &caller, const irep_idt &callee); + /** + * The type provides a mapping from edges of the call-graph to particular + * instructions in the caller GOTO programs, where the calls are performed. + */ + typedef std::map< + std::pair, + std::vector > + map_from_edges_to_call_locationst; + + const map_from_edges_to_call_locationst & + get_map_from_edges_to_call_locations() const + { return map_from_edges_to_call_locations; } + + void add(const irep_idt &caller, const irep_idt &callee, + const map_from_edges_to_call_locationst::mapped_type &call_sites); protected: void add(const irep_idt &function, const goto_programt &body); @@ -74,6 +82,9 @@ class call_grapht * std::cout << it->second << ", "; */ call_edges_ranget out_edges(const irep_idt &caller) const; + +private: + map_from_edges_to_call_locationst map_from_edges_to_call_locations; }; /*******************************************************************\ @@ -160,4 +171,26 @@ void find_leaves_below_function( const irep_idt &function, std::unordered_set &output); +void find_direct_or_indirect_callees_of_function( + const call_grapht &call_graph, + const irep_idt &function, + std::unordered_set &output); + +void find_nearest_common_callees( + const call_grapht &call_graph, + const std::set &functions, + std::set &output); + +/** + * The "callee" must be a DIRECT callee of the "caller" in the "call_graph". + */ +inline const std::vector & +get_call_sites( + const call_grapht &call_graph, + const irep_idt &caller, + const irep_idt &callee) +{ + return call_graph.get_map_from_edges_to_call_locations().at({caller, callee}); +} + #endif // CPROVER_ANALYSES_CALL_GRAPH_H