diff --git a/jbmc/src/jbmc/Makefile b/jbmc/src/jbmc/Makefile index 1efad8a920b..ec96389eefc 100644 --- a/jbmc/src/jbmc/Makefile +++ b/jbmc/src/jbmc/Makefile @@ -1,4 +1,6 @@ -SRC = jbmc_main.cpp \ +SRC = all_properties.cpp \ + bmc.cpp \ + jbmc_main.cpp \ jbmc_parse_options.cpp \ # Empty last line @@ -39,8 +41,6 @@ OBJ += ../$(CPROVER_DIR)/src/ansi-c/ansi-c$(LIBEXT) \ ../$(CPROVER_DIR)/src/util/util$(LIBEXT) \ ../miniz/miniz$(OBJEXT) \ ../$(CPROVER_DIR)/src/json/json$(LIBEXT) \ - ../$(CPROVER_DIR)/src/cbmc/all_properties$(OBJEXT) \ - ../$(CPROVER_DIR)/src/cbmc/bmc$(OBJEXT) \ # Empty last line INCLUDES= -I .. -I ../$(CPROVER_DIR)/src diff --git a/jbmc/src/jbmc/all_properties.cpp b/jbmc/src/jbmc/all_properties.cpp new file mode 100644 index 00000000000..89273d31d31 --- /dev/null +++ b/jbmc/src/jbmc/all_properties.cpp @@ -0,0 +1,303 @@ +/*******************************************************************\ + +Module: Symbolic Execution of ANSI-C + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +/// \file +/// Symbolic Execution of ANSI-C + +#include "all_properties_class.h" + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +void bmc_all_propertiest::goal_covered(const cover_goalst::goalt &) +{ + for(auto &g : goal_map) + { + // failed already? + if(g.second.status == goalt::statust::FAILURE) + continue; + + // check whether failed + for(auto &c : g.second.instances) + { + literalt cond = c->cond_literal; + + if(solver.l_get(cond).is_false()) + { + g.second.status = goalt::statust::FAILURE; + build_goto_trace(bmc.equation, c, solver, bmc.ns, g.second.goto_trace); + break; + } + } + } +} + +safety_checkert::resultt bmc_all_propertiest::operator()() +{ + status() << "Passing problem to " << solver.decision_procedure_text() << eom; + + auto solver_start = std::chrono::steady_clock::now(); + + convert_symex_target_equation( + bmc.equation, bmc.prop_conv, get_message_handler()); + bmc.freeze_program_variables(); + + // Collect _all_ goals in `goal_map'. + // This maps property IDs to 'goalt' + forall_goto_functions(f_it, goto_functions) + forall_goto_program_instructions(i_it, f_it->second.body) + if(i_it->is_assert()) + goal_map[i_it->source_location.get_property_id()] = goalt(*i_it); + + // get the conditions for these goals from formula + // collect all 'instances' of the properties + for(symex_target_equationt::SSA_stepst::iterator it = + bmc.equation.SSA_steps.begin(); + it != bmc.equation.SSA_steps.end(); + it++) + { + if(it->is_assert()) + { + irep_idt property_id = it->get_property_id(); + + if(property_id.empty()) + continue; + + if(it->source.pc->is_goto()) + { + // goto may yield an unwinding assertion + goal_map[property_id].description = it->comment; + } + + goal_map[property_id].instances.push_back(it); + } + } + + do_before_solving(); + + cover_goalst cover_goals(solver); + + cover_goals.register_observer(*this); + + for(const auto &g : goal_map) + { + // Our goal is to falsify a property, i.e., we will + // add the negation of the property as goal. + literalt p = !solver.convert(g.second.as_expr()); + cover_goals.add(p); + } + + status() << "Running " << solver.decision_procedure_text() << eom; + + bool error = false; + + const decision_proceduret::resultt result = + cover_goals(get_message_handler()); + + if(result == decision_proceduret::resultt::D_ERROR) + { + error = true; + for(auto &g : goal_map) + if(g.second.status == goalt::statust::UNKNOWN) + g.second.status = goalt::statust::ERROR; + } + else + { + for(auto &g : goal_map) + if(g.second.status == goalt::statust::UNKNOWN) + g.second.status = goalt::statust::SUCCESS; + } + + { + auto solver_stop = std::chrono::steady_clock::now(); + + statistics() + << "Runtime decision procedure: " + << std::chrono::duration(solver_stop - solver_start).count() + << "s" << eom; + } + + // report + report(cover_goals); + + if(error) + return safety_checkert::resultt::ERROR; + + bool safe = (cover_goals.number_covered() == 0); + + if(safe) + report_success(bmc.ui_message_handler); // legacy, might go away + else + report_failure(bmc.ui_message_handler); // legacy, might go away + + return safe ? safety_checkert::resultt::SAFE + : safety_checkert::resultt::UNSAFE; +} + +void bmc_all_propertiest::report(const cover_goalst &cover_goals) +{ + switch(bmc.ui_message_handler.get_ui()) + { + case ui_message_handlert::uit::PLAIN: + { + result() << "\n** Results:" << eom; + + // collect goals in a vector + std::vector goals; + + for(auto g_it = goal_map.begin(); g_it != goal_map.end(); g_it++) + goals.push_back(g_it); + + // now determine an ordering for those goals: + // 1. alphabetical ordering of file name + // 2. numerical ordering of line number + // 3. alphabetical ordering of goal ID + std::sort( + goals.begin(), + goals.end(), + [](goal_mapt::const_iterator git1, goal_mapt::const_iterator git2) { + const auto &g1 = git1->second.source_location; + const auto &g2 = git2->second.source_location; + if(g1.get_file() != g2.get_file()) + return id2string(g1.get_file()) < id2string(g2.get_file()); + else if(!g1.get_line().empty() && !g2.get_line().empty()) + return std::stoul(id2string(g1.get_line())) < + std::stoul(id2string(g2.get_line())); + else + return id2string(git1->first) < id2string(git2->first); + }); + + // now show in the order we have determined + + irep_idt previous_function; + irep_idt current_file; + for(const auto &g : goals) + { + const auto &l = g->second.source_location; + + if(l.get_function() != previous_function) + { + if(!previous_function.empty()) + result() << '\n'; + previous_function = l.get_function(); + if(!previous_function.empty()) + { + current_file = l.get_file(); + if(!current_file.empty()) + result() << current_file << ' '; + if(!l.get_function().empty()) + result() << "function " << l.get_function(); + result() << eom; + } + } + + result() << faint << '[' << g->first << "] " << reset; + + if(l.get_file() != current_file) + result() << "file " << l.get_file() << ' '; + + if(!l.get_line().empty()) + result() << "line " << l.get_line() << ' '; + + result() << g->second.description << ": "; + + if(g->second.status == goalt::statust::SUCCESS) + result() << green; + else + result() << red; + + result() << g->second.status_string() << reset << eom; + } + + if(bmc.options.get_bool_option("trace")) + { + for(const auto &g : goal_map) + if(g.second.status == goalt::statust::FAILURE) + { + result() << "\n" + << "Trace for " << g.first << ":" + << "\n"; + show_goto_trace( + result(), bmc.ns, g.second.goto_trace, bmc.trace_options()); + result() << eom; + } + } + + status() << "\n** " << cover_goals.number_covered() << " of " + << cover_goals.size() << " failed (" << cover_goals.iterations() + << " iteration" << (cover_goals.iterations() == 1 ? "" : "s") + << ")" << eom; + } + break; + + case ui_message_handlert::uit::XML_UI: + { + for(const auto &g : goal_map) + { + xmlt xml_result( + "result", + {{"property", id2string(g.first)}, + {"status", g.second.status_string()}}, + {}); + + if(g.second.status == goalt::statust::FAILURE) + convert(bmc.ns, g.second.goto_trace, xml_result.new_element()); + + result() << xml_result; + } + break; + } + + case ui_message_handlert::uit::JSON_UI: + { + if(result().tellp() > 0) + result() << eom; // force end of previous message + json_stream_objectt &json_result = + bmc.ui_message_handler.get_json_stream().push_back_stream_object(); + json_stream_arrayt &result_array = + json_result.push_back_stream_array("result"); + + for(const auto &g : goal_map) + { + json_stream_objectt &result = result_array.push_back_stream_object(); + result["property"] = json_stringt(g.first); + result["description"] = json_stringt(g.second.description); + result["status"] = json_stringt(g.second.status_string()); + + if(g.second.status == goalt::statust::FAILURE) + { + json_stream_arrayt &json_trace = result.push_back_stream_array("trace"); + convert( + bmc.ns, g.second.goto_trace, json_trace, bmc.trace_options()); + } + } + } + break; + } +} + +safety_checkert::resultt +bmct::all_properties(const goto_functionst &goto_functions) +{ + bmc_all_propertiest bmc_all_properties(goto_functions, prop_conv, *this); + bmc_all_properties.set_message_handler(get_message_handler()); + return bmc_all_properties(); +} diff --git a/src/cbmc/all_properties_class.h b/jbmc/src/jbmc/all_properties_class.h similarity index 72% rename from src/cbmc/all_properties_class.h rename to jbmc/src/jbmc/all_properties_class.h index 293babda092..a5fc881ada4 100644 --- a/src/cbmc/all_properties_class.h +++ b/jbmc/src/jbmc/all_properties_class.h @@ -17,16 +17,14 @@ Author: Daniel Kroening, kroening@kroening.com #include "bmc.h" -class bmc_all_propertiest: - public cover_goalst::observert, - public messaget +class bmc_all_propertiest : public cover_goalst::observert, public messaget { public: bmc_all_propertiest( const goto_functionst &_goto_functions, prop_convt &_solver, - bmct &_bmc): - goto_functions(_goto_functions), solver(_solver), bmc(_bmc) + bmct &_bmc) + : goto_functions(_goto_functions), solver(_solver), bmc(_bmc) { } @@ -44,17 +42,27 @@ class bmc_all_propertiest: source_locationt source_location; // if failed, we compute a goto_trace for the first failing instance - enum statust { UNKNOWN, FAILURE, SUCCESS, ERROR } status; + enum statust + { + UNKNOWN, + FAILURE, + SUCCESS, + ERROR + } status; goto_tracet goto_trace; std::string status_string() const { switch(status) { - case UNKNOWN: return "UNKNOWN"; - case FAILURE: return "FAILURE"; - case SUCCESS: return "SUCCESS"; - case ERROR: return "ERROR"; + case UNKNOWN: + return "UNKNOWN"; + case FAILURE: + return "FAILURE"; + case SUCCESS: + return "SUCCESS"; + case ERROR: + return "ERROR"; } // make some poor compilers happy @@ -62,15 +70,14 @@ class bmc_all_propertiest: return ""; } - explicit goalt( - const goto_programt::instructiont &instruction): - status(statust::UNKNOWN) + explicit goalt(const goto_programt::instructiont &instruction) + : status(statust::UNKNOWN) { source_location = instruction.source_location; - description=id2string(instruction.source_location.get_comment()); + description = id2string(instruction.source_location.get_comment()); } - goalt():status(statust::UNKNOWN) + goalt() : status(statust::UNKNOWN) { } @@ -93,7 +100,9 @@ class bmc_all_propertiest: bmct &bmc; virtual void report(const cover_goalst &cover_goals); - virtual void do_before_solving() {} + virtual void do_before_solving() + { + } }; #endif // CPROVER_CBMC_ALL_PROPERTIES_CLASS_H diff --git a/src/cbmc/bmc.cpp b/jbmc/src/jbmc/bmc.cpp similarity index 92% rename from src/cbmc/bmc.cpp rename to jbmc/src/jbmc/bmc.cpp index 44caf9e5280..e9b590a8ffa 100644 --- a/src/cbmc/bmc.cpp +++ b/jbmc/src/jbmc/bmc.cpp @@ -42,8 +42,8 @@ void bmct::freeze_program_variables() decision_proceduret::resultt bmct::run_decision_procedure() { - status() << "Passing problem to " - << prop_conv.decision_procedure_text() << eom; + status() << "Passing problem to " << prop_conv.decision_procedure_text() + << eom; auto solver_start = std::chrono::steady_clock::now(); @@ -54,7 +54,7 @@ decision_proceduret::resultt bmct::run_decision_procedure() status() << "Running " << prop_conv.decision_procedure_text() << eom; - decision_proceduret::resultt dec_result=prop_conv.dec_solve(); + decision_proceduret::resultt dec_result = prop_conv.dec_solve(); { auto solver_stop = std::chrono::steady_clock::now(); @@ -67,14 +67,13 @@ decision_proceduret::resultt bmct::run_decision_procedure() return dec_result; } -safety_checkert::resultt bmct::execute( - abstract_goto_modelt &goto_model) +safety_checkert::resultt bmct::execute(abstract_goto_modelt &goto_model) { try { - auto get_goto_function = [&goto_model](const irep_idt &id) -> - const goto_functionst::goto_functiont & - { + auto get_goto_function = + [&goto_model]( + const irep_idt &id) -> const goto_functionst::goto_functiont & { return goto_model.get_goto_function(id); }; @@ -84,8 +83,7 @@ safety_checkert::resultt bmct::execute( // iterators pointing into it, must not be stored by this function or its // callees, as goto_model.get_goto_function (as used by symex) // will have side-effects on it. - const goto_functionst &goto_functions = - goto_model.get_goto_functions(); + const goto_functionst &goto_functions = goto_model.get_goto_functions(); // This provides the driver program the opportunity to do things like a // symbol-table or goto-functions dump instead of actually running the @@ -108,16 +106,15 @@ safety_checkert::resultt bmct::execute( (*memory_model)(equation); } - statistics() << "size of program expression: " - << equation.SSA_steps.size() + statistics() << "size of program expression: " << equation.SSA_steps.size() << " steps" << eom; slice(symex, equation, ns, options, ui_message_handler); // coverage report - std::string cov_out=options.get_option("symex-coverage-report"); - if(!cov_out.empty() && - symex.output_coverage_report(goto_functions, cov_out)) + std::string cov_out = options.get_option("symex-coverage-report"); + if( + !cov_out.empty() && symex.output_coverage_report(goto_functions, cov_out)) { error() << "Failed to write symex coverage report" << eom; return safety_checkert::resultt::ERROR; @@ -156,7 +153,7 @@ safety_checkert::resultt bmct::execute( catch(const std::string &error_str) { messaget message(get_message_handler()); - message.error().source_location=symex.last_source_location; + message.error().source_location = symex.last_source_location; message.error() << error_str << messaget::eom; return safety_checkert::resultt::ERROR; @@ -165,7 +162,7 @@ safety_checkert::resultt bmct::execute( catch(const char *error_str) { messaget message(get_message_handler()); - message.error().source_location=symex.last_source_location; + message.error().source_location = symex.last_source_location; message.error() << error_str << messaget::eom; return safety_checkert::resultt::ERROR; @@ -178,9 +175,7 @@ safety_checkert::resultt bmct::execute( } } - -safety_checkert::resultt bmct::run( - abstract_goto_modelt &goto_model) +safety_checkert::resultt bmct::run(abstract_goto_modelt &goto_model) { memory_model = get_memory_model(options, ns); setup_symex(symex, ns, options, ui_message_handler); @@ -222,8 +217,7 @@ safety_checkert::resultt bmct::stop_on_fail() return resultt::UNSAFE; default: - if(options.get_bool_option("dimacs") || - options.get_option("outfile")!="") + if(options.get_bool_option("dimacs") || options.get_option("outfile") != "") return resultt::SAFE; error() << "decision procedure failed" << eom; diff --git a/src/cbmc/bmc.h b/jbmc/src/jbmc/bmc.h similarity index 96% rename from src/cbmc/bmc.h rename to jbmc/src/jbmc/bmc.h index b5bc58a6421..3543103cda3 100644 --- a/src/cbmc/bmc.h +++ b/jbmc/src/jbmc/bmc.h @@ -15,23 +15,22 @@ Author: Daniel Kroening, kroening@kroening.com #include #include +#include #include #include #include -#include #include #include -#include #include +#include #include #include #include - class cbmc_solverst; /// \brief Bounded model checking or path exploration for goto-programs @@ -39,7 +38,7 @@ class cbmc_solverst; /// Higher-level architectural information on symbolic execution is /// documented in the \ref symex-overview /// "Symbolic execution module page". -class bmct:public safety_checkert +class bmct : public safety_checkert { public: /// \brief Constructor for path exploration with freshly-initialized state @@ -100,11 +99,12 @@ class bmct:public safety_checkert resultt run(abstract_goto_modelt &); void setup(); safety_checkert::resultt execute(abstract_goto_modelt &); - virtual ~bmct() { } + virtual ~bmct() + { + } // the safety_checkert interface - virtual resultt operator()( - const goto_functionst &goto_functions) + virtual resultt operator()(const goto_functionst &goto_functions) { return run(goto_functions); } @@ -114,8 +114,8 @@ class bmct:public safety_checkert symex.add_loop_unwind_handler(handler); } - void add_unwind_recursion_handler( - symex_bmct::recursion_unwind_handlert handler) + void + add_unwind_recursion_handler(symex_bmct::recursion_unwind_handlert handler) { symex.add_recursion_unwind_handler(handler); } @@ -207,8 +207,8 @@ class bmct:public safety_checkert /// invoke the symbolic executor in a class-specific way. This /// implementation invokes goto_symext::operator() to perform /// full-program model-checking from the entry point of the program. - virtual void perform_symbolic_execution( - goto_symext::get_goto_functiont get_goto_function); + virtual void + perform_symbolic_execution(goto_symext::get_goto_functiont get_goto_function); /// Optional callback, to be run after symex but before handing the resulting /// equation to the solver. If it returns true then we will skip the solver diff --git a/jbmc/src/jbmc/jbmc_parse_options.cpp b/jbmc/src/jbmc/jbmc_parse_options.cpp index be67484fc4b..b0bc9e2e8e2 100644 --- a/jbmc/src/jbmc/jbmc_parse_options.cpp +++ b/jbmc/src/jbmc/jbmc_parse_options.cpp @@ -61,7 +61,7 @@ Author: Daniel Kroening, kroening@kroening.com #include -#include // will go away +#include "bmc.h" // will go away #include #include diff --git a/jbmc/src/jbmc/module_dependencies.txt b/jbmc/src/jbmc/module_dependencies.txt index e878b9c20fe..d9411b8728d 100644 --- a/jbmc/src/jbmc/module_dependencies.txt +++ b/jbmc/src/jbmc/module_dependencies.txt @@ -1,6 +1,5 @@ analyses ansi-c # should go away -cbmc # bmc.h - will go away goto-checker goto-instrument goto-programs @@ -11,5 +10,7 @@ langapi # should go away linking pointer-analysis solvers/refinement +solvers/prop +solvers/sat solvers/strings util diff --git a/src/cbmc/Makefile b/src/cbmc/Makefile index b9f7201d2dd..e2a15dfd0e0 100644 --- a/src/cbmc/Makefile +++ b/src/cbmc/Makefile @@ -1,6 +1,4 @@ -SRC = all_properties.cpp \ - bmc.cpp \ - c_test_input_generator.cpp \ +SRC = c_test_input_generator.cpp \ cbmc_languages.cpp \ cbmc_main.cpp \ cbmc_parse_options.cpp \ diff --git a/src/cbmc/all_properties.cpp b/src/cbmc/all_properties.cpp deleted file mode 100644 index 44d5eab13f4..00000000000 --- a/src/cbmc/all_properties.cpp +++ /dev/null @@ -1,302 +0,0 @@ -/*******************************************************************\ - -Module: Symbolic Execution of ANSI-C - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -/// \file -/// Symbolic Execution of ANSI-C - -#include "all_properties_class.h" - -#include -#include - -#include -#include - -#include -#include - -#include -#include - -#include -#include -#include - -void bmc_all_propertiest::goal_covered(const cover_goalst::goalt &) -{ - for(auto &g : goal_map) - { - // failed already? - if(g.second.status==goalt::statust::FAILURE) - continue; - - // check whether failed - for(auto &c : g.second.instances) - { - literalt cond=c->cond_literal; - - if(solver.l_get(cond).is_false()) - { - g.second.status=goalt::statust::FAILURE; - build_goto_trace(bmc.equation, c, solver, bmc.ns, g.second.goto_trace); - break; - } - } - } -} - -safety_checkert::resultt bmc_all_propertiest::operator()() -{ - status() << "Passing problem to " << solver.decision_procedure_text() << eom; - - auto solver_start=std::chrono::steady_clock::now(); - - convert_symex_target_equation( - bmc.equation, bmc.prop_conv, get_message_handler()); - bmc.freeze_program_variables(); - - // Collect _all_ goals in `goal_map'. - // This maps property IDs to 'goalt' - forall_goto_functions(f_it, goto_functions) - forall_goto_program_instructions(i_it, f_it->second.body) - if(i_it->is_assert()) - goal_map[i_it->source_location.get_property_id()]=goalt(*i_it); - - // get the conditions for these goals from formula - // collect all 'instances' of the properties - for(symex_target_equationt::SSA_stepst::iterator - it=bmc.equation.SSA_steps.begin(); - it!=bmc.equation.SSA_steps.end(); - it++) - { - if(it->is_assert()) - { - irep_idt property_id = it->get_property_id(); - - if(property_id.empty()) - continue; - - if(it->source.pc->is_goto()) - { - // goto may yield an unwinding assertion - goal_map[property_id].description = it->comment; - } - - goal_map[property_id].instances.push_back(it); - } - } - - do_before_solving(); - - cover_goalst cover_goals(solver); - - cover_goals.register_observer(*this); - - for(const auto &g : goal_map) - { - // Our goal is to falsify a property, i.e., we will - // add the negation of the property as goal. - literalt p=!solver.convert(g.second.as_expr()); - cover_goals.add(p); - } - - status() << "Running " << solver.decision_procedure_text() << eom; - - bool error=false; - - const decision_proceduret::resultt result = - cover_goals(get_message_handler()); - - if(result==decision_proceduret::resultt::D_ERROR) - { - error=true; - for(auto &g : goal_map) - if(g.second.status==goalt::statust::UNKNOWN) - g.second.status=goalt::statust::ERROR; - } - else - { - for(auto &g : goal_map) - if(g.second.status==goalt::statust::UNKNOWN) - g.second.status=goalt::statust::SUCCESS; - } - - { - auto solver_stop = std::chrono::steady_clock::now(); - - statistics() - << "Runtime decision procedure: " - << std::chrono::duration(solver_stop - solver_start).count() - << "s" << eom; - } - - // report - report(cover_goals); - - if(error) - return safety_checkert::resultt::ERROR; - - bool safe=(cover_goals.number_covered()==0); - - if(safe) - report_success(bmc.ui_message_handler); // legacy, might go away - else - report_failure(bmc.ui_message_handler); // legacy, might go away - - return safe?safety_checkert::resultt::SAFE:safety_checkert::resultt::UNSAFE; -} - -void bmc_all_propertiest::report(const cover_goalst &cover_goals) -{ - switch(bmc.ui_message_handler.get_ui()) - { - case ui_message_handlert::uit::PLAIN: - { - result() << "\n** Results:" << eom; - - // collect goals in a vector - std::vector goals; - - for(auto g_it = goal_map.begin(); g_it != goal_map.end(); g_it++) - goals.push_back(g_it); - - // now determine an ordering for those goals: - // 1. alphabetical ordering of file name - // 2. numerical ordering of line number - // 3. alphabetical ordering of goal ID - std::sort( - goals.begin(), - goals.end(), - [](goal_mapt::const_iterator git1, goal_mapt::const_iterator git2) { - const auto &g1 = git1->second.source_location; - const auto &g2 = git2->second.source_location; - if(g1.get_file() != g2.get_file()) - return id2string(g1.get_file()) < id2string(g2.get_file()); - else if(!g1.get_line().empty() && !g2.get_line().empty()) - return std::stoul(id2string(g1.get_line())) < - std::stoul(id2string(g2.get_line())); - else - return id2string(git1->first) < id2string(git2->first); - }); - - // now show in the order we have determined - - irep_idt previous_function; - irep_idt current_file; - for(const auto &g : goals) - { - const auto &l = g->second.source_location; - - if(l.get_function() != previous_function) - { - if(!previous_function.empty()) - result() << '\n'; - previous_function = l.get_function(); - if(!previous_function.empty()) - { - current_file = l.get_file(); - if(!current_file.empty()) - result() << current_file << ' '; - if(!l.get_function().empty()) - result() << "function " << l.get_function(); - result() << eom; - } - } - - result() << faint << '[' << g->first << "] " << reset; - - if(l.get_file() != current_file) - result() << "file " << l.get_file() << ' '; - - if(!l.get_line().empty()) - result() << "line " << l.get_line() << ' '; - - result() << g->second.description << ": "; - - if(g->second.status == goalt::statust::SUCCESS) - result() << green; - else - result() << red; - - result() << g->second.status_string() << reset << eom; - } - - if(bmc.options.get_bool_option("trace")) - { - for(const auto &g : goal_map) - if(g.second.status==goalt::statust::FAILURE) - { - result() << "\n" << "Trace for " << g.first << ":" << "\n"; - show_goto_trace( - result(), bmc.ns, g.second.goto_trace, bmc.trace_options()); - result() << eom; - } - } - - status() << "\n** " << cover_goals.number_covered() - << " of " << cover_goals.size() << " failed (" - << cover_goals.iterations() << " iteration" - << (cover_goals.iterations()==1?"":"s") - << ")" << eom; - } - break; - - case ui_message_handlert::uit::XML_UI: - { - for(const auto &g : goal_map) - { - xmlt xml_result( - "result", - {{"property", id2string(g.first)}, - {"status", g.second.status_string()}}, - {}); - - if(g.second.status==goalt::statust::FAILURE) - convert(bmc.ns, g.second.goto_trace, xml_result.new_element()); - - result() << xml_result; - } - break; - } - - case ui_message_handlert::uit::JSON_UI: - { - if(result().tellp() > 0) - result() << eom; // force end of previous message - json_stream_objectt &json_result = - bmc.ui_message_handler.get_json_stream().push_back_stream_object(); - json_stream_arrayt &result_array = - json_result.push_back_stream_array("result"); - - for(const auto &g : goal_map) - { - json_stream_objectt &result = result_array.push_back_stream_object(); - result["property"] = json_stringt(g.first); - result["description"] = json_stringt(g.second.description); - result["status"]=json_stringt(g.second.status_string()); - - if(g.second.status==goalt::statust::FAILURE) - { - json_stream_arrayt &json_trace = - result.push_back_stream_array("trace"); - convert( - bmc.ns, g.second.goto_trace, json_trace, bmc.trace_options()); - } - } - } - break; - } -} - -safety_checkert::resultt -bmct::all_properties(const goto_functionst &goto_functions) -{ - bmc_all_propertiest bmc_all_properties(goto_functions, prop_conv, *this); - bmc_all_properties.set_message_handler(get_message_handler()); - return bmc_all_properties(); -} diff --git a/src/cbmc/cbmc_parse_options.h b/src/cbmc/cbmc_parse_options.h index f3d63499913..49b7a5cc825 100644 --- a/src/cbmc/cbmc_parse_options.h +++ b/src/cbmc/cbmc_parse_options.h @@ -31,7 +31,6 @@ Author: Daniel Kroening, kroening@kroening.com #include -#include "bmc.h" #include "xml_interface.h" class goto_functionst; diff --git a/unit/Makefile b/unit/Makefile index c93aa6fc932..2d7bbc5bbec 100644 --- a/unit/Makefile +++ b/unit/Makefile @@ -98,9 +98,7 @@ testing-utils-clean: $(MAKE) $(MAKEARGS) -C testing-utils clean # We need to link bmc.o to the unit test, so here's everything it depends on... -BMC_DEPS =../src/cbmc/all_properties$(OBJEXT) \ - ../src/cbmc/bmc$(OBJEXT) \ - ../src/cbmc/c_test_input_generator$(OBJEXT) \ +BMC_DEPS =../src/cbmc/c_test_input_generator$(OBJEXT) \ ../src/cbmc/cbmc_languages$(OBJEXT) \ ../src/cbmc/cbmc_parse_options$(OBJEXT) \ ../src/cbmc/xml_interface$(OBJEXT) \ diff --git a/unit/path_strategies.cpp b/unit/path_strategies.cpp index 451be55eb66..6061077a615 100644 --- a/unit/path_strategies.cpp +++ b/unit/path_strategies.cpp @@ -16,10 +16,11 @@ Author: Kareem Khazem , 2018 #include -#include #include -#include +#include +#include +#include #include @@ -42,8 +43,8 @@ Author: Kareem Khazem , 2018 // `goto` instruction. // // Whenever symbolic execution reaches the end of a path, you should expect a -// result. Results are either SUCCESS, meaning that verification of that path -// succeeded, or FAILURE, meaning that there was an assertion failure on that +// result. Results are either DONE, meaning that verification of that path +// succeeded, or FOUND_FAIL, meaning that there was an assertion failure on that // path. // // To figure out what the events should be, run CBMC on the test program with @@ -80,14 +81,18 @@ SCENARIO("path strategies") "lifo", opts_callback, c, - {symex_eventt::resume(symex_eventt::enumt::JUMP, 7), + {// Entry state is line 0 + symex_eventt::resume(symex_eventt::enumt::NEXT, 0), + symex_eventt::resume(symex_eventt::enumt::JUMP, 7), symex_eventt::resume(symex_eventt::enumt::NEXT, 5), symex_eventt::result(symex_eventt::enumt::SUCCESS)}); check_with_strategy( "fifo", opts_callback, c, - {symex_eventt::resume(symex_eventt::enumt::NEXT, 5), + {// Entry state is line 0 + symex_eventt::resume(symex_eventt::enumt::NEXT, 0), + symex_eventt::resume(symex_eventt::enumt::NEXT, 5), symex_eventt::resume(symex_eventt::enumt::JUMP, 7), symex_eventt::result(symex_eventt::enumt::SUCCESS)}); } @@ -120,7 +125,9 @@ SCENARIO("path strategies") "lifo", opts_callback, c, - {// Outer else, inner else + {// Entry state is line 0 + symex_eventt::resume(symex_eventt::enumt::NEXT, 0), + // Outer else, inner else symex_eventt::resume(symex_eventt::enumt::JUMP, 13), symex_eventt::resume(symex_eventt::enumt::JUMP, 16), // Outer else, inner if @@ -136,7 +143,9 @@ SCENARIO("path strategies") "fifo", opts_callback, c, - {// Expand outer if, but don't continue depth-first + {// Entry state is line 0 + symex_eventt::resume(symex_eventt::enumt::NEXT, 0), + // Expand outer if, but don't continue depth-first symex_eventt::resume(symex_eventt::enumt::NEXT, 6), // Jump to outer else, but again don't continue depth-first symex_eventt::resume(symex_eventt::enumt::JUMP, 13), @@ -175,6 +184,9 @@ SCENARIO("path strategies") opts_callback, c, { + // Entry state is line 0 + symex_eventt::resume(symex_eventt::enumt::NEXT, 0), + // The path where x != 1 and we have nothing to check: symex_eventt::resume(symex_eventt::enumt::JUMP, 11), @@ -204,6 +216,9 @@ SCENARIO("path strategies") opts_callback, c, { + // Entry state is line 0 + symex_eventt::resume(symex_eventt::enumt::NEXT, 0), + // First the path where we enter the if-block at line 4 then on hitting // the branch at line 6 consider skipping straight to the end, but find // nothing there to investigate: @@ -257,7 +272,9 @@ SCENARIO("path strategies") "lifo", no_halt_callback, c, - {symex_eventt::resume(symex_eventt::enumt::JUMP, 7), + {// Entry state is line 0 + symex_eventt::resume(symex_eventt::enumt::NEXT, 0), + symex_eventt::resume(symex_eventt::enumt::JUMP, 7), symex_eventt::result(symex_eventt::enumt::FAILURE), symex_eventt::resume(symex_eventt::enumt::NEXT, 5), symex_eventt::result(symex_eventt::enumt::FAILURE), @@ -270,7 +287,9 @@ SCENARIO("path strategies") "lifo", halt_callback, c, - {symex_eventt::resume(symex_eventt::enumt::JUMP, 7), + {// Entry state is line 0 + symex_eventt::resume(symex_eventt::enumt::NEXT, 0), + symex_eventt::resume(symex_eventt::enumt::JUMP, 7), symex_eventt::result(symex_eventt::enumt::FAILURE), // Overall result symex_eventt::result(symex_eventt::enumt::FAILURE)}); @@ -283,24 +302,24 @@ SCENARIO("path strategies") void symex_eventt::validate_result( listt &events, - const safety_checkert::resultt result, + const incremental_goto_checkert::resultt::progresst result, std::size_t &counter) { INFO( "Expecting result to be '" - << (result == safety_checkert::resultt::SAFE ? "success" : "failure") + << (result == incremental_goto_checkert::resultt::progresst::DONE + ? "success" + : "failure") << "' (item at index [" << counter << "] in expected results list"); ++counter; - REQUIRE(result != safety_checkert::resultt::ERROR); - - if(result == safety_checkert::resultt::SAFE) + if(result == incremental_goto_checkert::resultt::progresst::DONE) { REQUIRE(!events.empty()); REQUIRE(events.front().first == symex_eventt::enumt::SUCCESS); events.pop_front(); } - else if(result == safety_checkert::resultt::UNSAFE) + else { REQUIRE(!events.empty()); REQUIRE(events.front().first == symex_eventt::enumt::FAILURE); @@ -315,23 +334,25 @@ void symex_eventt::validate_resume( { REQUIRE(!events.empty()); - int dst = std::stoi(state.saved_target->source_location.get_line().c_str()); + int dst = 0; + if(!state.saved_target->source_location.get_line().empty()) + dst = std::stoi(state.saved_target->source_location.get_line().c_str()); - if(state.has_saved_jump_target) + if(state.has_saved_next_instruction) { INFO( - "Expecting resume to be 'jump' to line " + "Expecting resume to be 'next' to line " << dst << " (item at index [" << counter << "] in expected resumes list)"); - REQUIRE(events.front().first == symex_eventt::enumt::JUMP); + REQUIRE(events.front().first == symex_eventt::enumt::NEXT); } - else if(state.has_saved_next_instruction) + else if(state.has_saved_jump_target) { INFO( - "Expecting resume to be 'next' to line " + "Expecting resume to be 'jump' to line " << dst << " (item at index [" << counter << "] in expected resumes list)"); - REQUIRE(events.front().first == symex_eventt::enumt::NEXT); + REQUIRE(events.front().first == symex_eventt::enumt::JUMP); } else REQUIRE(false); @@ -342,11 +363,8 @@ void symex_eventt::validate_resume( events.pop_front(); } -// This is a simplified version of bmct::do_language_agnostic_bmc, without all -// the edge cases to deal with java programs, bytecode loaded on demand, etc. We -// need to replicate some of this stuff because the worklist is a local variable -// of do_language_agnostic_bmc, and we need to check the worklist every time -// symex returns. +// This is a simplified copy of single_path_symex_checkert +// because we have to check the worklist every time goto-symex returns. void _check_with_strategy( const std::string &strategy, const std::string &program, @@ -366,95 +384,122 @@ void _check_with_strategy( config.main = "main"; config.set(cmdline); - optionst opts; - cbmc_parse_optionst::set_default_options(opts); - opts.set_option("paths", true); - opts.set_option("exploration-strategy", strategy); - - opts_callback(opts); - - ui_message_handlert mh(cmdline, "path-explore"); - mh.set_verbosity(0); - messaget log(mh); - + optionst options; + cbmc_parse_optionst::set_default_options(options); + options.set_option("paths", true); + options.set_option("exploration-strategy", strategy); REQUIRE(is_valid_path_strategy(strategy)); - std::unique_ptr worklist = get_path_strategy(strategy); - guard_managert guard_manager; + opts_callback(options); - goto_modelt gm; + ui_message_handlert ui_message_handler(cmdline, "path-explore"); + ui_message_handler.set_verbosity(0); + messaget log(ui_message_handler); + + goto_modelt goto_model; int ret; - ret = cbmc_parse_optionst::get_goto_program(gm, opts, cmdline, log, mh); + ret = cbmc_parse_optionst::get_goto_program( + goto_model, options, cmdline, log, ui_message_handler); REQUIRE(ret == -1); - namespacet ns(gm.get_symbol_table()); - solver_factoryt solver_factory(opts, ns, mh, false); - std::unique_ptr cbmc_solver = - solver_factory.get_solver(); - prop_convt &initial_pc = cbmc_solver->prop_conv(); - std::function callback = []() { return false; }; - - safety_checkert::resultt overall_result = safety_checkert::resultt::SAFE; - std::size_t expected_results_cnt = 0; + symbol_tablet symex_symbol_table; + namespacet ns(goto_model.get_symbol_table(), symex_symbol_table); + propertiest properties(initialize_properties(goto_model)); + std::unique_ptr worklist = get_path_strategy(strategy); + guard_managert guard_manager; - bmct bmc( - opts, - gm.get_symbol_table(), - mh, - initial_pc, - *worklist, - callback, - guard_manager); - safety_checkert::resultt tmp_result = bmc.run(gm); - - if(tmp_result != safety_checkert::resultt::PAUSED) { - symex_eventt::validate_result(events, tmp_result, expected_results_cnt); - overall_result &= tmp_result; - } + // Put initial state into the work list + symex_target_equationt equation(ui_message_handler); + symex_bmct symex( + ui_message_handler, + goto_model.get_symbol_table(), + equation, + options, + *worklist, + guard_manager); + setup_symex(symex, ns, options, ui_message_handler); - if( - overall_result == safety_checkert::resultt::UNSAFE && - opts.get_bool_option("stop-on-fail") && opts.is_set("paths")) - { - worklist->clear(); + symex.initialize_path_storage_from_entry_point_of( + goto_symext::get_goto_function(goto_model), symex_symbol_table); } + std::size_t expected_results_cnt = 0; while(!worklist->empty()) { - cbmc_solver = solver_factory.get_solver(); - prop_convt &pc = cbmc_solver->prop_conv(); path_storaget::patht &resume = worklist->peek(); symex_eventt::validate_resume(events, resume.state, expected_results_cnt); - path_explorert pe( - opts, - gm.get_symbol_table(), - mh, - pc, + symex_bmct symex( + ui_message_handler, + goto_model.get_symbol_table(), resume.equation, - resume.state, + options, *worklist, - guard_manager, - callback); - tmp_result = pe.run(gm); + guard_manager); + setup_symex(symex, ns, options, ui_message_handler); + + symex.resume_symex_from_saved_state( + goto_symext::get_goto_function(goto_model), + resume.state, + &resume.equation, + symex_symbol_table); + postprocess_equation( + symex, resume.equation, options, ns, ui_message_handler); - if(tmp_result != safety_checkert::resultt::PAUSED) + incremental_goto_checkert::resultt result( + incremental_goto_checkert::resultt::progresst::DONE); + + if(symex.get_remaining_vccs() > 0) { - symex_eventt::validate_result(events, tmp_result, expected_results_cnt); - overall_result &= tmp_result; + update_properties_status_from_symex_target_equation( + properties, result.updated_properties, resume.equation); + + goto_symex_property_decidert property_decider( + options, ui_message_handler, resume.equation, ns); + + const auto solver_runtime = prepare_property_decider( + properties, resume.equation, property_decider, ui_message_handler); + + run_property_decider( + result, + properties, + property_decider, + ui_message_handler, + solver_runtime, + false); + + symex_eventt::validate_result( + events, result.progress, expected_results_cnt); } + worklist->pop(); if( - overall_result == safety_checkert::resultt::UNSAFE && - opts.get_bool_option("stop-on-fail")) + result.progress == + incremental_goto_checkert::resultt::progresst::FOUND_FAIL && + options.get_bool_option("stop-on-fail")) { worklist->clear(); } + + if(worklist->empty()) + { + update_status_of_not_checked_properties( + properties, result.updated_properties); + + update_status_of_unknown_properties( + properties, result.updated_properties); + } } - symex_eventt::validate_result(events, overall_result, expected_results_cnt); + const resultt overall_result = determine_result(properties); + symex_eventt::validate_result( + events, + overall_result == resultt::FAIL + ? incremental_goto_checkert::resultt::progresst::FOUND_FAIL + : incremental_goto_checkert::resultt::progresst::DONE, + expected_results_cnt); INFO("The expected results list contains " << events.size() << " items"); REQUIRE(events.empty()); diff --git a/unit/path_strategies.h b/unit/path_strategies.h index c9537896194..118bc2f7307 100644 --- a/unit/path_strategies.h +++ b/unit/path_strategies.h @@ -6,7 +6,8 @@ #define CPROVER_PATH_STRATEGIES_H #include -#include + +#include #include @@ -38,7 +39,7 @@ struct symex_eventt static void validate_result( listt &events, - const safety_checkert::resultt result, + const incremental_goto_checkert::resultt::progresst result, std::size_t &); static void