Skip to content

Commit aaa8513

Browse files
committed
Call graph -> grapht transformation
Add export from call_grapht to grapht, such that generic graph algorithms can be applied to the call graph.
1 parent 8115248 commit aaa8513

File tree

3 files changed

+165
-0
lines changed

3 files changed

+165
-0
lines changed

src/analyses/call_graph.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,64 @@ call_grapht call_grapht::get_inverted() const
8787
return result;
8888
}
8989

90+
/// Helper class that maintains a map from function name to grapht node index
91+
/// and adds nodes to the graph on demand.
92+
class function_indicest
93+
{
94+
typedef call_grapht::directed_grapht::node_indext node_indext;
95+
call_grapht::directed_grapht &graph;
96+
97+
public:
98+
std::unordered_map<irep_idt, node_indext, irep_id_hash> function_indices;
99+
100+
explicit function_indicest(call_grapht::directed_grapht &graph):
101+
graph(graph)
102+
{
103+
}
104+
105+
node_indext operator[](const irep_idt &function)
106+
{
107+
auto findit=function_indices.insert({function, 0});
108+
if(findit.second)
109+
{
110+
node_indext new_index=graph.add_node();
111+
findit.first->second=new_index;
112+
graph[new_index].function=function;
113+
}
114+
return findit.first->second;
115+
}
116+
};
117+
118+
/// Returns a `grapht` representation of this call graph, suitable for use
119+
/// with generic grapht algorithms. Note that parallel edges in call_grapht
120+
/// (e.g. A { B(); B(); } appearing as two A->B edges) will be condensed in
121+
/// the grapht output, so only one edge will appear. If `collect_callsites`
122+
/// was set when this call-graph was constructed the edge will be annotated
123+
/// with the call-site set.
124+
/// \return grapht representation of this call_grapht
125+
call_grapht::directed_grapht call_grapht::get_directed_graph() const
126+
{
127+
call_grapht::directed_grapht ret;
128+
function_indicest function_indices(ret);
129+
130+
for(const auto &edge : graph)
131+
{
132+
auto a_index=function_indices[edge.first];
133+
auto b_index=function_indices[edge.second];
134+
// Check then create the edge like this to avoid copying the callsites
135+
// set once per parallel edge, which could be costly if there are many.
136+
if(!ret.has_edge(a_index, b_index))
137+
{
138+
ret.add_edge(a_index, b_index);
139+
if(collect_callsites)
140+
ret[a_index].out[b_index].callsites=callsites.at(edge);
141+
}
142+
}
143+
144+
ret.nodes_by_name=std::move(function_indices.function_indices);
145+
return ret;
146+
}
147+
90148
std::string call_grapht::format_callsites(const edget &edge) const
91149
{
92150
PRECONDITION(collect_callsites);
@@ -144,3 +202,13 @@ void call_grapht::output_xml(std::ostream &out) const
144202
out << "\">\n";
145203
}
146204
}
205+
206+
optionalt<std::size_t> call_grapht::directed_grapht::get_node_index(
207+
const irep_idt &function) const
208+
{
209+
auto findit=nodes_by_name.find(function);
210+
if(findit==nodes_by_name.end())
211+
return optionalt<node_indext>();
212+
else
213+
return findit->second;
214+
}

src/analyses/call_graph.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Author: Daniel Kroening, [email protected]
1616
#include <map>
1717

1818
#include <goto-programs/goto_model.h>
19+
#include <util/graph.h>
1920

2021
class call_grapht
2122
{
@@ -41,6 +42,44 @@ class call_grapht
4142
void add(const irep_idt &caller, const irep_idt &callee, locationt callsite);
4243
call_grapht get_inverted() const;
4344

45+
struct edge_with_callsitest
46+
{
47+
locationst callsites;
48+
};
49+
50+
struct function_nodet:public graph_nodet<edge_with_callsitest>
51+
{
52+
irep_idt function;
53+
};
54+
55+
/// Directed graph representation of this call graph
56+
class directed_grapht : public ::grapht<function_nodet>
57+
{
58+
friend class call_grapht;
59+
60+
/// Maps function names onto node indices
61+
std::unordered_map<irep_idt, node_indext, irep_id_hash> nodes_by_name;
62+
63+
public:
64+
/// Find the graph node by function name
65+
/// \param function: function to find
66+
/// \return none if function is not in this graph, or some index otherwise.
67+
optionalt<node_indext> get_node_index(const irep_idt &function) const;
68+
69+
/// Type of the node name -> node index map.
70+
typedef
71+
std::unordered_map<irep_idt, node_indext, irep_id_hash> nodes_by_namet;
72+
73+
/// Get the node name -> node index map
74+
/// \return node-by-name map
75+
const nodes_by_namet &get_nodes_by_name() const
76+
{
77+
return nodes_by_name;
78+
}
79+
};
80+
81+
directed_grapht get_directed_graph() const;
82+
4483
protected:
4584
void add(const irep_idt &function,
4685
const goto_programt &body);

unit/analyses/call_graph.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,5 +159,63 @@ SCENARIO("call_graph",
159159
}
160160
}
161161
}
162+
163+
WHEN("The call graph is exported as a grapht")
164+
{
165+
call_grapht::directed_call_grapht exported=
166+
call_graph_from_goto_functions.get_directed_graph();
167+
168+
typedef call_grapht::directed_call_grapht::node_indext node_indext;
169+
std::map<irep_idt, node_indext> nodes_by_name;
170+
for(node_indext i=0; i<exported.size(); ++i)
171+
nodes_by_name[exported[i].function]=i;
172+
173+
THEN("We expect edges A -> { A, B }, B -> { C, D }")
174+
{
175+
// Note that means the extra A -> B edge has gone away (the grapht
176+
// structure can't represent the parallel edge)
177+
REQUIRE(exported.has_edge(nodes_by_name["A"], nodes_by_name["A"]));
178+
REQUIRE(exported.has_edge(nodes_by_name["A"], nodes_by_name["B"]));
179+
REQUIRE(exported.has_edge(nodes_by_name["B"], nodes_by_name["C"]));
180+
REQUIRE(exported.has_edge(nodes_by_name["B"], nodes_by_name["D"]));
181+
}
182+
}
183+
184+
WHEN("The call graph, with call sites, is exported as a grapht")
185+
{
186+
call_grapht call_graph_from_goto_functions(goto_model, true);
187+
call_grapht::directed_call_grapht exported=
188+
call_graph_from_goto_functions.get_directed_graph();
189+
190+
typedef call_grapht::directed_call_grapht::node_indext node_indext;
191+
std::map<irep_idt, node_indext> nodes_by_name;
192+
for(node_indext i=0; i<exported.size(); ++i)
193+
nodes_by_name[exported[i].function]=i;
194+
195+
THEN("We expect edges A -> { A, B }, B -> { C, D }")
196+
{
197+
// Note that means the extra A -> B edge has gone away (the grapht
198+
// structure can't represent the parallel edge)
199+
REQUIRE(exported.has_edge(nodes_by_name["A"], nodes_by_name["A"]));
200+
REQUIRE(exported.has_edge(nodes_by_name["A"], nodes_by_name["B"]));
201+
REQUIRE(exported.has_edge(nodes_by_name["B"], nodes_by_name["C"]));
202+
REQUIRE(exported.has_edge(nodes_by_name["B"], nodes_by_name["D"]));
203+
}
204+
205+
THEN("We expect all edges to have one callsite apart from A -> B with 2")
206+
{
207+
for(node_indext i=0; i<exported.size(); ++i)
208+
{
209+
const auto &node=exported[i];
210+
for(const auto &edge : node.out)
211+
{
212+
if(i==nodes_by_name["A"] && edge.first==nodes_by_name["B"])
213+
REQUIRE(edge.second.callsites.size()==2);
214+
else
215+
REQUIRE(edge.second.callsites.size()==1);
216+
}
217+
}
218+
}
219+
}
162220
}
163221
}

0 commit comments

Comments
 (0)