model: remove public check_promises() interface
[model-checker.git] / model.cc
index 6a79ad4a90f80ff1ee3177511683cd96c67ebb54..afb72b0aae490bda4a0464bb04837b380d81f4ca 100644 (file)
--- a/model.cc
+++ b/model.cc
@@ -2,6 +2,7 @@
 #include <algorithm>
 #include <mutex>
 #include <new>
+#include <stdarg.h>
 
 #include "model.h"
 #include "action.h"
 #include "datarace.h"
 #include "threads-model.h"
 #include "output.h"
+#include "traceanalysis.h"
+#include "bugmessage.h"
 
 #define INITIAL_THREAD_ID      0
 
 ModelChecker *model;
 
-struct bug_message {
-       bug_message(const char *str) {
-               const char *fmt = "  [BUG] %s\n";
-               msg = (char *)snapshot_malloc(strlen(fmt) + strlen(str));
-               sprintf(msg, fmt, str);
-       }
-       ~bug_message() { if (msg) snapshot_free(msg); }
-
-       char *msg;
-       void print() { model_print("%s", msg); }
-
-       SNAPSHOTALLOC
-};
-
 /**
  * Structure for holding small ModelChecker members that should be snapshotted
  */
@@ -91,6 +80,7 @@ ModelChecker::ModelChecker(struct model_params params) :
        thrd_last_action(new SnapVector<ModelAction *>(1)),
        thrd_last_fence_release(new SnapVector<ModelAction *>()),
        node_stack(new NodeStack()),
+       trace_analyses(new ModelVector<TraceAnalysis *>()),
        priv(new struct model_snapshot_members()),
        mo_graph(new CycleGraph())
 {
@@ -120,6 +110,9 @@ ModelChecker::~ModelChecker()
        delete thrd_last_action;
        delete thrd_last_fence_release;
        delete node_stack;
+       for (unsigned int i = 0; i < trace_analyses->size(); i++)
+               delete (*trace_analyses)[i];
+       delete trace_analyses;
        delete scheduler;
        delete mo_graph;
        delete priv;
@@ -145,6 +138,18 @@ static SnapVector<action_list_t> * get_safe_ptr_vect_action(HashTable<void *, Sn
        return tmp;
 }
 
+action_list_t * ModelChecker::get_actions_on_obj(void * obj, thread_id_t tid) {
+       SnapVector<action_list_t> *wrv=obj_thrd_map->get(obj);
+       if (wrv==NULL)
+               return NULL;
+       unsigned int thread=id_to_int(tid);
+       if (thread < wrv->size())
+               return &(*wrv)[thread];
+       else
+               return NULL;
+}
+
+
 /**
  * Restores user program to initial state and resets all model-checker data
  * structures.
@@ -154,9 +159,6 @@ void ModelChecker::reset_to_initial_state()
        DEBUG("+++ Resetting to initial state +++\n");
        node_stack->reset_execution();
 
-       /* Print all model-checker output before rollback */
-       fflush(model_out);
-
        /**
         * FIXME: if we utilize partial rollback, we will need to free only
         * those pending actions which were NOT pending before the rollback
@@ -422,9 +424,16 @@ bool ModelChecker::is_complete_execution() const
  * @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)
+bool ModelChecker::assert_bug(const char *msg, ...)
 {
-       priv->bugs.push_back(new bug_message(msg));
+       char str[800];
+
+       va_list ap;
+       va_start(ap, msg);
+       vsnprintf(str, sizeof(str), msg, ap);
+       va_end(ap);
+
+       priv->bugs.push_back(new bug_message(str));
 
        if (isfeasibleprefix()) {
                set_assert();
@@ -548,6 +557,7 @@ bool ModelChecker::next_execution()
                        assert_bug("Deadlock detected");
 
                checkDataRaces();
+               run_trace_analyses();
        }
 
        record_stats();
@@ -573,6 +583,12 @@ bool ModelChecker::next_execution()
        return true;
 }
 
+/** @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(action_trace);
+}
+
 /**
  * @brief Find the last fence-related backtracking conflict for a ModelAction
  *
@@ -755,6 +771,7 @@ void ModelChecker::set_backtracking(ModelAction *act)
 
        Node *node = prev->get_node()->get_parent();
 
+       /* See Dynamic Partial Order Reduction (addendum), POPL '05 */
        int low_tid, high_tid;
        if (node->enabled_status(t->get_id()) == THREAD_ENABLED) {
                low_tid = id_to_int(act->get_tid());
@@ -771,6 +788,7 @@ void ModelChecker::set_backtracking(ModelAction *act)
                if (i >= node->get_num_threads())
                        break;
 
+               /* See Dynamic Partial Order Reduction (addendum), POPL '05 */
                /* Don't backtrack into a point where the thread is disabled or sleeping. */
                if (node->enabled_status(tid) != THREAD_ENABLED)
                        continue;
@@ -961,7 +979,7 @@ bool ModelChecker::process_mutex(ModelAction *curr)
                ModelAction *unlock = get_last_unlock(curr);
                //synchronize with the previous unlock statement
                if (unlock != NULL) {
-                       curr->synchronize_with(unlock);
+                       synchronize(unlock, curr);
                        return true;
                }
                break;
@@ -1171,8 +1189,7 @@ bool ModelChecker::process_fence(ModelAction *curr)
                        rel_heads_list_t release_heads;
                        get_release_seq_heads(curr, act, &release_heads);
                        for (unsigned int i = 0; i < release_heads.size(); i++)
-                               if (!curr->synchronize_with(release_heads[i]))
-                                       set_bad_synchronization();
+                               synchronize(release_heads[i], curr);
                        if (release_heads.size() != 0)
                                updated = true;
                }
@@ -1213,7 +1230,7 @@ bool ModelChecker::process_thread_action(ModelAction *curr)
        case THREAD_JOIN: {
                Thread *blocking = curr->get_thread_operand();
                ModelAction *act = get_last_action(blocking->get_id());
-               curr->synchronize_with(act);
+               synchronize(act, curr);
                updated = true; /* trigger rel-seq checks */
                break;
        }
@@ -1287,10 +1304,8 @@ void ModelChecker::process_relseq_fixup(ModelAction *curr, work_queue_t *work_qu
                 */
 
                /* Must synchronize */
-               if (!acquire->synchronize_with(release)) {
-                       set_bad_synchronization();
+               if (!synchronize(release, acquire))
                        return;
-               }
                /* Re-check all pending release sequences */
                work_queue->push_back(CheckRelSeqWorkEntry(NULL));
                /* Re-check act for mo_graph edges */
@@ -1301,7 +1316,7 @@ void ModelChecker::process_relseq_fixup(ModelAction *curr, work_queue_t *work_qu
                for (; (*rit) != acquire; rit++) {
                        ModelAction *propagate = *rit;
                        if (acquire->happens_before(propagate)) {
-                               propagate->synchronize_with(acquire);
+                               synchronize(acquire, propagate);
                                /* Re-check 'propagate' for mo_graph edges */
                                work_queue->push_back(MOEdgeWorkEntry(propagate));
                        }
@@ -1410,15 +1425,35 @@ bool ModelChecker::read_from(ModelAction *act, const ModelAction *rf)
                get_release_seq_heads(act, act, &release_heads);
                int num_heads = release_heads.size();
                for (unsigned int i = 0; i < release_heads.size(); i++)
-                       if (!act->synchronize_with(release_heads[i])) {
-                               set_bad_synchronization();
+                       if (!synchronize(release_heads[i], act))
                                num_heads--;
-                       }
                return num_heads > 0;
        }
        return false;
 }
 
+/**
+ * @brief Synchronizes two actions
+ *
+ * When A synchronizes with B (or A --sw-> B), B inherits A's clock vector.
+ * This function performs the synchronization as well as providing other hooks
+ * for other checks along with synchronization.
+ *
+ * @param first The left-hand side of the synchronizes-with relation
+ * @param second The right-hand side of the synchronizes-with relation
+ * @return True if the synchronization was successful (i.e., was consistent
+ * with the execution order); false otherwise
+ */
+bool ModelChecker::synchronize(const ModelAction *first, ModelAction *second)
+{
+       if (*second < *first) {
+               set_bad_synchronization();
+               return false;
+       }
+       check_promises(first->get_tid(), second->get_cv(), first->get_cv());
+       return second->synchronize_with(first);
+}
+
 /**
  * Check promises and eliminate potentially-satisfying threads when a thread is
  * blocked (e.g., join, lock). A thread which is waiting on another thread can
@@ -1490,20 +1525,9 @@ ModelAction * ModelChecker::check_current_action(ModelAction *curr)
 {
        ASSERT(curr);
        bool second_part_of_rmw = curr->is_rmwc() || curr->is_rmw();
-
-       if (!check_action_enabled(curr)) {
-               /* Make the execution look like we chose to run this action
-                * much later, when a lock/join can succeed */
-               get_thread(curr)->set_pending(curr);
-               scheduler->sleep(get_thread(curr));
-               return NULL;
-       }
-
        bool newly_explored = initialize_curr_action(&curr);
 
        DBG();
-       if (DBG_ENABLED())
-               curr->print();
 
        wake_up_sleeping_actions(curr);
 
@@ -1800,16 +1824,15 @@ bool ModelChecker::check_recency(ModelAction *curr, const T *rf) const
 }
 
 /**
- * Updates the mo_graph with the constraints imposed from the current
+ * @brief Updates the mo_graph with the constraints imposed from the current
  * read.
  *
  * Basic idea is the following: Go through each other thread and find
  * the last action that happened before our read.  Two cases:
  *
- * (1) The action is a write => that write must either occur before
+ * -# The action is a write: that write must either occur before
  * the write we read from or be the write we read from.
- *
- * (2) The action is a read => the write that that action read from
+ * -# The action is a read: the write that that action read from
  * must occur before the write we read from or be the same write.
  *
  * @param curr The current action. Must be a read.
@@ -2349,14 +2372,10 @@ bool ModelChecker::resolve_release_sequences(void *location, work_queue_t *work_
                rel_heads_list_t release_heads;
                bool complete;
                complete = release_seq_heads(rf, &release_heads, pending);
-               for (unsigned int i = 0; i < release_heads.size(); i++) {
-                       if (!acquire->has_synchronized_with(release_heads[i])) {
-                               if (acquire->synchronize_with(release_heads[i]))
+               for (unsigned int i = 0; i < release_heads.size(); i++)
+                       if (!acquire->has_synchronized_with(release_heads[i]))
+                               if (synchronize(release_heads[i], acquire))
                                        updated = true;
-                               else
-                                       set_bad_synchronization();
-                       }
-               }
 
                if (updated) {
                        /* Re-check all pending release sequences */
@@ -2370,7 +2389,7 @@ bool ModelChecker::resolve_release_sequences(void *location, work_queue_t *work_
                        for (; (*rit) != acquire; rit++) {
                                ModelAction *propagate = *rit;
                                if (acquire->happens_before(propagate)) {
-                                       propagate->synchronize_with(acquire);
+                                       synchronize(acquire, propagate);
                                        /* Re-check 'propagate' for mo_graph edges */
                                        work_queue->push_back(MOEdgeWorkEntry(propagate));
                                }
@@ -3078,32 +3097,14 @@ Thread * ModelChecker::take_step(ModelAction *curr)
        Thread *curr_thrd = get_thread(curr);
        ASSERT(curr_thrd->get_state() == THREAD_READY);
 
+       ASSERT(check_action_enabled(curr)); /* May have side effects? */
        curr = check_current_action(curr);
-
-       /* Infeasible -> don't take any more steps */
-       if (is_infeasible())
-               return NULL;
-       else if (isfeasibleprefix() && have_bug_reports()) {
-               set_assert();
-               return NULL;
-       }
-
-       if (params.bound != 0 && priv->used_sequence_numbers > params.bound)
-               return NULL;
+       ASSERT(curr);
 
        if (curr_thrd->is_blocked() || curr_thrd->is_complete())
                scheduler->remove_thread(curr_thrd);
 
-       Thread *next_thrd = NULL;
-       if (curr)
-               next_thrd = action_select_next_thread(curr);
-       if (!next_thrd)
-               next_thrd = get_next_thread();
-
-       DEBUG("(%d, %d)\n", curr_thrd ? id_to_int(curr_thrd->get_id()) : -1,
-                       next_thrd ? id_to_int(next_thrd->get_id()) : -1);
-
-       return next_thrd;
+       return action_select_next_thread(curr);
 }
 
 /** Wrapper to run the user's main function, with appropriate arguments */
@@ -3112,6 +3113,27 @@ void user_main_wrapper(void *)
        user_main(model->params.argc, model->params.argv);
 }
 
+/** @return True if the execution has taken too many steps */
+bool ModelChecker::too_many_steps() const
+{
+       return params.bound != 0 && priv->used_sequence_numbers > params.bound;
+}
+
+bool ModelChecker::should_terminate_execution()
+{
+       /* Infeasible -> don't take any more steps */
+       if (is_infeasible())
+               return true;
+       else if (isfeasibleprefix() && have_bug_reports()) {
+               set_assert();
+               return true;
+       }
+
+       if (too_many_steps())
+               return true;
+       return false;
+}
+
 /** @brief Run ModelChecker for the user program */
 void ModelChecker::run()
 {
@@ -3133,7 +3155,16 @@ void ModelChecker::run()
                                if (!thr->is_model_thread() && !thr->is_complete() && !thr->get_pending()) {
                                        switch_from_master(thr);
                                        if (thr->is_waiting_on(thr))
-                                               assert_bug("Deadlock detected");
+                                               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 && is_enabled(th) && !check_action_enabled(act)) {
+                                       scheduler->sleep(th);
                                }
                        }
 
@@ -3142,11 +3173,16 @@ void ModelChecker::run()
                        if (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 = take_step(curr);
-               } while (t && !t->is_model_thread());
+               } while (!should_terminate_execution());
 
                /*
                 * Launch end-of-execution release sequence fixups only when