path_symex_statet initial_state( var_mapt &var_map, const locst &locs, path_symex_historyt &path_symex_history) { return path_symex_statet( var_map, locs, path_symex_step_reft(path_symex_history), path_symex_statet::branchest()); }
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 }