path_searcht::resultt path_searcht::operator()( const goto_functionst &goto_functions) { #ifdef PATH_SYMEX_FORK // Disable output because there is no meaningful way // to write text when multiple path_search processes // run concurrently. This could be remedied by piping // to individual files or inter-process communication, // a performance bottleneck, however. *messaget::mstream.message_handler=NULL; #endif locst locs(ns); var_mapt var_map(ns); locs.build(goto_functions); // this is the container for the history-forest path_symex_historyt history; queue.push_back(initial_state(var_map, locs, history)); // set up the statistics number_of_paths=0; number_of_instructions=0; number_of_dropped_states=0; number_of_VCCs=0; number_of_VCCs_after_simplification=0; number_of_failed_properties=0; number_of_fast_forward_steps=0; // stop the time start_time=current_time(); initialize_property_map(goto_functions); while(!queue.empty()) { // Pick a state from the queue, // according to some heuristic. queuet::iterator state=pick_state(); // fast forwarding required? if(state->is_lazy()) { assert(state->is_executable()); assert(state->history.is_nil()); // keep allocated memory, this is faster than // instantiating a new empty vector and map history.clear(); var_map.clear(); state->history=path_symex_step_reft(history); // restore all fields of a lazy state by symbolic // execution along previously recorded branches const queuet::size_type queue_size=queue.size(); do { number_of_fast_forward_steps++; path_symex(*state, queue); #ifdef PATH_SYMEX_OUTPUT status() << "Fast forward thread " << state->get_current_thread() << "/" << state->threads.size() << " PC " << state->pc() << messaget::eom; #endif } while(state->is_lazy() && state->is_executable()); assert(queue.size() == queue_size); } // TODO: check lazy states before fast forwarding, or perhaps it // is better to even check before inserting into queue if(drop_state(*state)) { number_of_dropped_states++; queue.erase(state); continue; } if(!state->is_executable()) { queue.erase(state); continue; } // count only executable instructions number_of_instructions++; #ifdef PATH_SYMEX_OUTPUT status() << "Queue " << queue.size() << " thread " << state->get_current_thread() << "/" << state->threads.size() << " PC " << state->pc() << messaget::eom; #endif // an error, possibly? if(state->get_instruction()->is_assert()) { if(show_vcc) do_show_vcc(*state, ns); else { check_assertion(*state, ns); // all assertions failed? if(number_of_failed_properties==property_map.size()) break; } } #ifdef PATH_SYMEX_FORK if(try_await()) { debug() << "Child process has terminated " "so exit parent" << messaget::eom; break; } #endif // execute and record whether a "branch" occurred const queuet::size_type queue_size = queue.size(); path_symex(*state, queue); assert(queue_size <= queue.size()); number_of_paths += (queue.size() - queue_size); } #ifdef PATH_SYMEX_FORK int exit_status=await(); if(exit_status==0 && number_of_failed_properties!=0) { // the eldest child process (if any) reports found bugs report_statistics(); return UNSAFE; } else { // either a child found and reported a bug or // the parent's search partition is safe switch (exit_status) { case 0: return SAFE; case 10: return UNSAFE; default: return ERROR; } } #else report_statistics(); return number_of_failed_properties==0?SAFE:UNSAFE; #endif }
path_searcht::resultt path_searcht::operator()( const goto_functionst &goto_functions) { locst locs(ns); var_mapt var_map(ns); locs.build(goto_functions); // this is the container for the history-forest path_symex_historyt history; queue.push_back(initial_state(var_map, locs, history)); // set up the statistics number_of_dropped_states=0; number_of_paths=0; number_of_VCCs=0; number_of_steps=0; number_of_feasible_paths=0; number_of_infeasible_paths=0; number_of_VCCs_after_simplification=0; number_of_failed_properties=0; number_of_locs=locs.size(); // stop the time start_time=current_time(); initialize_property_map(goto_functions); while(!queue.empty()) { number_of_steps++; // Pick a state from the queue, // according to some heuristic. // The state moves to the head of the queue. pick_state(); // move into temporary queue queuet tmp_queue; tmp_queue.splice( tmp_queue.begin(), queue, queue.begin(), ++queue.begin()); try { statet &state=tmp_queue.front(); // record we have seen it loc_data[state.get_pc().loc_number].visited=true; debug() << "Loc: #" << state.get_pc().loc_number << ", queue: " << queue.size() << ", depth: " << state.get_depth(); for(const auto & s : queue) debug() << ' ' << s.get_depth(); debug() << eom; if(drop_state(state)) { number_of_dropped_states++; number_of_paths++; continue; } if(!state.is_executable()) { number_of_paths++; continue; } if(eager_infeasibility && state.last_was_branch() && !is_feasible(state)) { number_of_infeasible_paths++; number_of_paths++; continue; } if(number_of_steps%1000==0) { status() << "Queue " << queue.size() << " thread " << state.get_current_thread() << '/' << state.threads.size() << " PC " << state.pc() << messaget::eom; } // an error, possibly? if(state.get_instruction()->is_assert()) { if(show_vcc) do_show_vcc(state); else { check_assertion(state); // all assertions failed? if(number_of_failed_properties==property_map.size()) break; } } // execute path_symex(state, tmp_queue); // put at head of main queue queue.splice(queue.begin(), tmp_queue); } catch(const std::string &e) { error() << e << eom; number_of_dropped_states++; } catch(const char *e) { error() << e << eom; number_of_dropped_states++; } catch(int) { number_of_dropped_states++; } } report_statistics(); return number_of_failed_properties==0?SAFE:UNSAFE; }