Skip to content

Aggressive slicer #1587

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/analyses/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ SRC = ai.cpp \
locals.cpp \
natural_loops.cpp \
reaching_definitions.cpp \
reachable_call_graph.cpp \
static_analysis.cpp \
uncaught_exceptions_analysis.cpp \
uninitialized_domain.cpp \
Expand Down
169 changes: 136 additions & 33 deletions src/analyses/call_graph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Author: Daniel Kroening, [email protected]
\*******************************************************************/

/// \file
/// Function Call Graphs
/// Function Call Graph

#include "call_graph.h"

Expand All @@ -18,14 +18,9 @@ call_grapht::call_grapht()
{
}

call_grapht::call_grapht(const goto_modelt &goto_model):
call_grapht(goto_model.goto_functions)
call_grapht::call_grapht(const goto_modelt &goto_model)
{
}

call_grapht::call_grapht(const goto_functionst &goto_functions)
{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove this constructor?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this is left over from call graph changes. Will revert

forall_goto_functions(f_it, goto_functions)
forall_goto_functions(f_it, goto_model.goto_functions)
{
const goto_programt &body=f_it->second.body;
add(f_it->first, body);
Expand All @@ -51,50 +46,158 @@ void call_grapht::add(
const irep_idt &caller,
const irep_idt &callee)
{
graph.insert(std::pair<irep_idt, irep_idt>(caller, callee));
std::size_t caller_idx = node_numbering.number(caller);
if(caller_idx >= nodes.size())
{
node_indext node_index = add_node();
nodes[node_index].function_name = caller;
}

std::size_t callee_idx = node_numbering.number(callee);
if(callee_idx >= nodes.size())
{
node_indext node_index = add_node();
nodes[node_index].function_name = callee;
}

add_edge(caller_idx, callee_idx);
}


void call_grapht::output_dot_node(std::ostream &out, node_indext n) const
{
const nodet &node = nodes.at(n);

for(const auto &edge : node.out)
{
out << " \"" << node.function_name << "\" -> " << "\""
<< nodes[edge.first].function_name << "\" " << " [arrowhead=\"vee\"];"
<< "\n";
}
}

void call_grapht::output_xml_node(std::ostream &out, node_indext n) const
{
const nodet &node = nodes.at(n);

for(const auto &edge : node.out)
{
out << "<call_graph_edge caller=\"";
xmlt::escape_attribute(id2string(node.function_name), out);
out << "\" callee=\"";
xmlt::escape_attribute(id2string(nodes[edge.first].function_name), out);
out << "\">\n";
}
}

void call_grapht::output_xml(std::ostream &out) const
{
for(node_indext n = 0; n < nodes.size(); n++)
output_xml_node(out, n);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would asking for an output_json be unreasonable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not unreasonable, it should also be added to grapht, which doesn't have one either.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That'd be lovely.


/// Returns an inverted copy of this call graph
/// \return Inverted (callee -> caller) call graph
call_grapht call_grapht::get_inverted() const
{
call_grapht result;
for(const auto &caller_callee : graph)
result.add(caller_callee.second, caller_callee.first);
for(const auto &n : nodes)
{
for(const auto &i : n.in)
result.add(n.function_name, nodes[i.first].function_name);
}
return result;
}

void call_grapht::output_dot(std::ostream &out) const
std::list<irep_idt> call_grapht::shortest_function_path(
irep_idt src, irep_idt dest)
{
out << "digraph call_graph {\n";

for(const auto &edge : graph)
std::list<irep_idt> result;
node_indext src_idx, dest_idx;
if(!get_node_index(src, src_idx))
throw "unable to find src function in call graph";
if(!get_node_index(dest, dest_idx))
throw "unable to find destination function in call graph";

patht path;
shortest_path(src_idx, dest_idx, path);
for(const auto &n : path)
{
out << " \"" << edge.first << "\" -> "
<< "\"" << edge.second << "\" "
<< " [arrowhead=\"vee\"];"
<< "\n";
result.push_back(nodes[n].function_name);
}

out << "}\n";
return result;
}

void call_grapht::output(std::ostream &out) const
void call_grapht::reachable_within_n_steps(
std::size_t steps,
std::unordered_set<irep_idt,
irep_id_hash> & function_list)
{
for(const auto &edge : graph)
std::list<node_indext> worklist;

for(const auto &f : function_list)
{
out << edge.first << " -> " << edge.second << "\n";
node_indext start_index;
if(get_node_index(f, start_index))
worklist.push_back(start_index);
else
throw "function not found in call graph";
}

// mark end of level 0
worklist.push_back(std::numeric_limits<std::size_t>::max());
std::size_t depth=0;

while(!worklist.empty())
{
const node_indext id = worklist.front();
worklist.pop_front();

// check if we have hit end of level
if(id == std::numeric_limits<std::size_t>::max())
{
depth++;
// mark end of next level
if(!worklist.empty())
worklist.push_back(id);
continue;
}
function_list.insert(nodes[id].function_name);

if(depth < steps)
{
for(const auto &o : nodes[id].out)
{
if(function_list.find(nodes[o.first].function_name)
== function_list.end())
worklist.push_back(o.first);
}
}
}
}

void call_grapht::output_xml(std::ostream &out) const
std::unordered_set<irep_idt, irep_id_hash>
call_grapht::reachable_functions(irep_idt start_function)
{
for(const auto &edge : graph)
std::unordered_set<irep_idt, irep_id_hash> result;
std::list<node_indext> worklist;
node_indext start_index;

if(get_node_index(start_function, start_index))
worklist.push_back(start_index);
else
throw "no start function found in call graph";

while(!worklist.empty())
{
out << "<call_graph_edge caller=\"";
xmlt::escape_attribute(id2string(edge.first), out);
out << "\" callee=\"";
xmlt::escape_attribute(id2string(edge.second), out);
out << "\">\n";
const node_indext id = worklist.front();
worklist.pop_front();

result.insert(nodes[id].function_name);
for(const auto &o : nodes[id].out)
{
if(result.find(nodes[o.first].function_name) == result.end())
worklist.push_back(o.first);
}
}

return result;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much work would it be to move these algorithms out into util/graph.h. It seems like this might not be the only place we want to do a shortest path or bounded depth search.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is already a branch by Smowton that combines my call graph pull request and his, so I think I should probably incorporate this change into that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. If the two of you get together on this I'd be happy to review.

93 changes: 84 additions & 9 deletions src/analyses/call_graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,109 @@ Author: Daniel Kroening, [email protected]
\*******************************************************************/

/// \file
/// Function Call Graphs
/// Function Call Graph

#ifndef CPROVER_ANALYSES_CALL_GRAPH_H
#define CPROVER_ANALYSES_CALL_GRAPH_H

#include <iosfwd>
#include <map>
#include <unordered_set>

#include <goto-programs/goto_model.h>

class call_grapht
#include <util/irep.h>
#include <util/graph.h>
#include <util/numbering.h>


/// \brief Function call graph: each node represents a function
/// in the GOTO model, a directed edge from A to B indicates
/// that function A calls function B.
/// Inherits from grapht to allow forward and
/// backward traversal of the function call graph
struct call_graph_nodet: public graph_nodet<empty_edget>
{
typedef graph_nodet<empty_edget>::edget edget;
typedef graph_nodet<empty_edget>::edgest edgest;

irep_idt function_name;
bool visited = false;
};

class call_grapht: public grapht<call_graph_nodet>
{
public:
call_grapht();
explicit call_grapht(const goto_modelt &);
explicit call_grapht(const goto_functionst &);

void output_dot(std::ostream &out) const;
void output(std::ostream &out) const;
void add(const irep_idt &caller, const irep_idt &callee);
void output_xml(std::ostream &out) const;

typedef std::multimap<irep_idt, irep_idt> grapht;
grapht graph;

void add(const irep_idt &caller, const irep_idt &callee);
/// \return the inverted call graph
call_grapht get_inverted() const;

/// \brief get the names of all functions reachable from a start function
/// \param start name of initial function
/// \return set of all names of the reachable functions
std::unordered_set<irep_idt, irep_id_hash>
reachable_functions(irep_idt start);

/// \brief Function returns the shortest path on the function call graph
/// between a source and a destination function
/// \param src name of the starting function
/// \param dest name of the destination function
/// \return list of function names on the shortest path between src and dest
std::list<irep_idt>shortest_function_path(irep_idt src, irep_idt dest);

/// \brief get the names of all functions reachable from a list of functions
/// within N function call steps.
/// \param function_list list of functions to start from. Functions reachable within
/// N function call steps are appended to this list
/// \param steps number of function call steps
void reachable_within_n_steps(std::size_t steps,
std::unordered_set<irep_idt, irep_id_hash> &function_list);


/// get the index of the node that corresponds to a function name
/// \param[in] function_name function_name passed by reference
/// \param[out] n variable for the node index to be written to
/// \return true if a node with the given function name exists,
/// false if it does not exist
bool get_node_index(const irep_idt& function_name, node_indext &n) const
{
for(node_indext idx = 0; idx < nodes.size(); idx++)
{
if(nodes[idx].function_name == function_name)
{
n = idx;
return true;
}
}
return false;
}

/// \brief get a list of functions called by a function
/// \param function_name the irep_idt of the function
/// \return an unordered set of all functions called by function_name
std::unordered_set<irep_idt, irep_id_hash> get_successors(
const irep_idt& function_name) const
{
std::unordered_set<irep_idt, irep_id_hash> result;
node_indext function_idx;
if(!get_node_index(function_name, function_idx))
return result;

for(const auto &o : nodes[function_idx].out)
result.insert(nodes[o.first].function_name);
return result;
}


protected:
void output_dot_node(std::ostream &out, node_indext n) const override;
void output_xml_node(std::ostream &out, node_indext n) const;
numbering<irep_idt> node_numbering;
void add(const irep_idt &function,
const goto_programt &body);
};
Expand Down
Loading