X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=model.cc;h=c190e38cacb8c7ccb842957ef07c1c231f3c9bfe;hb=HEAD;hp=a1204d0ab852352600edb95063476bd6f2976fa7;hpb=c4f504bb8deb86c8e2b06cb8ddaec5d92ec17e9d;p=c11tester.git diff --git a/model.cc b/model.cc index a1204d0a..c190e38c 100644 --- a/model.cc +++ b/model.cc @@ -15,25 +15,73 @@ #include "output.h" #include "traceanalysis.h" #include "execution.h" -#include "history.h" #include "bugmessage.h" #include "params.h" #include "plugins.h" ModelChecker *model = NULL; +int inside_model = 0; + +uint64_t get_nanotime() +{ + struct timespec currtime; + clock_gettime(CLOCK_MONOTONIC, &currtime); + + return currtime.tv_nsec; +} void placeholder(void *) { ASSERT(0); } +#include + +#define SIGSTACKSIZE 65536 +static void mprot_handle_pf(int sig, siginfo_t *si, void *unused) +{ + model_print("Segmentation fault at %p\n", si->si_addr); + model_print("For debugging, place breakpoint at: %s:%d\n", + __FILE__, __LINE__); + print_trace(); // Trace printing may cause dynamic memory allocation + while(1) + ; +} + +void install_handler() { + stack_t ss; + ss.ss_sp = model_malloc(SIGSTACKSIZE); + ss.ss_size = SIGSTACKSIZE; + ss.ss_flags = 0; + sigaltstack(&ss, NULL); + struct sigaction sa; + sa.sa_flags = SA_SIGINFO | SA_NODEFER | SA_RESTART | SA_ONSTACK; + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = mprot_handle_pf; + + if (sigaction(SIGSEGV, &sa, NULL) == -1) { + perror("sigaction(SIGSEGV)"); + exit(EXIT_FAILURE); + } +} + +void createModelIfNotExist() { + if (!model) { + ENTER_MODEL_FLAG; + snapshot_system_init(100000); + model = new ModelChecker(); + model->startChecker(); + EXIT_MODEL_FLAG; + } +} + /** @brief Constructor */ ModelChecker::ModelChecker() : /* Initialize default scheduler */ params(), scheduler(new Scheduler()), - history(new ModelHistory()), execution(new ModelExecution(this, scheduler)), execution_number(1), + curr_thread_num(MAIN_THREAD_ID), trace_analyses(), inspect_plugin(NULL) { @@ -41,7 +89,8 @@ ModelChecker::ModelChecker() : "Copyright (c) 2013 and 2019 Regents of the University of California. All rights reserved.\n" "Distributed under the GPLv2\n" "Written by Weiyu Luo, Brian Norris, and Brian Demsky\n\n"); - memset(&stats,0,sizeof(struct execution_stats)); + init_memory_ops(); + real_memset(&stats,0,sizeof(struct execution_stats)); init_thread = new Thread(execution->get_next_id(), (thrd_t *) model_malloc(sizeof(thrd_t)), &placeholder, NULL, NULL); #ifdef TLS init_thread->setTLS((char *)get_tls_addr()); @@ -54,8 +103,7 @@ ModelChecker::ModelChecker() : parse_options(¶ms); initRaceDetector(); /* Configure output redirection for the model-checker */ - redirect_output(); - install_trace_analyses(get_execution()); + install_handler(); } /** @brief Destructor */ @@ -104,6 +152,18 @@ Thread * ModelChecker::get_current_thread() const return scheduler->get_current_thread(); } +/** + * Must be called from user-thread context (e.g., through the global + * thread_current_id() interface) + * + * @return The id of the currently executing Thread. + */ +thread_id_t ModelChecker::get_current_thread_id() const +{ + ASSERT(int_to_id(curr_thread_num) == get_current_thread()->get_id()); + return int_to_id(curr_thread_num); +} + /** * @brief Choose the next thread to execute. * @@ -158,7 +218,7 @@ void ModelChecker::assert_user_bug(const char *msg) { /* If feasible bug, bail out now */ assert_bug(msg); - switch_to_master(NULL); + switch_thread(NULL); } /** @brief Print bug report listing for this execution (if any bugs exist) */ @@ -226,7 +286,9 @@ void ModelChecker::print_execution(bool printbugs) const } model_print("\n"); +#ifdef PRINT_TRACE execution->print_summary(); +#endif } /** @@ -258,11 +320,10 @@ void ModelChecker::finish_execution(bool more_executions) else clear_program_output(); -// test code execution_number ++; + if (more_executions) reset_to_initial_state(); - history->set_new_exec_flag(); } /** @brief Run trace analyses on complete trace */ @@ -291,161 +352,103 @@ Thread * ModelChecker::get_thread(const ModelAction *act) const return execution->get_thread(act); } -/** - * Switch from a model-checker context to a user-thread context. This is the - * complement of ModelChecker::switch_to_master and must be called from the - * model-checker context - * - * @param thread The user-thread to switch to - */ -void ModelChecker::switch_from_master(Thread *thread) -{ - scheduler->set_current_thread(thread); - Thread::swap(&system_context, thread); -} - -/** - * Switch from a user-context to the "master thread" context (a.k.a. system - * context). This switch is made with the intention of exploring a particular - * model-checking action (described by a ModelAction object). Must be called - * from a user-thread context. - * - * @param act The current action that will be explored. May be NULL only if - * trace is exiting via an assertion (see ModelExecution::set_assert and - * ModelExecution::has_asserted). - * @return Return the value returned by the current action - */ -uint64_t ModelChecker::switch_to_master(ModelAction *act) -{ - if (modellock) { - static bool fork_message_printed = false; - - if (!fork_message_printed) { - model_print("Fork handler or dead thread trying to call into model checker...\n"); - fork_message_printed = true; +void ModelChecker::startRunExecution(Thread *old) { + while (true) { + if (params.traceminsize != 0 && + execution->get_curr_seq_num() > checkfree) { + checkfree += params.checkthreshold; + execution->collectActions(); } - delete act; - return 0; - } - DBG(); - Thread *old = thread_current(); - scheduler->set_current_thread(NULL); - ASSERT(!old->get_pending()); - if (inspect_plugin != NULL) { - inspect_plugin->inspectModelAction(act); - } - - old->set_pending(act); - if (Thread::swap(old, &system_context) < 0) { - perror("swap threads"); - exit(EXIT_FAILURE); - } - return old->get_return_value(); -} - -void ModelChecker::continueRunExecution(Thread *old) -{ - if (params.traceminsize != 0 && - execution->get_curr_seq_num() > checkfree) { - checkfree += params.checkthreshold; - execution->collectActions(); - } - thread_chosen = false; - curr_thread_num = 1; - Thread *thr = getNextThread(); - if (thr != nullptr) { - scheduler->set_current_thread(thr); - if (Thread::swap(old, thr) < 0) { - perror("swap threads"); - exit(EXIT_FAILURE); + curr_thread_num = MAIN_THREAD_ID; + Thread *thr = getNextThread(old); + if (thr != nullptr) { + scheduler->set_current_thread(thr); + EXIT_MODEL_FLAG; + if (Thread::swap(old, thr) < 0) { + perror("swap threads"); + exit(EXIT_FAILURE); + } + return; } - } else - handleChosenThread(old); -} -void ModelChecker::startRunExecution(ucontext_t *old) -{ - if (params.traceminsize != 0 && - execution->get_curr_seq_num() > checkfree) { - checkfree += params.checkthreshold; - execution->collectActions(); - } - thread_chosen = false; - curr_thread_num = 1; - Thread *thr = getNextThread(); - if (thr != nullptr) { - scheduler->set_current_thread(thr); - if (Thread::swap(old, thr) < 0) { - perror("swap threads"); - exit(EXIT_FAILURE); + if (!handleChosenThread(old)) { + return; } - } else - handleChosenThread(old); + } } -Thread* ModelChecker::getNextThread() +Thread* ModelChecker::getNextThread(Thread *old) { Thread *nextThread = nullptr; - for (unsigned int i = curr_thread_num; i < get_num_threads(); i++) { + for (unsigned int i = curr_thread_num;i < get_num_threads();i++) { thread_id_t tid = int_to_id(i); Thread *thr = get_thread(tid); - - if (!thr->is_complete() && !thr->get_pending()) { - curr_thread_num = i; - nextThread = thr; - break; + + if (!thr->is_complete()) { + if (!thr->get_pending()) { + curr_thread_num = i; + nextThread = thr; + break; + } + } else if (thr != old && !thr->is_freed()) { + thr->freeResources(); } + ModelAction *act = thr->get_pending(); - - if (act && execution->is_enabled(thr) && !execution->check_action_enabled(act)) { - scheduler->sleep(thr); - } + if (act && scheduler->is_enabled(tid)){ + /* Don't schedule threads which should be disabled */ + if (!execution->check_action_enabled(act)) { + scheduler->sleep(thr); + } - chooseThread(act, thr); + /* Allow pending relaxed/release stores or thread actions to perform first */ + else if (!chosen_thread) { + if (act->is_write()) { + std::memory_order order = act->get_mo(); + if (order == std::memory_order_relaxed || \ + order == std::memory_order_release) { + chosen_thread = thr; + } + } else if (act->get_type() == THREAD_CREATE || \ + act->get_type() == PTHREAD_CREATE || \ + act->get_type() == THREAD_START || \ + act->get_type() == THREAD_FINISH) { + chosen_thread = thr; + } + } + } } return nextThread; } -void ModelChecker::finishRunExecution(Thread *old) +/* Swap back to system_context and terminate this execution */ +void ModelChecker::finishRunExecution(Thread *old) { scheduler->set_current_thread(NULL); - if (Thread::swap(old, &system_context) < 0) { - perror("swap threads"); - exit(EXIT_FAILURE); - } -} -void ModelChecker::finishRunExecution(ucontext_t *old) -{ - scheduler->set_current_thread(NULL); -} + /** Reset curr_thread_num to initial value for next execution. */ + curr_thread_num = MAIN_THREAD_ID; -void ModelChecker::consumeAction() -{ - ModelAction *curr = chosen_thread->get_pending(); - chosen_thread->set_pending(NULL); - chosen_thread = execution->take_step(curr); -} + /** If we have more executions, we won't make it past this call. */ + finish_execution(execution_number < params.maxexecutions); -void ModelChecker::chooseThread(ModelAction *act, Thread *thr) -{ - if (!thread_chosen && act && execution->is_enabled(thr) && (thr->get_state() != THREAD_BLOCKED) ) { - if (act->is_write()) { - std::memory_order order = act->get_mo(); - if (order == std::memory_order_relaxed || \ - order == std::memory_order_release) { - chosen_thread = thr; - thread_chosen = true; - } - } else if (act->get_type() == THREAD_CREATE || \ - act->get_type() == PTHREAD_CREATE || \ - act->get_type() == THREAD_START || \ - act->get_type() == THREAD_FINISH) { - chosen_thread = thr; - thread_chosen = true; - } - } + + /** We finished the final execution. Print stuff and exit. */ + model_print("******* Model-checking complete: *******\n"); + print_stats(); + + /* Have the trace analyses dump their output. */ + for (unsigned int i = 0;i < trace_analyses.size();i++) + trace_analyses[i]->finish(); + + /* unlink tmp file created by last child process */ + char filename[256]; + snprintf_(filename, sizeof(filename), "C11FuzzerTmp%d", getpid()); + unlink(filename); + + /* Exit. */ + _Exit(0); } uint64_t ModelChecker::switch_thread(ModelAction *act) @@ -460,8 +463,12 @@ uint64_t ModelChecker::switch_thread(ModelAction *act) delete act; return 0; } + ENTER_MODEL_FLAG; + DBG(); Thread *old = thread_current(); + old->set_state(THREAD_READY); + ASSERT(!old->get_pending()); if (inspect_plugin != NULL) { @@ -469,102 +476,75 @@ uint64_t ModelChecker::switch_thread(ModelAction *act) } old->set_pending(act); - + if (old->is_waiting_on(old)) assert_bug("Deadlock detected (thread %u)", curr_thread_num); - ModelAction *act2 = old->get_pending(); - - if (act2 && execution->is_enabled(old) && !execution->check_action_enabled(act2)) { - scheduler->sleep(old); + Thread* next = getNextThread(old); + if (next != nullptr) { + scheduler->set_current_thread(next); + if (Thread::swap(old, next) < 0) { + perror("swap threads"); + exit(EXIT_FAILURE); + } + } else { + if (handleChosenThread(old)) { + startRunExecution(old); + } } - chooseThread(act2, old); - - curr_thread_num++; - Thread* next = getNextThread(); - if (next != nullptr) - handleNewValidThread(old, next); - else - handleChosenThread(old); - return old->get_return_value(); } -void ModelChecker::handleNewValidThread(Thread *old, Thread *next) -{ - scheduler->set_current_thread(next); - - if (Thread::swap(old, next) < 0) { - perror("swap threads"); - exit(EXIT_FAILURE); - } -} - -void ModelChecker::handleChosenThread(Thread *old) +bool ModelChecker::handleChosenThread(Thread *old) { - if (execution->has_asserted()) + if (execution->has_asserted()) { finishRunExecution(old); - if (!chosen_thread) + return false; + } + if (!chosen_thread) { chosen_thread = get_next_thread(); - if (!chosen_thread || chosen_thread->is_model_thread()) + } + if (!chosen_thread) { finishRunExecution(old); + return false; + } if (chosen_thread->just_woken_up()) { chosen_thread->set_wakeup_state(false); chosen_thread->set_pending(NULL); chosen_thread = NULL; // Allow this thread to stash the next pending action - if (should_terminate_execution()) - finishRunExecution(old); - else - continueRunExecution(old); - } else { - /* Consume the next action for a Thread */ - consumeAction(); - - if (should_terminate_execution()) - finishRunExecution(old); - else - continueRunExecution(old); + return true; } -} -void ModelChecker::handleChosenThread(ucontext_t *old) -{ - if (execution->has_asserted()) - finishRunExecution(old); - if (!chosen_thread) - chosen_thread = get_next_thread(); - if (!chosen_thread || chosen_thread->is_model_thread()) + // Consume the next action for a Thread + ModelAction *curr = chosen_thread->get_pending(); + chosen_thread->set_pending(NULL); + chosen_thread = execution->take_step(curr); + + if (should_terminate_execution()) { finishRunExecution(old); - if (chosen_thread->just_woken_up()) { - chosen_thread->set_wakeup_state(false); - chosen_thread->set_pending(NULL); - chosen_thread = NULL; - // Allow this thread to stash the next pending action - if (should_terminate_execution()) - finishRunExecution(old); - else - startRunExecution(old); + return false; } else { - /* Consume the next action for a Thread */ - consumeAction(); - - if (should_terminate_execution()) - finishRunExecution(old); - else - startRunExecution(old); + return true; } } - -static void runChecker() { - model->run(); - delete model; -} - void ModelChecker::startChecker() { - startExecution(get_system_context(), runChecker); + startExecution(); + //Need to initial random number generator state to avoid resets on rollback + //initstate(423121, random_state, sizeof(random_state)); + uint64_t seed = get_nanotime(); + srandom(seed); + snapshot = take_snapshot(); + + //reset random number generator state + //setstate(random_state); + seed = get_nanotime(); + srandom(seed); + + install_trace_analyses(get_execution()); + redirect_output(); initMainThread(); } @@ -578,33 +558,3 @@ bool ModelChecker::should_terminate_execution() } return false; } - -/** @brief Run ModelChecker for the user program */ -void ModelChecker::run() -{ - //Need to initial random number generator state to avoid resets on rollback - char random_state[256]; - initstate(423121, random_state, sizeof(random_state)); - checkfree = params.checkthreshold; - for(int exec = 0;exec < params.maxexecutions;exec++) { - chosen_thread = init_thread; - thread_chosen = false; - curr_thread_num = 1; - startRunExecution(&system_context); - finish_execution((exec+1) < params.maxexecutions); - //restore random number generator state after rollback - setstate(random_state); - } - - model_print("******* Model-checking complete: *******\n"); - print_stats(); - - /* Have the trace analyses dump their output. */ - for (unsigned int i = 0;i < trace_analyses.size();i++) - trace_analyses[i]->finish(); - - /* unlink tmp file created by last child process */ - char filename[256]; - snprintf_(filename, sizeof(filename), "C11FuzzerTmp%d", getpid()); - unlink(filename); -}