X-Git-Url: http://plrg.eecs.uci.edu/git/?p=c11tester.git;a=blobdiff_plain;f=model.cc;h=078a1ce75849ebc48334958a7842b22dd6c958d1;hp=6214e9bcfcea73f4668351197820473e0ae0d923;hb=829aac4055269992c26fed5530cfdaf59775b2a9;hpb=fdfcb2e993567ebdc484639dd18dfc4aadda2609 diff --git a/model.cc b/model.cc index 6214e9bc..078a1ce7 100644 --- a/model.cc +++ b/model.cc @@ -18,34 +18,44 @@ #include "history.h" #include "bugmessage.h" #include "params.h" +#include "plugins.h" ModelChecker *model = NULL; -/** Wrapper to run the user's main function, with appropriate arguments */ -void user_main_wrapper(void *) -{ - user_main(model->params.argc, model->params.argv); +void placeholder(void *) { + ASSERT(0); } /** @brief Constructor */ ModelChecker::ModelChecker() : /* Initialize default scheduler */ params(), - restart_flag(false), scheduler(new Scheduler()), - execution(new ModelExecution(this, scheduler)), history(new ModelHistory()), + execution(new ModelExecution(this, scheduler)), execution_number(1), trace_analyses(), inspect_plugin(NULL) { + model_print("C11Tester\n" + "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_thread = new Thread(execution->get_next_id(), (thrd_t *) model_malloc(sizeof(thrd_t)), &user_main_wrapper, NULL, NULL); // L: user_main_wrapper passes the user program + 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()); +#endif execution->add_thread(init_thread); scheduler->set_current_thread(init_thread); + register_plugins(); execution->setParams(¶ms); param_defaults(¶ms); + parse_options(¶ms); initRaceDetector(); + /* Configure output redirection for the model-checker */ + redirect_output(); + install_trace_analyses(get_execution()); } /** @brief Destructor */ @@ -74,7 +84,7 @@ void ModelChecker::reset_to_initial_state() for (unsigned int i = 0;i < get_num_threads();i++) delete get_thread(int_to_id(i))->get_pending(); - snapshot_backtrack_before(0); + snapshot_roll_back(snapshot); } /** @return the number of user threads created during this execution */ @@ -127,7 +137,7 @@ Thread * ModelChecker::get_next_thread() * @param msg Descriptive message for the bug (do not include newline char) * @return True if bug is immediately-feasible */ -bool ModelChecker::assert_bug(const char *msg, ...) +void ModelChecker::assert_bug(const char *msg, ...) { char str[800]; @@ -136,7 +146,7 @@ bool ModelChecker::assert_bug(const char *msg, ...) vsnprintf(str, sizeof(str), msg, ap); va_end(ap); - return execution->assert_bug(str); + execution->assert_bug(str); } /** @@ -147,8 +157,8 @@ bool ModelChecker::assert_bug(const char *msg, ...) void ModelChecker::assert_user_bug(const char *msg) { /* If feasible bug, bail out now */ - if (assert_bug(msg)) - switch_to_master(NULL); + assert_bug(msg); + switch_to_master(NULL); } /** @brief Print bug report listing for this execution (if any bugs exist) */ @@ -160,7 +170,7 @@ void ModelChecker::print_bugs() const bugs->size(), bugs->size() > 1 ? "s" : ""); for (unsigned int i = 0;i < bugs->size();i++) - (*bugs)[i]->print(); + (*bugs)[i] -> print(); } /** @@ -171,16 +181,13 @@ void ModelChecker::print_bugs() const */ void ModelChecker::record_stats() { - stats.num_total++; - if (!execution->isfeasibleprefix()) - stats.num_infeasible++; - else if (execution->have_bug_reports()) - stats.num_buggy_executions++; + stats.num_total ++; + if (execution->have_bug_reports()) + stats.num_buggy_executions ++; else if (execution->is_complete_execution()) - stats.num_complete++; + stats.num_complete ++; else { - stats.num_redundant++; - + //All threads are sleeping /** * @todo We can violate this ASSERT() when fairness/sleep sets * conflict to cause an execution to terminate, e.g. with: @@ -194,9 +201,7 @@ void ModelChecker::record_stats() void ModelChecker::print_stats() const { model_print("Number of complete, bug-free executions: %d\n", stats.num_complete); - model_print("Number of redundant executions: %d\n", stats.num_redundant); model_print("Number of buggy executions: %d\n", stats.num_buggy_executions); - model_print("Number of infeasible executions: %d\n", stats.num_infeasible); model_print("Total executions: %d\n", stats.num_total); } @@ -231,12 +236,11 @@ void ModelChecker::print_execution(bool printbugs) const * @return If there are more executions to explore, return true. Otherwise, * return false. */ -bool ModelChecker::next_execution() +void ModelChecker::finish_execution(bool more_executions) { DBG(); /* Is this execution a feasible execution that's worth bug-checking? */ - bool complete = execution->isfeasibleprefix() && - (execution->is_complete_execution() || + bool complete = (execution->is_complete_execution() || execution->have_bug_reports()); /* End-of-execution bug checks */ @@ -244,7 +248,6 @@ bool ModelChecker::next_execution() if (execution->is_deadlocked()) assert_bug("Deadlock detected"); - checkDataRaces(); run_trace_analyses(); } @@ -255,20 +258,17 @@ bool ModelChecker::next_execution() else clear_program_output(); - if (restart_flag) { - do_restart(); - return true; - } // test code - execution_number++; - reset_to_initial_state(); - return false; + execution_number ++; + if (more_executions) + reset_to_initial_state(); + history->set_new_exec_flag(); } /** @brief Run trace analyses on complete trace */ void ModelChecker::run_trace_analyses() { - for (unsigned int i = 0;i < trace_analyses.size();i++) - trace_analyses[i]->analyze(execution->get_action_trace()); + for (unsigned int i = 0;i < trace_analyses.size();i ++) + trace_analyses[i] -> analyze(execution->get_action_trace()); } /** @@ -317,11 +317,11 @@ void ModelChecker::switch_from_master(Thread *thread) */ uint64_t ModelChecker::switch_to_master(ModelAction *act) { - if (forklock) { + if (modellock) { static bool fork_message_printed = false; if (!fork_message_printed) { - model_print("Fork handler trying to call into model checker...\n"); + model_print("Fork handler or dead thread trying to call into model checker...\n"); fork_message_printed = true; } delete act; @@ -344,58 +344,253 @@ uint64_t ModelChecker::switch_to_master(ModelAction *act) return old->get_return_value(); } -static void runChecker() { - model->run(); - delete model; +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); + } + } else + handleChosenThread(old); } -void ModelChecker::startChecker() { - startExecution(get_system_context(), runChecker); +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); + } + } else + handleChosenThread(old); } -bool ModelChecker::should_terminate_execution() +Thread* ModelChecker::getNextThread() { - /* Infeasible -> don't take any more steps */ - if (execution->is_infeasible()) - return true; - else if (execution->isfeasibleprefix() && execution->have_bug_reports()) { - execution->set_assert(); - return true; + Thread *nextThread = nullptr; + 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; + } + ModelAction *act = thr->get_pending(); + + if (act && execution->is_enabled(thr) && !execution->check_action_enabled(act)) { + scheduler->sleep(thr); + } + + chooseThread(act, thr); } - return false; + return nextThread; } -/** @brief Restart ModelChecker upon returning to the run loop of the - * model checker. */ -void ModelChecker::restart() +void ModelChecker::finishRunExecution(Thread *old) { - restart_flag = true; + scheduler->set_current_thread(NULL); + if (Thread::swap(old, &system_context) < 0) { + perror("swap threads"); + exit(EXIT_FAILURE); + } } -void ModelChecker::do_restart() +void ModelChecker::finishRunExecution(ucontext_t *old) { - restart_flag = false; - reset_to_initial_state(); - memset(&stats,0,sizeof(struct execution_stats)); - execution_number = 1; + scheduler->set_current_thread(NULL); } -void ModelChecker::startMainThread() { - init_thread->set_state(THREAD_RUNNING); - scheduler->set_current_thread(init_thread); - thread_startup(); -} - -static bool is_nonsc_write(const ModelAction *act) { - if (act->get_type() == ATOMIC_WRITE) { - std::memory_order order = act->get_mo(); - switch(order) { - case std::memory_order_relaxed: - case std::memory_order_release: - return true; - default: - return false; +void ModelChecker::consumeAction() +{ + ModelAction *curr = chosen_thread->get_pending(); + Thread * th = thread_current(); + if (curr->get_type() == THREAD_FINISH && th != NULL) { + // Thread finish must be consumed in the master context + scheduler->set_current_thread(NULL); + if (Thread::swap(th, &system_context) < 0) { + perror("swap threads"); + exit(EXIT_FAILURE); } + } else { + chosen_thread->set_pending(NULL); + chosen_thread = execution->take_step(curr); + } +} + +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; + } + } +} + +uint64_t ModelChecker::switch_thread(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; + } + delete act; + return 0; + } + DBG(); + Thread *old = thread_current(); + ASSERT(!old->get_pending()); + + if (inspect_plugin != NULL) { + inspect_plugin->inspectModelAction(act); + } + + old->set_pending(act); + + if (old->is_waiting_on(old)) + assert_bug("Deadlock detected (thread %u)", curr_thread_num); + + if (act && execution->is_enabled(old) && !execution->check_action_enabled(act)) { + scheduler->sleep(old); + } + chooseThread(act, old); + + curr_thread_num++; + Thread* next = getNextThread(); + if (next != nullptr) + handleNewValidThread(old, next); + else { + old->set_state(THREAD_READY); // Just to avoid the first ASSERT in ModelExecution::take_step + 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) +{ + if (execution->has_asserted()) + finishRunExecution(old); + if (!chosen_thread) + chosen_thread = get_next_thread(); + if (!chosen_thread || chosen_thread->is_model_thread()) + 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 + continueRunExecution(old); + } else { + /* Consume the next action for a Thread */ + consumeAction(); + + if (should_terminate_execution()) + finishRunExecution(old); + else + continueRunExecution(old); + } +} + +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()) + 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); + } else + + { + /* Consume the next action for a Thread */ + consumeAction(); + + if (should_terminate_execution()) + finishRunExecution(old); + else + startRunExecution(old); + } +} + + +static void runChecker() { + model->run(); + delete model; +} + +void ModelChecker::startChecker() { + startExecution(get_system_context(), runChecker); + snapshot = take_snapshot(); + initMainThread(); +} + +bool ModelChecker::should_terminate_execution() +{ + if (execution->have_bug_reports()) { + execution->set_assert(); + return true; + } else if (execution->isFinished()) { + return true; } return false; } @@ -406,74 +601,16 @@ 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++) { - Thread * t = init_thread; - + chosen_thread = init_thread; do { - /* - * Stash next pending action(s) for thread(s). There - * should only need to stash one thread's action--the - * thread which just took a step--plus the first step - * for any newly-created thread - */ - ModelAction * pending; - for (unsigned int i = 0;i < get_num_threads();i++) { - thread_id_t tid = int_to_id(i); - Thread *thr = get_thread(tid); - if (!thr->is_model_thread() && !thr->is_complete() && ((!(pending=thr->get_pending())) || is_nonsc_write(pending)) ) { - switch_from_master(thr); // L: context swapped, and action type of thr changed. - if (thr->is_waiting_on(thr)) - assert_bug("Deadlock detected (thread %u)", i); - } - } - - /* Don't schedule threads which should be disabled */ - for (unsigned int i = 0;i < get_num_threads();i++) { - Thread *th = get_thread(int_to_id(i)); - ModelAction *act = th->get_pending(); - if (act && execution->is_enabled(th) && !execution->check_action_enabled(act)) { - scheduler->sleep(th); - } - } - - for (unsigned int i = 1;i < get_num_threads();i++) { - Thread *th = get_thread(int_to_id(i)); - ModelAction *act = th->get_pending(); - if (act && execution->is_enabled(th) && (th->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) { - t = th; - break; - } - } else if (act->get_type() == THREAD_CREATE || \ - act->get_type() == PTHREAD_CREATE || \ - act->get_type() == THREAD_START || \ - act->get_type() == THREAD_FINISH) { - t = th; - break; - } - } - } - - /* Catch assertions from prior take_step or from - * between-ModelAction bugs (e.g., data races) */ - - if (execution->has_asserted()) - break; - if (!t) - t = get_next_thread(); - if (!t || t->is_model_thread()) - break; - - /* Consume the next action for a Thread */ - ModelAction *curr = t->get_pending(); - t->set_pending(NULL); - t = execution->take_step(curr); + thread_chosen = false; + curr_thread_num = 1; + startRunExecution(&system_context); } while (!should_terminate_execution()); - next_execution(); + + finish_execution((exec+1) < params.maxexecutions); //restore random number generator state after rollback setstate(random_state); } @@ -484,4 +621,9 @@ void ModelChecker::run() /* 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); }