Skip to content

Commit 29cf00c

Browse files
marek-trtikpeterschrammel
authored andcommitted
Extension of the call-graph and call-graph-based algorithms (#641)
Added mapping from edges of the call-graph to particular instructions in the caller GOTO programs, where the calls are performed. Added new functions find_direct_or_indirect_callees_of_function and find_nearest_common_callees. Clean up output_dot.
1 parent c694703 commit 29cf00c

File tree

2 files changed

+205
-36
lines changed

2 files changed

+205
-36
lines changed

src/analyses/call_graph.cpp

+163-27
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ Author: Daniel Kroening, [email protected]
66
77
\*******************************************************************/
88

9+
#include "call_graph.h"
910
#include <util/std_expr.h>
1011
#include <util/xml.h>
12+
#include <algorithm>
13+
#include <iterator>
1114

12-
#include "call_graph.h"
1315

1416
/*******************************************************************\
1517
@@ -70,11 +72,31 @@ void call_grapht::add(
7072
{
7173
const exprt &function_expr=to_code_function_call(i_it->code).function();
7274
if(function_expr.id()==ID_symbol)
73-
add(function, to_symbol_expr(function_expr).get_identifier());
75+
add(function, to_symbol_expr(function_expr).get_identifier(), {i_it});
7476
}
7577
}
7678
}
7779

80+
/*******************************************************************\
81+
82+
Function: call_grapht::swap
83+
84+
Inputs:
85+
86+
Outputs:
87+
88+
Purpose:
89+
90+
\*******************************************************************/
91+
92+
void call_grapht::swap(call_grapht &other)
93+
{
94+
std::swap(graph, other.graph);
95+
std::swap(map_from_edges_to_call_locations,
96+
other.map_from_edges_to_call_locations);
97+
}
98+
99+
78100
/*******************************************************************\
79101
80102
Function: call_grapht::add
@@ -96,7 +118,7 @@ void call_grapht::add(
96118

97119
/*******************************************************************\
98120
99-
Function: call_grapht::output_dot
121+
Function: call_grapht::add
100122
101123
Inputs:
102124
@@ -106,37 +128,60 @@ Function: call_grapht::output_dot
106128
107129
\*******************************************************************/
108130

109-
void call_grapht::output_dot(std::ostream &out) const
131+
void call_grapht::add(
132+
const irep_idt &caller,
133+
const irep_idt &callee,
134+
const map_from_edges_to_call_locationst::mapped_type &call_sites)
110135
{
111-
out << "digraph call_graph {\n";
112-
113-
for(const auto &edge : graph)
136+
bool exists=false;
137+
const call_grapht::call_edges_ranget range=out_edges(caller);
138+
for(auto it=range.first; it!=range.second; ++it)
114139
{
115-
out << " \"" << edge.first << "\" -> "
116-
<< "\"" << edge.second << "\" "
117-
<< " [arrowhead=\"vee\"];"
118-
<< "\n";
140+
if(it->second==callee)
141+
{
142+
exists=true;
143+
break;
144+
}
119145
}
120-
121-
out << "}\n";
146+
if(!exists)
147+
add(caller, callee);
148+
std::copy(
149+
call_sites.cbegin(), call_sites.cend(),
150+
std::back_inserter(map_from_edges_to_call_locations[{caller, callee}]));
122151
}
123152

124153

125-
void call_grapht::output_dot(
126-
const goto_functionst &functions,
127-
std::ostream &out) const
154+
/*******************************************************************\
155+
156+
Function: call_grapht::output_dot
157+
158+
Inputs:
159+
160+
Outputs:
161+
162+
Purpose:
163+
164+
\*******************************************************************/
165+
166+
void call_grapht::output_dot(std::ostream &out) const
128167
{
129-
out << "digraph call_graph {\n";
130-
for(const auto &elem : functions.function_map)
131-
out << " \"" << elem.first << "\";\n";
132-
for(grapht::const_iterator it=graph.begin();
133-
it!=graph.end();
134-
it++)
168+
out << "digraph call_graph {\n"
169+
<< " node [fontsize=12 shape=box];\n";
170+
for(const auto &edge : graph)
135171
{
136-
out << " \"" << it->first << "\" -> "
137-
<< "\"" << it->second << "\" "
138-
<< " [arrowhead=\"vee\"];"
139-
<< "\n";
172+
out << " \"" << edge.first << "\" -> "
173+
<< "\"" << edge.second << "\" "
174+
<< " [label=\"{";
175+
bool first=true;
176+
for(const auto instr_it :
177+
get_map_from_edges_to_call_locations().at({edge.first, edge.second}))
178+
{
179+
if(!first)
180+
out << ",";
181+
out << instr_it->location_number;
182+
first=false;
183+
}
184+
out << "}\"];\n";
140185
}
141186
out << "}\n";
142187
}
@@ -382,7 +427,10 @@ void compute_inverted_call_graph(
382427
{
383428
assert(output_inverted_call_graph.graph.empty());
384429
for(const auto &elem : original_call_graph.graph)
385-
output_inverted_call_graph.add(elem.second, elem.first);
430+
output_inverted_call_graph.add(
431+
elem.second, elem.first,
432+
original_call_graph.get_map_from_edges_to_call_locations().at(
433+
{elem.first, elem.second}));
386434
}
387435

388436
/*******************************************************************\
@@ -443,3 +491,91 @@ void find_leaves_below_function(
443491
std::unordered_set<irep_idt, dstring_hash> to_avoid;
444492
find_leaves_below_function(call_graph, function, to_avoid, output);
445493
}
494+
495+
/*******************************************************************\
496+
497+
Function: find_direct_or_indirect_callees_of_function
498+
499+
Inputs:
500+
501+
Outputs:
502+
503+
Purpose:
504+
505+
\*******************************************************************/
506+
507+
void find_direct_or_indirect_callees_of_function(
508+
const call_grapht &call_graph,
509+
const irep_idt &function,
510+
std::unordered_set<irep_idt, dstring_hash> &output)
511+
{
512+
std::unordered_set<irep_idt, dstring_hash> leaves;
513+
find_leaves_below_function(call_graph, function, output, leaves);
514+
output.insert(leaves.cbegin(), leaves.cend());
515+
}
516+
517+
/*******************************************************************\
518+
519+
Function: find_nearest_common_callees
520+
521+
Inputs:
522+
523+
Outputs:
524+
525+
Purpose:
526+
527+
\*******************************************************************/
528+
529+
void find_nearest_common_callees(
530+
const call_grapht &call_graph,
531+
const std::set<irep_idt> &functions,
532+
std::set<irep_idt> &output)
533+
{
534+
if(functions.empty())
535+
return;
536+
if(functions.size()==1UL)
537+
{
538+
output.insert(*functions.cbegin());
539+
return;
540+
}
541+
542+
std::map<irep_idt, std::size_t> counting;
543+
for(const auto &elem : call_graph.graph)
544+
{
545+
counting[elem.first]=0U;
546+
counting[elem.second]=0U;
547+
}
548+
for(const auto &fn : functions)
549+
{
550+
std::unordered_set<irep_idt, dstring_hash> callees;
551+
find_direct_or_indirect_callees_of_function(call_graph, fn, callees);
552+
assert(callees.count(fn)==1U);
553+
for(const auto &callee : callees)
554+
++counting[callee];
555+
}
556+
557+
std::set<irep_idt> leaves;
558+
for(const auto &elem : counting)
559+
if(elem.second!=0U)
560+
{
561+
const call_grapht::call_edges_ranget range=
562+
call_graph.out_edges(elem.first);
563+
if(range.first==range.second)
564+
leaves.insert(elem.first);
565+
}
566+
567+
for(auto &elem : counting)
568+
if(leaves.count(elem.first)!=0UL)
569+
output.insert(elem.first);
570+
else if(elem.second!=0U && elem.second<functions.size())
571+
{
572+
const call_grapht::call_edges_ranget range=
573+
call_graph.out_edges(elem.first);
574+
for(auto it=range.first; it!=range.second; ++it)
575+
{
576+
auto cit=counting.find(it->second);
577+
if(cit->second==functions.size())
578+
output.insert(cit->first);
579+
}
580+
}
581+
}

src/analyses/call_graph.h

+42-9
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,31 @@ class call_grapht
2323
explicit call_grapht(const goto_functionst &);
2424

2525
void output_dot(std::ostream &out) const;
26-
27-
/**
28-
* It writes this into the passed stream in the Graphviz's DOT format.
29-
* The method accepts also functions, because the callgraph does not
30-
* store funtions (nodes). It only stores edges (from caller to callee).
31-
* So, the resulting graph would not show not-called functions.
32-
*/
33-
void output_dot(const goto_functionst &functions, std::ostream &out) const;
34-
3526
void output(std::ostream &out) const;
3627
void output_xml(std::ostream &out) const;
3728

3829
typedef std::multimap<irep_idt, irep_idt> grapht;
3930
grapht graph;
4031

32+
void swap(call_grapht &other);
33+
4134
void add(const irep_idt &caller, const irep_idt &callee);
4235

36+
/**
37+
* The type provides a mapping from edges of the call-graph to particular
38+
* instructions in the caller GOTO programs, where the calls are performed.
39+
*/
40+
typedef std::map<
41+
std::pair<irep_idt, irep_idt>,
42+
std::vector<goto_programt::instructionst::const_iterator> >
43+
map_from_edges_to_call_locationst;
44+
45+
const map_from_edges_to_call_locationst &
46+
get_map_from_edges_to_call_locations() const
47+
{ return map_from_edges_to_call_locations; }
48+
49+
void add(const irep_idt &caller, const irep_idt &callee,
50+
const map_from_edges_to_call_locationst::mapped_type &call_sites);
4351
protected:
4452
void add(const irep_idt &function,
4553
const goto_programt &body);
@@ -74,6 +82,9 @@ class call_grapht
7482
* std::cout << it->second << ", ";
7583
*/
7684
call_edges_ranget out_edges(const irep_idt &caller) const;
85+
86+
private:
87+
map_from_edges_to_call_locationst map_from_edges_to_call_locations;
7788
};
7889

7990
/*******************************************************************\
@@ -160,4 +171,26 @@ void find_leaves_below_function(
160171
const irep_idt &function,
161172
std::unordered_set<irep_idt, dstring_hash> &output);
162173

174+
void find_direct_or_indirect_callees_of_function(
175+
const call_grapht &call_graph,
176+
const irep_idt &function,
177+
std::unordered_set<irep_idt, dstring_hash> &output);
178+
179+
void find_nearest_common_callees(
180+
const call_grapht &call_graph,
181+
const std::set<irep_idt> &functions,
182+
std::set<irep_idt> &output);
183+
184+
/**
185+
* The "callee" must be a DIRECT callee of the "caller" in the "call_graph".
186+
*/
187+
inline const std::vector<goto_programt::instructionst::const_iterator> &
188+
get_call_sites(
189+
const call_grapht &call_graph,
190+
const irep_idt &caller,
191+
const irep_idt &callee)
192+
{
193+
return call_graph.get_map_from_edges_to_call_locations().at({caller, callee});
194+
}
195+
163196
#endif // CPROVER_ANALYSES_CALL_GRAPH_H

0 commit comments

Comments
 (0)