-
Notifications
You must be signed in to change notification settings - Fork 273
Rewriting call_grapht to inherit from grapht<T>, and introduce reachable_call_graph class #1509
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
Conversation
This was needed after an update to goto-cc, which caused previously-compiled binaries used in the test suite to trigger an assertion failure. Also updated instructions on how to generate these binaries. This commit fixes diffblue#1376.
Without the "digraph call_graph{" this is not parsable dot. This change makes the output dot consistent with the previous output_dot function in call_grapht
…it from grapht<T> I have rewritten call_grapht to inherit from grapht, because I think there should be fewer graph classes in CBMC. Instead of constructing the entire call graph, the reachable call graph class constructs only the reachable call graph. There is some duplication between the call_graph class and the show_call_sequences function, however the show_call_sequences function outputs the graph on the fly, instead of constructing it, which doesn't allow for further use of the graph
Michael's comments on CR-766007 highlighted things that should be changed in grapht.
Returns the list of functions on the shortest path from a src function to a destination function on the function call graph.
bb0327f
to
799d034
Compare
74d0638
to
635071e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So given I was recently burned by merging something that changed a public interface, I think we should try to keep it stable here.
call_grapht
exposed all of its implementation, inviting users to directly inspect the std::multimap<irep_idt, irep_idt>
it used to store the graph. Therefore if we don't want to surprise external users, we should keep that.
That's why #1498 keeps the original call_grapht
interface but adds the ability to export it in grapht
form. I propose we merge #1498 and then build this functionality (the single-root call graph and backward-reachability functions for example) on top of it. I'm happy to do the porting work.
|
||
On Ubuntu, you can get a suitable compiler using: | ||
To regenerate object.arm on an x86_64 machine, you will need to install |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think these changes belong in this PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not entirely sure why this commit has appeared in the PR, since it is the head commit of the develop branch (or was when I created this branch). I created the branch by checking out develop and then cherry-picking my commits on top of it.
Nevertheless, I need to rebase against develop again anyway so I will make it disappear then
out << "}\n"; | ||
void call_grapht::output_xml(std::ostream &out) const | ||
{ | ||
for(node_indext n = 0; n < nodes.size(); n++) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use C++11 for-loop (i.e. for(const auto &node : nodes) output_xml_node(out, node);
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason for this for loop is that the output_xml_node function requires a node indext to be passed to it.
The reason the output_xml_node function requires a node indext to be passed to it, rather than a reference to the node is to preserve the compatability with the output functions of grapht (see grapht::output_dot_node).
And the reason graphtoutput_dot_node needs to have the node index passed to it is because it outputs that node index (instead of outputting data stored in the node, because the template doesn't know what might be contained in the node).
In order to use a C++11 for loop I would have to reimplement the output functions of grapht, would you still prefer that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK never mind then
src/analyses/call_graph.cpp
Outdated
} | ||
|
||
void call_grapht::output_xml(std::ostream &out) const | ||
std::list<irep_idt>call_grapht::shortest_function_path |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Space after >
src/analyses/call_graph.cpp
Outdated
} | ||
|
||
void call_grapht::output_xml(std::ostream &out) const | ||
std::list<irep_idt>call_grapht::shortest_function_path | ||
(irep_idt src, irep_idt dest) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Open-paren on previous line, indent this by two
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"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're throwing, throw a structured error (probably just call_graph_errort : public std::invalid_argument {}
that a caller can selectively catch
/// \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( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Members of out
are guaranteed to be unique, so we could return vector
here and let the caller decide whether they need the more-costly hashtable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. Actually the only place this is called is the unit test, so it could just be a function within the unit test
irep_idt caller=*working_queue.begin(); | ||
working_queue.erase(working_queue.begin()); | ||
|
||
if(!done.insert(caller).second) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given this is happening, I don't think working_queue
needs to itself be a set type
{ | ||
irep_idt callee = to_symbol_expr(function_expr).get_identifier(); | ||
add(caller, callee); | ||
working_queue.insert(callee); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above, depth-first search would mean cheaper types (stacks made out of vectors) for the working set
{ | ||
std::unordered_set<irep_idt, irep_id_hash> result; | ||
std::list<node_indext> worklist; | ||
for(node_indext idx=0; idx<nodes.size(); idx++) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use get_node_index
} | ||
INVARIANT(!worklist.empty(), "destination function not found"); | ||
|
||
while(!worklist.empty()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Factor this and the forward-reachable function above
Can we keep the original call graph interface and the one built on top of grapht for some period of time, but then eventually remove the original one? That would allow external users time to adapt their code to the new one, but also wouldn't be adding to the already very large collection of graph classes in CBMC |
Sure sounds good. I'll put together a PR cherry-picked from these two that combines their features... |
Ok, thanks. It might take me a couple of days to get round to the changes in here, but I hope I will have them done by the end of the week |
To avoid duplicating effort: making a merged version of these two PRs now... |
Made a draft here for review: https://github.com/smowton/cbmc/tree/call_graph_polgreen_draft (The latest commit adds most of the features from this PR on top of #1498 -- it still needs the DOT/XML output stuff porting however) |
Thanks Chris! Sorry I haven't had time to look at any of this, I should be able to get to it next week. I'll do the re-implementation of the BFS as DFS etc on top of a copy of your new combined pull request, is that ok? I can also do the dot/xml stuff |
The DFS is already in there actually (it dropped out naturally from tweaking grapht a little). DOT/XML would be much appreciated though. I was also considering whether perhaps to avoid cluttering call_graph.h too much, perhaps the get-successors/predecessors/reachable-nodes functions should move into some call_graph_helpers.h as out-of-line functions, since they can do all their work using grapht's public interface? |
That works for me. An alternative, if there is no likely use for them elsewhere, would be to integrate them into the call graph based slicer i built them for. I'll look and see if that would actually improve the efficiency of the slicer at all, and maybe we can have that discussion once i've created a pull request for it |
Sounds good to me. Note the graph now hosts a name->index map, so the lookup is cheaper than it was. |
The pull request we are commenting on is identical to the internal code review you looked at. Chris has created a branch, which merges this pull request and #1498 : https://github.com/smowton/cbmc/tree/call_graph_polgreen_draft I need to add in the dot output changes, which I haven't got round to doing yet. |
@tautschnig @polgreen finally got around to completing the merge! You can find the results at my original PR, #1498 |
Yes, closing this as subsumed by #1498 |
I have rewritten the call_grapht class to inherit from grapht. There are many graph functions implemented for grapht that it would be useful to have for call_grapht. It also makes traversing the call graph backwards easier.
Rewriting call_grapht also highlighted some minor changes in grapht that should be made.