model: add infeasibility debugging
[model-checker.git] / model.cc
index 79c52d019d35f2ad39d22300597ee4575682f61d..38c5d4a9396f82bde9b8341fac00f8b2d8dc63a6 100644 (file)
--- a/model.cc
+++ b/model.cc
@@ -21,6 +21,7 @@ ModelChecker::ModelChecker(struct model_params params) :
        /* Initialize default scheduler */
        scheduler(new Scheduler()),
        num_executions(0),
+       num_feasible_executions(0),
        params(params),
        diverge(NULL),
        action_trace(new action_list_t()),
@@ -174,6 +175,8 @@ bool ModelChecker::next_execution()
        DBG();
 
        num_executions++;
+       if (isfinalfeasible())
+               num_feasible_executions++;
 
        if (isfinalfeasible() || DBG_ENABLED())
                print_summary();
@@ -417,16 +420,36 @@ Thread * ModelChecker::check_current_action(ModelAction *curr)
                break;
        }
 
-       bool updated = false;
+       work_queue_t work_queue(1, CheckCurrWorkEntry(curr));
+
+       while (!work_queue.empty()) {
+               WorkQueueEntry work = work_queue.front();
+               work_queue.pop_front();
 
-       if (curr->is_read() && process_read(curr, second_part_of_rmw))
-               updated = true;
+               switch (work.type) {
+               case WORK_CHECK_CURR_ACTION: {
+                       ModelAction *act = work.action;
+                       bool updated = false;
+                       if (act->is_read() && process_read(act, second_part_of_rmw))
+                               updated = true;
 
-       if (curr->is_write() && process_write(curr))
-               updated = true;
+                       if (act->is_write() && process_write(act))
+                               updated = true;
 
-       if (updated)
-               resolve_release_sequences(curr->get_location());
+                       if (updated)
+                               work_queue.push_back(CheckRelSeqWorkEntry(act->get_location()));
+                       break;
+               }
+               case WORK_CHECK_RELEASE_SEQ:
+                       resolve_release_sequences(work.location, &work_queue);
+                       break;
+               case WORK_CHECK_MO_EDGES:
+                       /** @todo Perform follow-up mo_graph checks */
+               default:
+                       ASSERT(false);
+                       break;
+               }
+       }
 
        /* Add action to list.  */
        if (!second_part_of_rmw)
@@ -477,11 +500,24 @@ bool ModelChecker::isfeasible() {
 /** @returns whether the current partial trace is feasible other than
  * multiple RMW reading from the same store. */
 bool ModelChecker::isfeasibleotherthanRMW() {
+       if (DBG_ENABLED()) {
+               if (mo_graph->checkForCycles())
+                       DEBUG("Infeasible: modification order cycles\n");
+               if (failed_promise)
+                       DEBUG("Infeasible: failed promise\n");
+               if (too_many_reads)
+                       DEBUG("Infeasible: too many reads\n");
+               if (promises_expired())
+                       DEBUG("Infeasible: promises expired\n");
+       }
        return !mo_graph->checkForCycles() && !failed_promise && !too_many_reads && !promises_expired();
 }
 
 /** Returns whether the current completed trace is feasible. */
 bool ModelChecker::isfinalfeasible() {
+       if (DBG_ENABLED() && promises->size() != 0)
+               DEBUG("Infeasible: unrevolved promises\n");
+
        return isfeasible() && promises->size() == 0;
 }
 
@@ -594,7 +630,18 @@ void ModelChecker::check_recency(ModelAction *curr, bool already_added) {
 }
 
 /**
- * Updates the mo_graph with the constraints imposed from the current read.
+ * 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 lastest action that happened before our read.  Two cases:
+ *
+ * (1) 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
+ * must occur before the write we read from or be the same write.
+ *
  * @param curr The current action. Must be a read.
  * @param rf The action that curr reads from. Must be a write.
  * @return True if modification order edges were added; false otherwise
@@ -637,7 +684,22 @@ bool ModelChecker::r_modification_order(ModelAction *curr, const ModelAction *rf
        return added;
 }
 
-/** Updates the mo_graph with the constraints imposed from the current read. */
+/** This method fixes up the modification order when we resolve a
+ *  promises.  The basic problem is that actions that occur after the
+ *  read curr could not property add items to the modification order
+ *  for our read.
+ *  
+ *  So for each thread, we find the earliest item that happens after
+ *  the read curr.  This is the item we have to fix up with additional
+ *  constraints.  If that action is write, we add a MO edge between
+ *  the Action rf and that action.  If the action is a read, we add a
+ *  MO edge between the Action rf, and whatever the read accessed.
+ *
+ * @param curr is the read ModelAction that we are fixing up MO edges for.
+ * @param rf is the write ModelAction that curr reads from.
+ *
+ */
+
 void ModelChecker::post_r_modification_order(ModelAction *curr, const ModelAction *rf)
 {
        std::vector<action_list_t> *thrd_lists = obj_thrd_map->get_safe_ptr(curr->get_location());
@@ -676,6 +738,23 @@ void ModelChecker::post_r_modification_order(ModelAction *curr, const ModelActio
 
 /**
  * Updates the mo_graph with the constraints imposed from the current write.
+ *
+ * Basic idea is the following: Go through each other thread and find
+ * the lastest action that happened before our write.  Two cases:
+ *
+ * (1) The action is a write => that write must occur before
+ * the current write
+ *
+ * (2) The action is a read => the write that that action read from
+ * must occur before the current write.
+ *
+ * This method also handles two other issues:
+ *
+ * (I) Sequential Consistency: Making sure that if the current write is
+ * seq_cst, that it occurs after the previous seq_cst write.
+ *
+ * (II) Sending the write back to non-synchronizing reads.
+ *
  * @param curr The current action. Must be a write.
  * @return True if modification order edges were added; false otherwise
  */
@@ -916,9 +995,12 @@ void ModelChecker::get_release_seq_heads(ModelAction *act,
  *
  * @param location The location/object that should be checked for release
  * sequence resolutions
- * @return True if any updates occurred (new synchronization, new mo_graph edges)
+ * @param work_queue The work queue to which to add work items as they are
+ * generated
+ * @return True if any updates occurred (new synchronization, new mo_graph
+ * edges)
  */
-bool ModelChecker::resolve_release_sequences(void *location)
+bool ModelChecker::resolve_release_sequences(void *location, work_queue_t *work_queue)
 {
        std::list<ModelAction *> *list;
        list = lazy_sync_with_release->getptr(location);
@@ -941,14 +1023,18 @@ bool ModelChecker::resolve_release_sequences(void *location)
                }
 
                if (updated) {
+                       /* Re-check act for mo_graph edges */
+                       work_queue->push_back(MOEdgeWorkEntry(act));
+
                        /* propagate synchronization to later actions */
                        action_list_t::reverse_iterator it = action_trace->rbegin();
                        while ((*it) != act) {
                                ModelAction *propagate = *it;
-                               if (act->happens_before(propagate))
-                                       /** @todo new mo_graph edges along with
-                                        * this synchronization? */
+                               if (act->happens_before(propagate)) {
                                        propagate->synchronize_with(act);
+                                       /* Re-check 'propagate' for mo_graph edges */
+                                       work_queue->push_back(MOEdgeWorkEntry(propagate));
+                               }
                        }
                }
                if (complete) {
@@ -1051,7 +1137,11 @@ bool ModelChecker::resolve_promises(ModelAction *write)
                        if (read->is_rmw()) {
                                mo_graph->addRMWEdge(write, read);
                        }
+                       //First fix up the modification order for actions that happened
+                       //before the read
                        r_modification_order(read, write);
+                       //Next fix up the modification order for actions that happened
+                       //after the read.
                        post_r_modification_order(read, write);
                        promises->erase(promises->begin() + promise_index);
                        resolved = true;
@@ -1190,9 +1280,15 @@ void ModelChecker::print_summary()
 {
        printf("\n");
        printf("Number of executions: %d\n", num_executions);
+       printf("Number of feasible executions: %d\n", num_feasible_executions);
        printf("Total nodes created: %d\n", node_stack->get_total_nodes());
 
+#if SUPPORT_MOD_ORDER_DUMP
        scheduler->print();
+       char buffername[100];
+       sprintf(buffername, "exec%u",num_executions);
+       mo_graph->dumpGraphToFile(buffername);
+#endif
 
        if (!isfinalfeasible())
                printf("INFEASIBLE EXECUTION!\n");