14
14
#include < util/std_expr.h>
15
15
#include < util/xml.h>
16
16
17
- call_grapht::call_grapht ()
17
+ // / Create empty call graph
18
+ // / \param collect_callsites: if true, then each added graph edge will have
19
+ // / the calling instruction recorded in `callsites` map.
20
+ call_grapht::call_grapht (bool collect_callsites):
21
+ collect_callsites(collect_callsites)
18
22
{
19
23
}
20
24
21
- call_grapht::call_grapht (const goto_modelt &goto_model):
22
- call_grapht(goto_model.goto_functions)
25
+ // / Create complete call graph
26
+ // / \param goto_model: model to search for callsites
27
+ // / \param collect_callsites: if true, then each added graph edge will have
28
+ // / the calling instruction recorded in `callsites` map.
29
+ call_grapht::call_grapht (const goto_modelt &goto_model, bool collect_callsites):
30
+ call_grapht(goto_model.goto_functions, collect_callsites)
23
31
{
24
32
}
25
33
26
- call_grapht::call_grapht (const goto_functionst &goto_functions)
34
+ // / Create complete call graph
35
+ // / \param goto_functions: functions to search for callsites
36
+ // / \param collect_callsites: if true, then each added graph edge will have
37
+ // / the calling instruction recorded in `callsites` map.
38
+ call_grapht::call_grapht (
39
+ const goto_functionst &goto_functions, bool collect_callsites):
40
+ collect_callsites(collect_callsites)
27
41
{
28
42
forall_goto_functions (f_it, goto_functions)
29
43
{
@@ -32,28 +46,107 @@ call_grapht::call_grapht(const goto_functionst &goto_functions)
32
46
}
33
47
}
34
48
35
- void call_grapht::add (
36
- const irep_idt &function ,
37
- const goto_programt &body )
49
+ static void forall_callsites (
50
+ const goto_programt &body ,
51
+ std::function< void (goto_programt::const_targett, const irep_idt &)> call_task )
38
52
{
39
53
forall_goto_program_instructions (i_it, body)
40
54
{
41
55
if (i_it->is_function_call ())
42
56
{
43
57
const exprt &function_expr=to_code_function_call (i_it->code ).function ();
44
58
if (function_expr.id ()==ID_symbol)
45
- add (function, to_symbol_expr (function_expr).get_identifier ());
59
+ {
60
+ const irep_idt &callee=to_symbol_expr (function_expr).get_identifier ();
61
+ call_task (i_it, callee);
62
+ }
46
63
}
47
64
}
48
65
}
49
66
67
+ // / Create call graph restricted to functions reachable from `root`
68
+ // / \param goto_functions: functions to search for callsites
69
+ // / \param root: function to start exploring the graph
70
+ // / \param collect_callsites: if true, then each added graph edge will have
71
+ // / the calling instruction recorded in `callsites` map.
72
+ call_grapht::call_grapht (
73
+ const goto_functionst &goto_functions,
74
+ const irep_idt &root,
75
+ bool collect_callsites)
76
+ {
77
+ std::stack<irep_idt, std::vector<irep_idt>> pending_stack;
78
+ pending_stack.push (root);
79
+
80
+ while (!pending_stack.empty ())
81
+ {
82
+ irep_idt function=pending_stack.top ();
83
+ pending_stack.pop ();
84
+ const goto_programt &goto_program=
85
+ goto_functions.function_map .at (function).body ;
86
+
87
+ forall_callsites (
88
+ goto_program,
89
+ [&](goto_programt::const_targett i_it, const irep_idt &callee)
90
+ {
91
+ add (function, callee, i_it);
92
+ if (graph.find (callee)==graph.end ())
93
+ pending_stack.push (callee);
94
+ }
95
+ ); // NOLINT
96
+ }
97
+ }
98
+
99
+ // / Create call graph restricted to functions reachable from `root`
100
+ // / \param goto_model: model to search for callsites
101
+ // / \param root: function to start exploring the graph
102
+ // / \param collect_callsites: if true, then each added graph edge will have
103
+ // / the calling instruction recorded in `callsites` map.
104
+ call_grapht::call_grapht (
105
+ const goto_modelt &goto_model,
106
+ const irep_idt &root,
107
+ bool collect_callsites):
108
+ call_grapht(goto_model.goto_functions, root, collect_callsites)
109
+ {
110
+ }
111
+
112
+ void call_grapht::add (
113
+ const irep_idt &function,
114
+ const goto_programt &body)
115
+ {
116
+ forall_callsites (
117
+ body,
118
+ [&](goto_programt::const_targett i_it, const irep_idt &callee)
119
+ {
120
+ add (function, callee, i_it);
121
+ }
122
+ ); // NOLINT
123
+ }
124
+
125
+ // / Add edge
126
+ // / \param caller: caller function
127
+ // / \param callee: callee function
50
128
void call_grapht::add (
51
129
const irep_idt &caller,
52
130
const irep_idt &callee)
53
131
{
54
132
graph.insert (std::pair<irep_idt, irep_idt>(caller, callee));
55
133
}
56
134
135
+ // / Add edge with optional callsite information
136
+ // / \param caller: caller function
137
+ // / \param callee: callee function
138
+ // / \param callsite: call instruction responsible for this edge. Note this is
139
+ // / only stored if `collect_callsites` was specified during construction.
140
+ void call_grapht::add (
141
+ const irep_idt &caller,
142
+ const irep_idt &callee,
143
+ locationt callsite)
144
+ {
145
+ add (caller, callee);
146
+ if (collect_callsites)
147
+ callsites[{caller, callee}].insert (callsite);
148
+ }
149
+
57
150
// / Returns an inverted copy of this call graph
58
151
// / \return Inverted (callee -> caller) call graph
59
152
call_grapht call_grapht::get_inverted () const
@@ -64,6 +157,82 @@ call_grapht call_grapht::get_inverted() const
64
157
return result;
65
158
}
66
159
160
+ // / Helper class that maintains a map from function name to grapht node index
161
+ // / and adds nodes to the graph on demand.
162
+ class function_indicest
163
+ {
164
+ typedef call_grapht::directed_grapht::node_indext node_indext;
165
+ call_grapht::directed_grapht &graph;
166
+
167
+ public:
168
+ std::unordered_map<irep_idt, node_indext, irep_id_hash> function_indices;
169
+
170
+ explicit function_indicest (call_grapht::directed_grapht &graph):
171
+ graph(graph)
172
+ {
173
+ }
174
+
175
+ node_indext operator [](const irep_idt &function)
176
+ {
177
+ auto findit=function_indices.insert ({function, 0 });
178
+ if (findit.second )
179
+ {
180
+ node_indext new_index=graph.add_node ();
181
+ findit.first ->second =new_index;
182
+ graph[new_index].function =function;
183
+ }
184
+ return findit.first ->second ;
185
+ }
186
+ };
187
+
188
+ // / Returns a `grapht` representation of this call graph, suitable for use
189
+ // / with generic grapht algorithms. Note that parallel edges in call_grapht
190
+ // / (e.g. A { B(); B(); } appearing as two A->B edges) will be condensed in
191
+ // / the grapht output, so only one edge will appear. If `collect_callsites`
192
+ // / was set when this call-graph was constructed the edge will be annotated
193
+ // / with the call-site set.
194
+ // / \return grapht representation of this call_grapht
195
+ call_grapht::directed_grapht call_grapht::get_directed_graph () const
196
+ {
197
+ call_grapht::directed_grapht ret;
198
+ function_indicest function_indices (ret);
199
+
200
+ for (const auto &edge : graph)
201
+ {
202
+ auto a_index=function_indices[edge.first ];
203
+ auto b_index=function_indices[edge.second ];
204
+ // Check then create the edge like this to avoid copying the callsites
205
+ // set once per parallel edge, which could be costly if there are many.
206
+ if (!ret.has_edge (a_index, b_index))
207
+ {
208
+ ret.add_edge (a_index, b_index);
209
+ if (collect_callsites)
210
+ ret[a_index].out [b_index].callsites =callsites.at (edge);
211
+ }
212
+ }
213
+
214
+ ret.nodes_by_name =std::move (function_indices.function_indices );
215
+ return ret;
216
+ }
217
+
218
+ // / Prints callsites responsible for a graph edge as comma-separated
219
+ // / location numbers, e.g. "{1, 2, 3}".
220
+ // / \param edge: graph edge
221
+ // / \return pretty representation of edge callsites
222
+ std::string call_grapht::format_callsites (const edget &edge) const
223
+ {
224
+ PRECONDITION (collect_callsites);
225
+ std::string ret=" {" ;
226
+ for (const locationt &loc : callsites.at (edge))
227
+ {
228
+ if (ret.size ()>1 )
229
+ ret+=" , " ;
230
+ ret+=std::to_string (loc->location_number );
231
+ }
232
+ ret+=' }' ;
233
+ return ret;
234
+ }
235
+
67
236
void call_grapht::output_dot (std::ostream &out) const
68
237
{
69
238
out << " digraph call_graph {\n " ;
@@ -72,8 +241,10 @@ void call_grapht::output_dot(std::ostream &out) const
72
241
{
73
242
out << " \" " << edge.first << " \" -> "
74
243
<< " \" " << edge.second << " \" "
75
- << " [arrowhead=\" vee\" ];"
76
- << " \n " ;
244
+ << " [arrowhead=\" vee\" " ;
245
+ if (collect_callsites)
246
+ out << " label=\" " << format_callsites (edge) << " \" " ;
247
+ out << " ];\n " ;
77
248
}
78
249
79
250
out << " }\n " ;
@@ -84,11 +255,18 @@ void call_grapht::output(std::ostream &out) const
84
255
for (const auto &edge : graph)
85
256
{
86
257
out << edge.first << " -> " << edge.second << " \n " ;
258
+ if (collect_callsites)
259
+ out << " (callsites: " << format_callsites (edge) << " )\n " ;
87
260
}
88
261
}
89
262
90
263
void call_grapht::output_xml (std::ostream &out) const
91
264
{
265
+ // Note I don't implement callsite output here; I'll leave that
266
+ // to the first interested XML user.
267
+ if (collect_callsites)
268
+ out << " <!-- XML call-graph representation does not document callsites yet."
269
+ " If you need this, edit call_grapht::output_xml -->\n " ;
92
270
for (const auto &edge : graph)
93
271
{
94
272
out << " <call_graph_edge caller=\" " ;
@@ -98,3 +276,13 @@ void call_grapht::output_xml(std::ostream &out) const
98
276
out << " \" >\n " ;
99
277
}
100
278
}
279
+
280
+ optionalt<std::size_t > call_grapht::directed_grapht::get_node_index (
281
+ const irep_idt &function) const
282
+ {
283
+ auto findit=nodes_by_name.find (function);
284
+ if (findit==nodes_by_name.end ())
285
+ return optionalt<node_indext>();
286
+ else
287
+ return findit->second ;
288
+ }
0 commit comments