Skip to content

Complexity graph #7081

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

Draft
wants to merge 36 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
aef4738
initial cfg dot program
bquiring May 31, 2022
831ccf5
disable default
bquiring May 31, 2022
6070b36
exclude unreachable functions from the control flow graph
bquiring Jun 2, 2022
6f7aea6
revise graph styling, add new metrics
bquiring Jun 6, 2022
78ee6fc
add metrics, use forall loops
bquiring Jun 9, 2022
a220835
refactor, add symex instrumentation and add this to default cbmc call…
bquiring Jun 14, 2022
e028920
add solver instrumentation
bquiring Jun 15, 2022
78c66fc
instrument solver to fail, and constant propagator to be dumber. Disa…
bquiring Jun 16, 2022
e214941
add option to track symex coverage without generating a report
bquiring Jun 16, 2022
dafcdcb
moved around files. updated but broken
bquiring Jun 17, 2022
2a81ed8
working
bquiring Jun 17, 2022
30a73b2
add dumping of function instructions
bquiring Jun 20, 2022
0b528e5
better displaying of instructions
bquiring Jun 20, 2022
c6b8d15
display instruction-level complexity
bquiring Jun 28, 2022
8eaff6a
disable conver_assignments debugging
bquiring Jun 28, 2022
9cd0f0e
fix html bug with dollar signs and colons and add instruction dumping…
bquiring Jun 28, 2022
6b03cbb
updates
bquiring Jun 28, 2022
e5f15ee
rebase
bquiring Aug 22, 2022
3104abf
add short README
bquiring Aug 17, 2022
b92fb2b
small change
bquiring Aug 17, 2022
0cf70f7
fix build break
bquiring Aug 17, 2022
ea706fd
rebase
bquiring Aug 22, 2022
18b269a
updates to metrics and display
bquiring Aug 17, 2022
ab365ff
update library function list
bquiring Aug 17, 2022
d9e6f43
update library function styling
bquiring Aug 17, 2022
d06a8f5
add document and refactor
bquiring Aug 22, 2022
945486e
more refactor and documentation
bquiring Aug 22, 2022
51933bf
revert changes to remove_function_pointers
bquiring Aug 22, 2022
8ae5f37
patch CBMC bug for loop computation
bquiring Aug 23, 2022
ac97769
updates to documentation
bquiring Aug 23, 2022
a3b027e
address Jim's comments
bquiring Aug 25, 2022
0ff6966
revert files
bquiring Aug 25, 2022
3eedead
revert files
bquiring Aug 25, 2022
e03d478
update command line flags
bquiring Aug 25, 2022
fdadc05
move README
bquiring Aug 25, 2022
f99b996
small
bquiring Aug 25, 2022
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
116 changes: 116 additions & 0 deletions doc/cprover-manual/complexity-graph.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
[CPROVER Manual TOC](../)

## Complexity Graph

This tool is meant to help decompose CBMC proofs.
It does this by producing a control-flow graph that contains information about where CBMC may run into issues.

```
goto-instrument FILE.goto FLAGS
```

The recommended file to use is `FUNCTION_harness1.goto` which is produced from the Makefile provided in the starter kit. If this is not available, use `goto-cc` to compile all relevant source files into a `.goto` file.
We recommend this file because it is produced before any program transformations are made, which can complicate the graph.

## Relevant FLAGS

--show-complexity-graph FILE.dot
instructs CBMC to write the graph to the .dot file
This can be compiled to a PDF by using the command
```
dot -Tpdf FILE.dot -o FILE.pdf
```


--complexity-graph-roots FUNCTION-NAME
Instructs CBMC to generate the graph spanned by this function. This flag can be passed multiple times and produces one graph spanned by all roots. If no roots are provided, all functions in the provided source files will be used.
Typically this will be `FUNCTION_harness`.


--remove-function-body FUNCTION-NAME
Removes the body of a function, which has the effect of removing all outedges of that vertex from the graph.


--omit-function FUNCTION-NAME
Omits a function from the graph entirely. Useful for removing high-indegree sink nodes to reduce visual clutter.


--constant-propagator
Recommended to run for the reason that it simplifies the underlying program, and hence the graph.


--omit-function-pointers
Removes function pointer edges from the graph, which have a habit of producing visual clutter.


--add-library
Includes the CBMC library for functions like `malloc`, `free`, etc.

--complexity-graph-global-scores
Uses a more global notion of scoring, where the "global score" for a node is the number of paths to that node multiplied by the sum of the global scores of its children.

The following flags can be used with the main CBMC executable
```
cbmc FILE.goto FLAGS
```

--show-complexity-graph-with-symex FILE.dot
Write the complexity graph with symbolic execution information to FILE.dot.


--show-complexity-graph-with-solver FILE.dot
Write the complexity graph with symbolic execution and solver formula information to FILE.dot.
This flag requires the addition of `--write-solver-stats-to FILE.out` to obtain solver formula information.


--complexity-graph-instructions
When used with '--show-complexity-graph-with-symex' or '--show-complexity-graph-with-solver' displays the instructions of the program that have a high cost in symbolic execution and/or the solver formula.

## Reading the graph

### Node shapes

- Rectangluar nodes: normal functions
- Elliptical nodes: private functions
- Diamond nodes: functions with no body
- Arrow-shaped nodes: function pointers
- Pentagon: standard/CBMC library functions

### Node colors

The intensity of red coloring on a node is a display of its complexity relative to other nodes in the graph.

When using `--show-complexity-graph-with-symex` with `--complexity-graph-instructions`, intensity of pink on instructions shows the complexity of that instruction in symbolic execution, and intensity of yellow shows the complexity of that instruction in the creation of the solver formula.


## Sample interaction

Suppose we have a function `foobar` and we'd like to do a proof for it.
After creating a GOTO file `foobar_harness1.goto` with the starter kit or `goto-cc`, try the following

```
goto-instrument foobar_harness1.goto --show-complexity-graph graph.dot --complexity-graph-roots foobar
```
Followed by
```
dot -Tpdf graph.dot -o graph.pdf
```

This will display the call graph for foobar.
The first step is to take steps to clean the graph.

Likely, you can ignore function pointers at this point. See [this page](http://cprover.diffblue.com/md__home_travis_build_diffblue_cbmc_doc_architectural_restrict-function-pointer.html) for how to restrict function pointers - the information provided in the graph displays CBMC's sound overapproximation of what functions can be targets of which function pointers.

Additionally, there may be some high-indegree functions in the graph that don't appear to be useful and just complicate the graph.
Let's say one of those functions is `notuseful`.
We can then run a new command
```
goto-instrument foobar_harness1.goto --show-complexity-graph graph.dot --complexity-graph-roots foobar --complexity-graph-omit-function-pointers --complexity-graph-omit-function notuseful
```
Which should provide a cleaner graph to inspect.


When doing a proof, you should use information provided in the graph along with some domain knowledge about the codebase to decide where to decompose. See `doc/cprover-manual/contracts-functions.md` for a tutorial on proof decomposition using function contracts, which we refer to as abstracting a function.
It's good to abstract functions that are complex. In the case that there is a simple function `g` that calls a complex function `f`, it may be better to abstract `g` if the contract is easier to write.
A good way to approximate where a contract is easy to write is by how many memory locations a function can write to.

53 changes: 53 additions & 0 deletions src/cbmc/cbmc_parse_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Author: Daniel Kroening, [email protected]
#include <goto-programs/remove_skip.h>
#include <goto-programs/remove_unused_functions.h>
#include <goto-programs/set_properties.h>
#include <goto-programs/show_complexity_graph.h>
#include <goto-programs/show_goto_functions.h>
#include <goto-programs/show_properties.h>
#include <goto-programs/show_symbol_table.h>
Expand Down Expand Up @@ -334,8 +335,10 @@ void cbmc_parse_optionst::get_command_line_options(optionst &options)
options.set_option("trace", true);
}

options.set_option("symex-record-coverage", cmdline.get_values ("symex-record-coverage"));
if(cmdline.isset("symex-coverage-report"))
{
options.set_option("symex-record-coverage", true);
options.set_option(
"symex-coverage-report",
cmdline.get_value("symex-coverage-report"));
Expand Down Expand Up @@ -372,6 +375,51 @@ void cbmc_parse_optionst::get_command_line_options(optionst &options)
parse_solver_options(cmdline, options);
}

void set_complexity_graph_options (const cmdlinet &cmdline, optionst &options) {
if (cmdline.isset ("show-complexity-graph-with-symex")
|| cmdline.isset ("show-complexity-graph-with-solver")) {
options.set_option("symex-record-coverage", true);
}

options.set_option ("show-complexity-graph", cmdline.get_values ("show-complexity-graph"));
options.set_option ("show-complexity-graph-with-symex", cmdline.get_values ("show-complexity-graph-with-symex"));
options.set_option ("show-complexity-graph-with-solver", cmdline.get_values ("show-complexity-graph-with-solver"));
if (cmdline.isset ("complexity-graph-root"))
{
std::stringstream stream;
for (const std::string &val : cmdline.get_values("complexity-graph-root"))
{
stream << val << ",";
}
options.set_option ("complexity-graph-roots", stream.str());
}

if (cmdline.isset ("complexity-graph-omit-function"))
{
std::stringstream stream;
for (const std::string &val : cmdline.get_values("complexity-graph-omit-function"))
{
stream << val << ",";
}
options.set_option ("complexity-graph-omit-function", stream.str());
}

if (cmdline.isset ("complexity-graph-global-scores"))
{
options.set_option ("complexity-graph-global-scores", true);
}

if (cmdline.isset ("complexity-graph-omit-function-pointers"))
{
options.set_option ("complexity-graph-omit-function-pointers", true);
}

if (cmdline.isset ("complexity-graph-instructions"))
{
options.set_option ("complexity-graph-instructions", true);
}
}

/// invoke main modules
int cbmc_parse_optionst::doit()
{
Expand Down Expand Up @@ -499,6 +547,8 @@ int cbmc_parse_optionst::doit()
return CPROVER_EXIT_SUCCESS;
}

set_complexity_graph_options (cmdline, options);

int get_goto_program_ret =
get_goto_program(goto_model, options, cmdline, ui_message_handler);

Expand Down Expand Up @@ -635,6 +685,7 @@ int cbmc_parse_optionst::doit()
verifier = util_make_unique<
all_properties_verifier_with_trace_storaget<multi_path_symex_checkert>>(
options, ui_message_handler, goto_model);
// TODO: this is the branch triggered by default
Copy link
Collaborator

Choose a reason for hiding this comment

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

What needs to be done here? Maybe file an issue on whatever it is instead of leaving this isn the code.

}
}
else
Expand Down Expand Up @@ -894,6 +945,8 @@ void cbmc_parse_optionst::help()
" --show-parse-tree show parse tree\n"
" --show-symbol-table show loaded symbol table\n"
HELP_SHOW_GOTO_FUNCTIONS
HELP_SHOW_COMPLEXITY_GRAPH
HELP_SHOW_COMPLEXITY_GRAPH_CBMC
HELP_VALIDATE
"\n"
"Program instrumentation options:\n"
Expand Down
3 changes: 3 additions & 0 deletions src/cbmc/cbmc_parse_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ class optionst;
OPT_SOLVER \
OPT_STRING_REFINEMENT_CBMC \
OPT_SHOW_GOTO_FUNCTIONS \
"(symex-record-coverage)" \
OPT_COMPLEXITY_GRAPH \
OPT_COMPLEXITY_GRAPH_CBMC \
OPT_SHOW_PROPERTIES \
"(show-symbol-table)(show-parse-tree)" \
"(drop-unused-functions)" \
Expand Down
106 changes: 106 additions & 0 deletions src/goto-checker/multi_path_symex_checker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Author: Daniel Kroening, Peter Schrammel
#include "counterexample_beautification.h"
#include "goto_symex_fault_localizer.h"

#include <goto-programs/show_complexity_graph.h>

multi_path_symex_checkert::multi_path_symex_checkert(
const optionst &options,
ui_message_handlert &ui_message_handler,
Expand All @@ -31,6 +33,103 @@ multi_path_symex_checkert::multi_path_symex_checkert(
{
}

// generates a complexity graph associated with the given query,
// possibly with symex and SAT formula information.
void generate_goto_dot (const abstract_goto_modelt &goto_model,
const symex_bmct &symex,
const optionst &options,
ui_message_handlert &ui_message_handler,
const goto_symex_property_decidert &property_decider,
const std::string &type)
{
const std::string path = options.get_option(type);
if (!path.empty())
{
const auto &goto_functions = goto_model.get_goto_functions();

messaget msg(ui_message_handler);
const namespacet ns(goto_model.get_symbol_table());
const auto sorted = goto_functions.sorted();

const symex_coveraget &symex_coverage = symex.get_coverage();
std::map<goto_programt::const_targett, symex_infot> instr_symex_info;
std::map<goto_programt::const_targett, solver_infot> instr_solver_info;

// populate instr_symex_info
for(const auto &fun : sorted)
{
const bool has_body = fun->second.body_available();

if (has_body)
{
const goto_programt &body = fun->second.body;
forall_goto_program_instructions(from, body)
{
const goto_programt::const_targetst to_list = symex_coverage.coverage_to (from);
size_t total_steps = 0;
double total_duration = 0.0;
for (goto_programt::const_targett to : to_list)
{
total_steps += symex_coverage.num_executions(from, to);
total_duration += symex_coverage.duration(from, to);
}

symex_infot info;
info.steps = total_steps;
info.duration = total_duration;

instr_symex_info.insert ({from, info});
}
}
}

// populate instr_solver_info
with_solver_hardness(
property_decider.get_decision_procedure(),
[&instr_solver_info](solver_hardnesst &solver_hardness)
{
// source: solver_hardness.cpp solver_hardnesst::produce_report
const std::vector<std::unordered_map<solver_hardnesst::hardness_ssa_keyt, solver_hardnesst::sat_hardnesst>> &hardness_stats = solver_hardness.get_hardness_stats();
for(std::size_t i = 0; i < hardness_stats.size(); i++)
{
const auto &ssa_step_hardness = hardness_stats[i];
for(const auto &key_value_pair : ssa_step_hardness)
{
auto const &ssa = key_value_pair.first;
auto const &hardness = key_value_pair.second;
const goto_programt::const_targett target = ssa.pc;

auto ensure_exists = instr_solver_info.find (target);
if (ensure_exists == instr_solver_info.end())
{
solver_infot solver_info;
instr_solver_info.insert ({target, solver_info});
}

auto entry = instr_solver_info.find (target);
solver_infot &solver_info = entry->second;
solver_info.clauses += hardness.clauses;
solver_info.literals += hardness.literals;
solver_info.variables += hardness.variables.size();
}
}
});

if (type == "show-complexity-graph")
{
show_complexity_graph(options, goto_model, path, ui_message_handler);
} else if (type == "show-complexity-graph-with-symex")
{
show_complexity_graph(options, goto_model, path, ui_message_handler,
instr_symex_info);
} else if (type == "show-complexity-graph-with-solver")
{
show_complexity_graph(options, goto_model, path, ui_message_handler,
instr_symex_info, instr_solver_info);
}
}
}

incremental_goto_checkert::resultt multi_path_symex_checkert::
operator()(propertiest &properties)
{
Expand All @@ -46,7 +145,12 @@ operator()(propertiest &properties)
// we haven't got an equation yet
if(!equation_generated)
{

generate_goto_dot(goto_model, symex, options, ui_message_handler, property_decider, "show-complexity-graph");

generate_equation();

generate_goto_dot(goto_model, symex, options, ui_message_handler, property_decider, "show-complexity-graph-with-symex");

output_coverage_report(
options.get_option("symex-coverage-report"),
Expand All @@ -67,6 +171,8 @@ operator()(propertiest &properties)

run_property_decider(result, properties, solver_runtime);

generate_goto_dot(goto_model, symex, options, ui_message_handler, property_decider, "show-complexity-graph-with-solver");

return result;
}

Expand Down
Loading