Skip to content

Commit f6ddabc

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 3ba6d06 commit f6ddabc

File tree

3 files changed

+130
-1
lines changed

3 files changed

+130
-1
lines changed

src/analyses/call_graph.cpp

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,65 @@ call_grapht call_grapht::get_inverted() const
8181
return result;
8282
}
8383

84+
/// Helper class that maintains a map from function name to grapht node index
85+
/// and adds nodes to the graph on demand.
86+
class function_indicest
87+
{
88+
typedef call_grapht::directed_call_grapht::node_indext node_indext;
89+
std::map<irep_idt, node_indext> function_indices;
90+
call_grapht::directed_call_grapht &graph;
91+
92+
public:
93+
explicit function_indicest(call_grapht::directed_call_grapht &graph):
94+
graph(graph)
95+
{
96+
}
97+
98+
node_indext operator[](const irep_idt &function)
99+
{
100+
auto findit=function_indices.insert({function, 0});
101+
if(findit.second)
102+
{
103+
node_indext new_index=graph.add_node();
104+
findit.first->second=new_index;
105+
graph[new_index].function=function;
106+
}
107+
return findit.first->second;
108+
}
109+
};
110+
111+
/// Returns a `grapht` representation of this call graph, suitable for use
112+
/// with generic grapht algorithms. Note that parallel edges in call_grapht
113+
/// (e.g. A { B(); B(); } appearing as two A->B edges) will be condensed in
114+
/// the grapht output, so only one edge will appear. If `collect_callsites`
115+
/// was set when this call-graph was constructed the edge will be annotated
116+
/// with the call-site set.
117+
/// \return grapht representation of this call_grapht
118+
call_grapht::directed_call_grapht call_grapht::get_directed_graph() const
119+
{
120+
call_grapht::directed_call_grapht ret;
121+
function_indicest function_indices(ret);
122+
123+
for(const auto &edge : graph)
124+
{
125+
auto a_index=function_indices[edge.first];
126+
auto b_index=function_indices[edge.second];
127+
// Check then create the edge like this to avoid copying the callsites
128+
// set once per parallel edge, which could be costly if there are many.
129+
if(!ret.has_edge(a_index, b_index))
130+
{
131+
ret.add_edge(a_index, b_index);
132+
if(collect_callsites)
133+
ret[a_index].out[b_index].callsites=callsites.at(edge);
134+
}
135+
}
136+
137+
return ret;
138+
}
139+
84140
std::string call_grapht::format_callsites(const edget &edge) const
85141
{
86-
PRECONDITION(collect_callsites, "collect_callsites should be enabled");
142+
PRECONDITION(collect_callsites);
87143
std::string ret="{";
88144
for(const locationt &loc : callsites.at(edge))
89145
{

src/analyses/call_graph.h

Lines changed: 15 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,20 @@ 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+
typedef ::grapht<function_nodet> directed_call_grapht;
56+
57+
directed_call_grapht get_directed_graph() const;
58+
4459
protected:
4560
void add(const irep_idt &function,
4661
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)