diff --git a/regression/goto-instrument/call-graph1/main.c b/regression/goto-instrument/call-graph1/main.c new file mode 100644 index 00000000000..d58b61dcb14 --- /dev/null +++ b/regression/goto-instrument/call-graph1/main.c @@ -0,0 +1,81 @@ + +int x; + +void func0() +{ + func0(); +} + +void func1() +{ + x = 1; +} + +void func2() +{ + x = 2; + func3(); +} + +void func3() +{ + x = 3; +} + +void func4(int b) +{ + x = 4; + if(b) + { + func5(0); + } +} + +void func5(int b) +{ + x = 5; + func4(b); +} + +void func6() +{ + x = 6; +} + +void func7(int b) +{ + x = 7; + if(b) + { + func8(0); + } +} + +void func8(int b) +{ + x = 8; + func7(b); +} + +void func9() +{ + x = 9; + funca(); +} + +void funca() +{ + x = 10; + func9(); +} + + + +int main() +{ + func1(); + func2(); + func4(1); + + return 0; +} diff --git a/regression/goto-instrument/call-graph1/test.desc b/regression/goto-instrument/call-graph1/test.desc new file mode 100644 index 00000000000..5b9f5e84c11 --- /dev/null +++ b/regression/goto-instrument/call-graph1/test.desc @@ -0,0 +1,20 @@ +CORE +main.c +--call-graph +^EXIT=0$ +^SIGNAL=0$ +^main -> func2$ +^main -> func1$ +^main -> func4$ +^funca -> func9$ +^func9 -> funca$ +^func8 -> func7$ +^func7 -> func8$ +^__CPROVER__start -> __CPROVER_initialize$ +^__CPROVER__start -> main$ +^func2 -> func3$ +^func0 -> func0$ +^func4 -> func5$ +^func5 -> func4$ +-- +^warning: ignoring diff --git a/regression/goto-instrument/reachable-call-graph1/main.c b/regression/goto-instrument/reachable-call-graph1/main.c new file mode 100644 index 00000000000..d58b61dcb14 --- /dev/null +++ b/regression/goto-instrument/reachable-call-graph1/main.c @@ -0,0 +1,81 @@ + +int x; + +void func0() +{ + func0(); +} + +void func1() +{ + x = 1; +} + +void func2() +{ + x = 2; + func3(); +} + +void func3() +{ + x = 3; +} + +void func4(int b) +{ + x = 4; + if(b) + { + func5(0); + } +} + +void func5(int b) +{ + x = 5; + func4(b); +} + +void func6() +{ + x = 6; +} + +void func7(int b) +{ + x = 7; + if(b) + { + func8(0); + } +} + +void func8(int b) +{ + x = 8; + func7(b); +} + +void func9() +{ + x = 9; + funca(); +} + +void funca() +{ + x = 10; + func9(); +} + + + +int main() +{ + func1(); + func2(); + func4(1); + + return 0; +} diff --git a/regression/goto-instrument/reachable-call-graph1/test.desc b/regression/goto-instrument/reachable-call-graph1/test.desc new file mode 100644 index 00000000000..045d610b79e --- /dev/null +++ b/regression/goto-instrument/reachable-call-graph1/test.desc @@ -0,0 +1,20 @@ +CORE +main.c +--reachable-call-graph +^EXIT=0$ +^SIGNAL=0$ +^main -> func2$ +^main -> func1$ +^main -> func4$ +^__CPROVER__start -> __CPROVER_initialize$ +^__CPROVER__start -> main$ +^func2 -> func3$ +^func4 -> func5$ +^func5 -> func4$ +-- +^funca -> func9$ +^func9 -> funca$ +^func8 -> func7$ +^func7 -> func8$ +^func0 -> func0$ +^warning: ignoring diff --git a/src/analyses/call_graph.cpp b/src/analyses/call_graph.cpp index e3abe2fdcc2..1d7cd526ca7 100644 --- a/src/analyses/call_graph.cpp +++ b/src/analyses/call_graph.cpp @@ -41,8 +41,10 @@ call_grapht::call_grapht( { forall_goto_functions(f_it, goto_functions) { - const goto_programt &body=f_it->second.body; - add(f_it->first, body); + const irep_idt &function_name = f_it->first; + const goto_programt &body = f_it->second.body; + nodes.insert(function_name); + add(function_name, body); } } @@ -84,12 +86,14 @@ call_grapht::call_grapht( const goto_programt &goto_program= goto_functions.function_map.at(function).body; + nodes.insert(function); + forall_callsites( goto_program, [&](goto_programt::const_targett i_it, const irep_idt &callee) { add(function, callee, i_it); - if(graph.find(callee)==graph.end()) + if(edges.find(callee)==edges.end()) pending_stack.push(callee); } ); // NOLINT @@ -129,7 +133,9 @@ void call_grapht::add( const irep_idt &caller, const irep_idt &callee) { - graph.insert(std::pair(caller, callee)); + edges.insert({caller, callee}); + nodes.insert(caller); + nodes.insert(callee); } /// Add edge with optional callsite information @@ -152,7 +158,8 @@ void call_grapht::add( call_grapht call_grapht::get_inverted() const { call_grapht result; - for(const auto &caller_callee : graph) + result.nodes = nodes; + for(const auto &caller_callee : edges) result.add(caller_callee.second, caller_callee.first); return result; } @@ -197,7 +204,12 @@ call_grapht::directed_grapht call_grapht::get_directed_graph() const call_grapht::directed_grapht ret; function_indicest function_indices(ret); - for(const auto &edge : graph) + // To make sure we include unreachable functions we first create indices + // for all nodes in the graph + for(const irep_idt &function_name : nodes) + function_indices[function_name]; + + for(const auto &edge : edges) { auto a_index=function_indices[edge.first]; auto b_index=function_indices[edge.second]; @@ -237,7 +249,7 @@ void call_grapht::output_dot(std::ostream &out) const { out << "digraph call_graph {\n"; - for(const auto &edge : graph) + for(const auto &edge : edges) { out << " \"" << edge.first << "\" -> " << "\"" << edge.second << "\" " @@ -252,7 +264,7 @@ void call_grapht::output_dot(std::ostream &out) const void call_grapht::output(std::ostream &out) const { - for(const auto &edge : graph) + for(const auto &edge : edges) { out << edge.first << " -> " << edge.second << "\n"; if(collect_callsites) @@ -267,7 +279,7 @@ void call_grapht::output_xml(std::ostream &out) const if(collect_callsites) out << "\n"; - for(const auto &edge : graph) + for(const auto &edge : edges) { out << " grapht; + /// Type of the nodes in the call graph. + typedef std::unordered_set nodest; - /// Type of a call graph edge in `grapht` + /// Type of the edges in the call graph. Note parallel edges (e.g. A having + /// two callsites both targeting B) result in multiple graph edges. + typedef std::multimap edgest; + + /// Type of a call graph edge in `edgest` typedef std::pair edget; /// Type of a callsite stored in member `callsites` @@ -84,7 +87,8 @@ class call_grapht /// backward compatibility; use `get_directed_graph()` to get a generic /// directed graph representation that provides more graph algorithms /// (shortest path, SCCs and so on). - grapht graph; + edgest edges; + nodest nodes; /// Map from call-graph edges to a set of callsites that make the given call. callsitest callsites; diff --git a/unit/analyses/call_graph.cpp b/unit/analyses/call_graph.cpp index 39c7a61588b..77e06e7d6ef 100644 --- a/unit/analyses/call_graph.cpp +++ b/unit/analyses/call_graph.cpp @@ -64,6 +64,7 @@ SCENARIO("call_graph", // } // void C() { } // void D() { } + // void E() { } goto_modelt goto_model; code_typet void_function_type; @@ -101,6 +102,8 @@ SCENARIO("call_graph", create_void_function_symbol("C", code_skipt())); goto_model.symbol_table.add( create_void_function_symbol("D", code_skipt())); + goto_model.symbol_table.add( + create_void_function_symbol("E", code_skipt())); stream_message_handlert msg(std::cout); goto_convert(goto_model, msg); @@ -111,7 +114,7 @@ SCENARIO("call_graph", { THEN("We expect A -> { A, B, B }, B -> { C, D }") { - const auto &check_graph=call_graph_from_goto_functions.graph; + const auto &check_graph=call_graph_from_goto_functions.edges; REQUIRE(check_graph.size()==5); REQUIRE(multimap_key_matches(check_graph, "A", {"A", "B", "B"})); REQUIRE(multimap_key_matches(check_graph, "B", {"C", "D"})); @@ -128,7 +131,7 @@ SCENARIO("call_graph", call_graph_from_goto_functions.get_inverted(); THEN("We expect A -> { A }, B -> { A, A }, C -> { B }, D -> { B }") { - const auto &check_graph=inverse_call_graph_from_goto_functions.graph; + const auto &check_graph=inverse_call_graph_from_goto_functions.edges; REQUIRE(check_graph.size()==5); REQUIRE(multimap_key_matches(check_graph, "A", {"A"})); REQUIRE(multimap_key_matches(check_graph, "B", {"A", "A"})); @@ -143,9 +146,9 @@ SCENARIO("call_graph", THEN("We expect two callsites for the A -> B edge, one for all others") { const auto &check_callsites=call_graph_from_goto_functions.callsites; - for(const auto &edge : call_graph_from_goto_functions.graph) + for(const auto &edge : call_graph_from_goto_functions.edges) { - if(edge==call_grapht::grapht::value_type("A", "B")) + if(edge==call_grapht::edgest::value_type("A", "B")) REQUIRE(check_callsites.at(edge).size()==2); else REQUIRE(check_callsites.at(edge).size()==1); @@ -167,7 +170,7 @@ SCENARIO("call_graph", call_grapht::create_from_root_function(goto_model, "B", false); THEN("We expect only B -> C and B -> D in the resulting graph") { - const auto &check_graph=call_graph_from_b.graph; + const auto &check_graph=call_graph_from_b.edges; REQUIRE(check_graph.size()==2); REQUIRE(multimap_key_matches(check_graph, "B", {"C", "D"})); } @@ -183,6 +186,11 @@ SCENARIO("call_graph", for(node_indext i=0; i { A, B }, B -> { C, D }") { // Note that means the extra A -> B edge has gone away (the grapht @@ -228,6 +236,14 @@ SCENARIO("call_graph", REQUIRE(predecessors.count("B")); REQUIRE(predecessors.count("D")); } + + THEN("We expect {E} to be able to reach E") + { + std::set predecessors = + get_reaching_functions(exported, "E"); + REQUIRE(predecessors.size() == 1); + REQUIRE(predecessors.count("E")); + } } WHEN("The call graph, with call sites, is exported as a grapht") @@ -241,6 +257,11 @@ SCENARIO("call_graph", for(node_indext i=0; i { A, B }, B -> { C, D }") { // Note that means the extra A -> B edge has gone away (the grapht