Skip to content

Commit 7e10e68

Browse files
author
Owen Jones
committed
Make directed callgraph include nodes with no edges
Note, if the callgraph was created from a particular entry point then this will not change the output. It is only when the callgraph is created for a goto_functionst that the output will change - it will now include functions that have no edges, i.e. that are not called by any other functions and which don't call any other functions.
1 parent 77203b6 commit 7e10e68

File tree

3 files changed

+57
-19
lines changed

3 files changed

+57
-19
lines changed

src/analyses/call_graph.cpp

+21-9
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ call_grapht::call_grapht(
4141
{
4242
forall_goto_functions(f_it, goto_functions)
4343
{
44-
const goto_programt &body=f_it->second.body;
45-
add(f_it->first, body);
44+
const irep_idt &function_name = f_it->first;
45+
const goto_programt &body = f_it->second.body;
46+
nodes.insert(function_name);
47+
add(function_name, body);
4648
}
4749
}
4850

@@ -84,12 +86,14 @@ call_grapht::call_grapht(
8486
const goto_programt &goto_program=
8587
goto_functions.function_map.at(function).body;
8688

89+
nodes.insert(function);
90+
8791
forall_callsites(
8892
goto_program,
8993
[&](goto_programt::const_targett i_it, const irep_idt &callee)
9094
{
9195
add(function, callee, i_it);
92-
if(graph.find(callee)==graph.end())
96+
if(edges.find(callee)==edges.end())
9397
pending_stack.push(callee);
9498
}
9599
); // NOLINT
@@ -129,7 +133,9 @@ void call_grapht::add(
129133
const irep_idt &caller,
130134
const irep_idt &callee)
131135
{
132-
graph.insert(std::pair<irep_idt, irep_idt>(caller, callee));
136+
edges.insert({caller, callee});
137+
nodes.insert(caller);
138+
nodes.insert(callee);
133139
}
134140

135141
/// Add edge with optional callsite information
@@ -152,7 +158,8 @@ void call_grapht::add(
152158
call_grapht call_grapht::get_inverted() const
153159
{
154160
call_grapht result;
155-
for(const auto &caller_callee : graph)
161+
result.nodes = nodes;
162+
for(const auto &caller_callee : edges)
156163
result.add(caller_callee.second, caller_callee.first);
157164
return result;
158165
}
@@ -197,7 +204,12 @@ call_grapht::directed_grapht call_grapht::get_directed_graph() const
197204
call_grapht::directed_grapht ret;
198205
function_indicest function_indices(ret);
199206

200-
for(const auto &edge : graph)
207+
// To make sure we include unreachable functions we first create indices
208+
// for all nodes in the graph
209+
for(const irep_idt &function_name : nodes)
210+
function_indices[function_name];
211+
212+
for(const auto &edge : edges)
201213
{
202214
auto a_index=function_indices[edge.first];
203215
auto b_index=function_indices[edge.second];
@@ -237,7 +249,7 @@ void call_grapht::output_dot(std::ostream &out) const
237249
{
238250
out << "digraph call_graph {\n";
239251

240-
for(const auto &edge : graph)
252+
for(const auto &edge : edges)
241253
{
242254
out << " \"" << edge.first << "\" -> "
243255
<< "\"" << edge.second << "\" "
@@ -252,7 +264,7 @@ void call_grapht::output_dot(std::ostream &out) const
252264

253265
void call_grapht::output(std::ostream &out) const
254266
{
255-
for(const auto &edge : graph)
267+
for(const auto &edge : edges)
256268
{
257269
out << edge.first << " -> " << edge.second << "\n";
258270
if(collect_callsites)
@@ -267,7 +279,7 @@ void call_grapht::output_xml(std::ostream &out) const
267279
if(collect_callsites)
268280
out << "<!-- XML call-graph representation does not document callsites yet."
269281
" If you need this, edit call_grapht::output_xml -->\n";
270-
for(const auto &edge : graph)
282+
for(const auto &edge : edges)
271283
{
272284
out << "<call_graph_edge caller=\"";
273285
xmlt::escape_attribute(id2string(edge.first), out);

src/analyses/call_graph.h

+10-5
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,17 @@ class call_grapht
6262
void output(std::ostream &out) const;
6363
void output_xml(std::ostream &out) const;
6464

65-
/// Type of the call graph. Note parallel edges (e.g. A having two callsites
66-
/// both targeting B) result in multiple graph edges.
67-
typedef std::multimap<irep_idt, irep_idt> grapht;
65+
/// Type of the nodes in the call graph.
66+
typedef std::unordered_set<irep_idt, irep_id_hash> nodest;
6867

69-
/// Type of a call graph edge in `grapht`
68+
/// Type of the edges in the call graph. Note parallel edges (e.g. A having
69+
/// two callsites both targeting B) result in multiple graph edges.
70+
typedef std::multimap<irep_idt, irep_idt> edgest;
71+
72+
/// Type of a call graph edge in `edgest`
7073
typedef std::pair<irep_idt, irep_idt> edget;
7174

75+
7276
/// Type of a callsite stored in member `callsites`
7377
typedef goto_programt::const_targett locationt;
7478

@@ -84,7 +88,8 @@ class call_grapht
8488
/// backward compatibility; use `get_directed_graph()` to get a generic
8589
/// directed graph representation that provides more graph algorithms
8690
/// (shortest path, SCCs and so on).
87-
grapht graph;
91+
edgest edges;
92+
nodest nodes;
8893

8994
/// Map from call-graph edges to a set of callsites that make the given call.
9095
callsitest callsites;

unit/analyses/call_graph.cpp

+26-5
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ SCENARIO("call_graph",
6464
// }
6565
// void C() { }
6666
// void D() { }
67+
// void E() { }
6768

6869
goto_modelt goto_model;
6970
code_typet void_function_type;
@@ -101,6 +102,8 @@ SCENARIO("call_graph",
101102
create_void_function_symbol("C", code_skipt()));
102103
goto_model.symbol_table.add(
103104
create_void_function_symbol("D", code_skipt()));
105+
goto_model.symbol_table.add(
106+
create_void_function_symbol("E", code_skipt()));
104107

105108
stream_message_handlert msg(std::cout);
106109
goto_convert(goto_model, msg);
@@ -111,7 +114,7 @@ SCENARIO("call_graph",
111114
{
112115
THEN("We expect A -> { A, B, B }, B -> { C, D }")
113116
{
114-
const auto &check_graph=call_graph_from_goto_functions.graph;
117+
const auto &check_graph=call_graph_from_goto_functions.edges;
115118
REQUIRE(check_graph.size()==5);
116119
REQUIRE(multimap_key_matches(check_graph, "A", {"A", "B", "B"}));
117120
REQUIRE(multimap_key_matches(check_graph, "B", {"C", "D"}));
@@ -128,7 +131,7 @@ SCENARIO("call_graph",
128131
call_graph_from_goto_functions.get_inverted();
129132
THEN("We expect A -> { A }, B -> { A, A }, C -> { B }, D -> { B }")
130133
{
131-
const auto &check_graph=inverse_call_graph_from_goto_functions.graph;
134+
const auto &check_graph=inverse_call_graph_from_goto_functions.edges;
132135
REQUIRE(check_graph.size()==5);
133136
REQUIRE(multimap_key_matches(check_graph, "A", {"A"}));
134137
REQUIRE(multimap_key_matches(check_graph, "B", {"A", "A"}));
@@ -143,9 +146,9 @@ SCENARIO("call_graph",
143146
THEN("We expect two callsites for the A -> B edge, one for all others")
144147
{
145148
const auto &check_callsites=call_graph_from_goto_functions.callsites;
146-
for(const auto &edge : call_graph_from_goto_functions.graph)
149+
for(const auto &edge : call_graph_from_goto_functions.edges)
147150
{
148-
if(edge==call_grapht::grapht::value_type("A", "B"))
151+
if(edge==call_grapht::edgest::value_type("A", "B"))
149152
REQUIRE(check_callsites.at(edge).size()==2);
150153
else
151154
REQUIRE(check_callsites.at(edge).size()==1);
@@ -167,7 +170,7 @@ SCENARIO("call_graph",
167170
call_grapht::create_from_root_function(goto_model, "B", false);
168171
THEN("We expect only B -> C and B -> D in the resulting graph")
169172
{
170-
const auto &check_graph=call_graph_from_b.graph;
173+
const auto &check_graph=call_graph_from_b.edges;
171174
REQUIRE(check_graph.size()==2);
172175
REQUIRE(multimap_key_matches(check_graph, "B", {"C", "D"}));
173176
}
@@ -183,6 +186,11 @@ SCENARIO("call_graph",
183186
for(node_indext i=0; i<exported.size(); ++i)
184187
nodes_by_name[exported[i].function]=i;
185188

189+
THEN("We expect 5 nodes")
190+
{
191+
REQUIRE(exported.size() == 5);
192+
}
193+
186194
THEN("We expect edges A -> { A, B }, B -> { C, D }")
187195
{
188196
// Note that means the extra A -> B edge has gone away (the grapht
@@ -228,6 +236,14 @@ SCENARIO("call_graph",
228236
REQUIRE(predecessors.count("B"));
229237
REQUIRE(predecessors.count("D"));
230238
}
239+
240+
THEN("We expect {E} to be able to reach E")
241+
{
242+
std::set<irep_idt> predecessors =
243+
get_reaching_functions(exported, "E");
244+
REQUIRE(predecessors.size() == 1);
245+
REQUIRE(predecessors.count("E"));
246+
}
231247
}
232248

233249
WHEN("The call graph, with call sites, is exported as a grapht")
@@ -241,6 +257,11 @@ SCENARIO("call_graph",
241257
for(node_indext i=0; i<exported.size(); ++i)
242258
nodes_by_name[exported[i].function]=i;
243259

260+
THEN("We expect 5 nodes")
261+
{
262+
REQUIRE(exported.size() == 5);
263+
}
264+
244265
THEN("We expect edges A -> { A, B }, B -> { C, D }")
245266
{
246267
// Note that means the extra A -> B edge has gone away (the grapht

0 commit comments

Comments
 (0)