Skip to content

Commit 8115248

Browse files
committed
Call graph: optionally record callsites
This makes it easier for call-graph users to discover the particular instruction responsible for a call-graph edge. Ideally these would be recorded in-line with the map (i.e. change the graph type to std::multimap<irep_idt, std::pair<locationt, irep_idt>>), but here we introduce an optional auxiliary map to avoid breaking existing users.
1 parent c545369 commit 8115248

File tree

3 files changed

+103
-17
lines changed

3 files changed

+103
-17
lines changed

src/analyses/call_graph.cpp

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,19 @@ Author: Daniel Kroening, [email protected]
1414
#include <util/std_expr.h>
1515
#include <util/xml.h>
1616

17-
call_grapht::call_grapht()
17+
call_grapht::call_grapht(bool collect_callsites):
18+
collect_callsites(collect_callsites)
1819
{
1920
}
2021

21-
call_grapht::call_grapht(const goto_modelt &goto_model):
22-
call_grapht(goto_model.goto_functions)
22+
call_grapht::call_grapht(const goto_modelt &goto_model, bool collect_callsites):
23+
call_grapht(goto_model.goto_functions, collect_callsites)
2324
{
2425
}
2526

26-
call_grapht::call_grapht(const goto_functionst &goto_functions)
27+
call_grapht::call_grapht(
28+
const goto_functionst &goto_functions, bool collect_callsites):
29+
collect_callsites(collect_callsites)
2730
{
2831
forall_goto_functions(f_it, goto_functions)
2932
{
@@ -42,7 +45,12 @@ void call_grapht::add(
4245
{
4346
const exprt &function_expr=to_code_function_call(i_it->code).function();
4447
if(function_expr.id()==ID_symbol)
45-
add(function, to_symbol_expr(function_expr).get_identifier());
48+
{
49+
const irep_idt &callee=to_symbol_expr(function_expr).get_identifier();
50+
add(function, callee);
51+
if(collect_callsites)
52+
callsites[{function, callee}].insert(i_it);
53+
}
4654
}
4755
}
4856
}
@@ -54,6 +62,21 @@ void call_grapht::add(
5462
graph.insert(std::pair<irep_idt, irep_idt>(caller, callee));
5563
}
5664

65+
/// Add edge with optional callsite information
66+
/// \param caller: caller function
67+
/// \param callee: callee function
68+
/// \param callsite: call instruction responsible for this edge. Note this is
69+
/// only stored if `collect_callsites` was specified during construction.
70+
void call_grapht::add(
71+
const irep_idt &caller,
72+
const irep_idt &callee,
73+
locationt callsite)
74+
{
75+
add(caller, callee);
76+
if(collect_callsites)
77+
callsites[{caller, callee}].insert(callsite);
78+
}
79+
5780
/// Returns an inverted copy of this call graph
5881
/// \return Inverted (callee -> caller) call graph
5982
call_grapht call_grapht::get_inverted() const
@@ -64,6 +87,20 @@ call_grapht call_grapht::get_inverted() const
6487
return result;
6588
}
6689

90+
std::string call_grapht::format_callsites(const edget &edge) const
91+
{
92+
PRECONDITION(collect_callsites);
93+
std::string ret="{";
94+
for(const locationt &loc : callsites.at(edge))
95+
{
96+
if(ret.size()>1)
97+
ret+=", ";
98+
ret+=std::to_string(loc->location_number);
99+
}
100+
ret+='}';
101+
return ret;
102+
}
103+
67104
void call_grapht::output_dot(std::ostream &out) const
68105
{
69106
out << "digraph call_graph {\n";
@@ -72,8 +109,10 @@ void call_grapht::output_dot(std::ostream &out) const
72109
{
73110
out << " \"" << edge.first << "\" -> "
74111
<< "\"" << edge.second << "\" "
75-
<< " [arrowhead=\"vee\"];"
76-
<< "\n";
112+
<< " [arrowhead=\"vee\"";
113+
if(collect_callsites)
114+
out << " label=\"" << format_callsites(edge) << "\"";
115+
out << "];\n";
77116
}
78117

79118
out << "}\n";
@@ -84,11 +123,18 @@ void call_grapht::output(std::ostream &out) const
84123
for(const auto &edge : graph)
85124
{
86125
out << edge.first << " -> " << edge.second << "\n";
126+
if(collect_callsites)
127+
out << " (callsites: " << format_callsites(edge) << ")\n";
87128
}
88129
}
89130

90131
void call_grapht::output_xml(std::ostream &out) const
91132
{
133+
// Note I don't implement callsite output here; I'll leave that
134+
// to the first interested XML user.
135+
if(collect_callsites)
136+
out << "<!-- XML call-graph representation does not document callsites yet."
137+
" If you need this, edit call_grapht::output_xml -->\n";
92138
for(const auto &edge : graph)
93139
{
94140
out << "<call_graph_edge caller=\"";

src/analyses/call_graph.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,33 @@ Author: Daniel Kroening, [email protected]
2020
class call_grapht
2121
{
2222
public:
23-
call_grapht();
24-
explicit call_grapht(const goto_modelt &);
25-
explicit call_grapht(const goto_functionst &);
23+
explicit call_grapht(bool collect_callsites=false);
24+
explicit call_grapht(const goto_modelt &, bool collect_callsites=false);
25+
explicit call_grapht(const goto_functionst &, bool collect_callsites=false);
2626

2727
void output_dot(std::ostream &out) const;
2828
void output(std::ostream &out) const;
2929
void output_xml(std::ostream &out) const;
3030

3131
typedef std::multimap<irep_idt, irep_idt> grapht;
32+
typedef std::pair<irep_idt, irep_idt> edget;
33+
typedef goto_programt::const_targett locationt;
34+
typedef std::set<locationt> locationst;
35+
typedef std::map<edget, locationst> callsitest;
36+
3237
grapht graph;
38+
callsitest callsites;
3339

3440
void add(const irep_idt &caller, const irep_idt &callee);
41+
void add(const irep_idt &caller, const irep_idt &callee, locationt callsite);
3542
call_grapht get_inverted() const;
3643

3744
protected:
3845
void add(const irep_idt &function,
3946
const goto_programt &body);
47+
private:
48+
bool collect_callsites;
49+
std::string format_callsites(const edget &edge) const;
4050
};
4151

4252
#endif // CPROVER_ANALYSES_CALL_GRAPH_H

unit/analyses/call_graph.cpp

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ SCENARIO("call_graph",
5454
// {
5555
// A();
5656
// B();
57+
// B();
5758
// }
5859
// void B()
5960
// {
@@ -72,8 +73,11 @@ SCENARIO("call_graph",
7273
call1.function()=symbol_exprt("A", void_function_type);
7374
code_function_callt call2;
7475
call2.function()=symbol_exprt("B", void_function_type);
76+
code_function_callt call3;
77+
call3.function()=symbol_exprt("B", void_function_type);
7578
calls.move_to_operands(call1);
7679
calls.move_to_operands(call2);
80+
calls.move_to_operands(call3);
7781

7882
goto_model.symbol_table.add(
7983
create_void_function_symbol("A", calls));
@@ -104,30 +108,56 @@ SCENARIO("call_graph",
104108

105109
WHEN("A call graph is constructed from the GOTO functions")
106110
{
107-
THEN("We expect A -> { A, B }, B -> { C, D }")
111+
THEN("We expect A -> { A, B, B }, B -> { C, D }")
108112
{
109113
const auto &check_graph=call_graph_from_goto_functions.graph;
110-
REQUIRE(check_graph.size()==4);
111-
REQUIRE(multimap_key_matches(check_graph, "A", {"A", "B"}));
114+
REQUIRE(check_graph.size()==5);
115+
REQUIRE(multimap_key_matches(check_graph, "A", {"A", "B", "B"}));
112116
REQUIRE(multimap_key_matches(check_graph, "B", {"C", "D"}));
113117
}
118+
THEN("No callsite data should be collected")
119+
{
120+
REQUIRE(call_graph_from_goto_functions.callsites.empty());
121+
}
114122
}
115123

116124
WHEN("The call graph is inverted")
117125
{
118126
call_grapht inverse_call_graph_from_goto_functions=
119127
call_graph_from_goto_functions.get_inverted();
120-
THEN("We expect A -> { A }, B -> { A }, C -> { B }, D -> { B }")
128+
THEN("We expect A -> { A }, B -> { A, A }, C -> { B }, D -> { B }")
121129
{
122130
const auto &check_graph=inverse_call_graph_from_goto_functions.graph;
123-
REQUIRE(check_graph.size()==4);
131+
REQUIRE(check_graph.size()==5);
124132
REQUIRE(multimap_key_matches(check_graph, "A", {"A"}));
125-
REQUIRE(multimap_key_matches(check_graph, "B", {"A"}));
133+
REQUIRE(multimap_key_matches(check_graph, "B", {"A", "A"}));
126134
REQUIRE(multimap_key_matches(check_graph, "C", {"B"}));
127135
REQUIRE(multimap_key_matches(check_graph, "D", {"B"}));
128136
}
129137
}
130138

139+
WHEN("A call graph is constructed with call-site tracking")
140+
{
141+
call_grapht call_graph_from_goto_functions(goto_model, true);
142+
THEN("We expect two callsites for the A -> B edge, one for all others")
143+
{
144+
const auto &check_callsites=call_graph_from_goto_functions.callsites;
145+
for(const auto &edge : call_graph_from_goto_functions.graph)
146+
{
147+
if(edge==call_grapht::grapht::value_type("A", "B"))
148+
REQUIRE(check_callsites.at(edge).size()==2);
149+
else
150+
REQUIRE(check_callsites.at(edge).size()==1);
151+
}
152+
}
153+
WHEN("Such a graph is inverted")
154+
{
155+
call_grapht inverted=call_graph_from_goto_functions.get_inverted();
156+
THEN("The callsite data should be discarded")
157+
{
158+
REQUIRE(inverted.callsites.empty());
159+
}
160+
}
161+
}
131162
}
132-
133163
}

0 commit comments

Comments
 (0)