move more configurables and add documentation
[model-checker.git] / model.cc
1 #include <stdio.h>
2
3 #include "model.h"
4 #include "action.h"
5 #include "nodestack.h"
6 #include "schedule.h"
7 #include "snapshot-interface.h"
8 #include "common.h"
9 #include "clockvector.h"
10 #include "cyclegraph.h"
11
12 #define INITIAL_THREAD_ID       0
13
14 ModelChecker *model;
15
16 /** @brief Constructor */
17 ModelChecker::ModelChecker()
18         :
19         /* Initialize default scheduler */
20         scheduler(new Scheduler()),
21         /* First thread created will have id INITIAL_THREAD_ID */
22         next_thread_id(INITIAL_THREAD_ID),
23         used_sequence_numbers(0),
24
25         num_executions(0),
26         current_action(NULL),
27         diverge(NULL),
28         nextThread(THREAD_ID_T_NONE),
29         action_trace(new action_list_t()),
30         thread_map(new HashTable<int, Thread *, int>()),
31         obj_map(new HashTable<const void *, action_list_t, uintptr_t, 4>()),
32         obj_thrd_map(new HashTable<void *, std::vector<action_list_t>, uintptr_t, 4 >()),
33         thrd_last_action(new std::vector<ModelAction *>(1)),
34         node_stack(new NodeStack()),
35         next_backtrack(NULL),
36         cyclegraph(new CycleGraph())
37 {
38 }
39
40 /** @brief Destructor */
41 ModelChecker::~ModelChecker()
42 {
43         /*      std::map<int, Thread *>::iterator it;
44         for (it = thread_map->begin(); it != thread_map->end(); it++)
45         delete (*it).second;*/
46         delete thread_map;
47
48         delete obj_thrd_map;
49         delete obj_map;
50         delete action_trace;
51         delete thrd_last_action;
52         delete node_stack;
53         delete scheduler;
54         delete cyclegraph;
55 }
56
57 /**
58  * Restores user program to initial state and resets all model-checker data
59  * structures.
60  */
61 void ModelChecker::reset_to_initial_state()
62 {
63         DEBUG("+++ Resetting to initial state +++\n");
64         node_stack->reset_execution();
65         current_action = NULL;
66         next_thread_id = INITIAL_THREAD_ID;
67         used_sequence_numbers = 0;
68         nextThread = 0;
69         next_backtrack = NULL;
70         snapshotObject->backTrackBeforeStep(0);
71 }
72
73 /** @returns a thread ID for a new Thread */
74 thread_id_t ModelChecker::get_next_id()
75 {
76         return next_thread_id++;
77 }
78
79 /** @returns the number of user threads created during this execution */
80 int ModelChecker::get_num_threads()
81 {
82         return next_thread_id;
83 }
84
85 /** @returns a sequence number for a new ModelAction */
86 modelclock_t ModelChecker::get_next_seq_num()
87 {
88         return ++used_sequence_numbers;
89 }
90
91 /**
92  * Performs the "scheduling" for the model-checker. That is, it checks if the
93  * model-checker has selected a "next thread to run" and returns it, if
94  * available. This function should be called from the Scheduler routine, where
95  * the Scheduler falls back to a default scheduling routine if needed.
96  *
97  * @return The next thread chosen by the model-checker. If the model-checker
98  * makes no selection, retuns NULL.
99  */
100 Thread * ModelChecker::schedule_next_thread()
101 {
102         Thread *t;
103         if (nextThread == THREAD_ID_T_NONE)
104                 return NULL;
105         t = thread_map->get(id_to_int(nextThread));
106
107         ASSERT(t != NULL);
108
109         return t;
110 }
111
112 /**
113  * Choose the next thread in the replay sequence.
114  *
115  * If the replay sequence has reached the 'diverge' point, returns a thread
116  * from the backtracking set. Otherwise, simply returns the next thread in the
117  * sequence that is being replayed.
118  */
119 thread_id_t ModelChecker::get_next_replay_thread()
120 {
121         thread_id_t tid;
122
123         /* Have we completed exploring the preselected path? */
124         if (diverge == NULL)
125                 return THREAD_ID_T_NONE;
126
127         /* Else, we are trying to replay an execution */
128         ModelAction * next = node_stack->get_next()->get_action();
129
130         if (next == diverge) {
131                 Node *nextnode = next->get_node();
132                 /* Reached divergence point */
133                 if (nextnode->increment_read_from()) {
134                         /* The next node will read from a different value */
135                         tid = next->get_tid();
136                         node_stack->pop_restofstack(2);
137                 } else {
138                         /* Make a different thread execute for next step */
139                         Node *node = nextnode->get_parent();
140                         tid = node->get_next_backtrack();
141                         node_stack->pop_restofstack(1);
142                 }
143                 DEBUG("*** Divergence point ***\n");
144                 diverge = NULL;
145         } else {
146                 tid = next->get_tid();
147         }
148         DEBUG("*** ModelChecker chose next thread = %d ***\n", tid);
149         return tid;
150 }
151
152 /**
153  * Queries the model-checker for more executions to explore and, if one
154  * exists, resets the model-checker state to execute a new execution.
155  *
156  * @return If there are more executions to explore, return true. Otherwise,
157  * return false.
158  */
159 bool ModelChecker::next_execution()
160 {
161         DBG();
162
163         num_executions++;
164
165         if (isfeasible() || DBG_ENABLED())
166                 print_summary();
167
168         if ((diverge = model->get_next_backtrack()) == NULL)
169                 return false;
170
171         if (DBG_ENABLED()) {
172                 printf("Next execution will diverge at:\n");
173                 diverge->print();
174         }
175
176         model->reset_to_initial_state();
177         return true;
178 }
179
180 ModelAction * ModelChecker::get_last_conflict(ModelAction *act)
181 {
182         action_type type = act->get_type();
183
184         switch (type) {
185                 case ATOMIC_READ:
186                 case ATOMIC_WRITE:
187                 case ATOMIC_RMW:
188                         break;
189                 default:
190                         return NULL;
191         }
192         /* linear search: from most recent to oldest */
193         action_list_t *list = obj_map->ensureptr(act->get_location());
194         action_list_t::reverse_iterator rit;
195         for (rit = list->rbegin(); rit != list->rend(); rit++) {
196                 ModelAction *prev = *rit;
197                 if (act->is_synchronizing(prev))
198                         return prev;
199         }
200         return NULL;
201 }
202
203 void ModelChecker::set_backtracking(ModelAction *act)
204 {
205         ModelAction *prev;
206         Node *node;
207         Thread *t = get_thread(act->get_tid());
208
209         prev = get_last_conflict(act);
210         if (prev == NULL)
211                 return;
212
213         node = prev->get_node()->get_parent();
214
215         while (!node->is_enabled(t))
216                 t = t->get_parent();
217
218         /* Check if this has been explored already */
219         if (node->has_been_explored(t->get_id()))
220                 return;
221
222         /* Cache the latest backtracking point */
223         if (!next_backtrack || *prev > *next_backtrack)
224                 next_backtrack = prev;
225
226         /* If this is a new backtracking point, mark the tree */
227         if (!node->set_backtrack(t->get_id()))
228                 return;
229         DEBUG("Setting backtrack: conflict = %d, instead tid = %d\n",
230                         prev->get_tid(), t->get_id());
231         if (DBG_ENABLED()) {
232                 prev->print();
233                 act->print();
234         }
235 }
236
237 /**
238  * Returns last backtracking point. The model checker will explore a different
239  * path for this point in the next execution.
240  * @return The ModelAction at which the next execution should diverge.
241  */
242 ModelAction * ModelChecker::get_next_backtrack()
243 {
244         ModelAction *next = next_backtrack;
245         next_backtrack = NULL;
246         return next;
247 }
248
249 void ModelChecker::check_current_action(void)
250 {
251         ModelAction *curr = this->current_action;
252         bool already_added = false;
253         this->current_action = NULL;
254         if (!curr) {
255                 DEBUG("trying to push NULL action...\n");
256                 return;
257         }
258
259         if (curr->is_rmwc()||curr->is_rmw()) {
260                 ModelAction *tmp=process_rmw(curr);
261                 already_added = true;
262                 delete curr;
263                 curr=tmp;
264         } else {
265                 ModelAction * tmp = node_stack->explore_action(curr);
266                 if (tmp) {
267                         /* Discard duplicate ModelAction; use action from NodeStack */
268                         /* First restore type and order in case of RMW operation */
269                         if (curr->is_rmwr())
270                                 tmp->copy_typeandorder(curr);
271                         delete curr;
272                         curr = tmp;
273                 } else {
274                         /*
275                          * Perform one-time actions when pushing new ModelAction onto
276                          * NodeStack
277                          */
278                         curr->create_cv(get_parent_action(curr->get_tid()));
279                         /* Build may_read_from set */
280                         if (curr->is_read())
281                                 build_reads_from_past(curr);
282                 }
283         }
284
285         /* Assign 'creation' parent */
286         if (curr->get_type() == THREAD_CREATE) {
287                 Thread *th = (Thread *)curr->get_location();
288                 th->set_creation(curr);
289         }
290
291         /* Assign reads_from values */
292         Thread *th = get_thread(curr->get_tid());
293         uint64_t value = VALUE_NONE;
294         if (curr->is_read()) {
295                 const ModelAction *reads_from = curr->get_node()->get_read_from();
296                 value = reads_from->get_value();
297                 /* Assign reads_from, perform release/acquire synchronization */
298                 curr->read_from(reads_from);
299                 r_modification_order(curr,reads_from);
300         } else if (curr->is_write()) {
301                 w_modification_order(curr);
302         }
303
304         th->set_return_value(value);
305
306         /* Add action to list.  */
307         if (!already_added)
308                 add_action_to_lists(curr);
309
310         /* Is there a better interface for setting the next thread rather
311                  than this field/convoluted approach?  Perhaps like just returning
312                  it or something? */
313
314         /* Do not split atomic actions. */
315         if (curr->is_rmwr()) {
316                 nextThread = thread_current()->get_id();
317         } else {
318                 nextThread = get_next_replay_thread();
319         }
320
321         Node *currnode = curr->get_node();
322         Node *parnode = currnode->get_parent();
323
324         if (!parnode->backtrack_empty()||!currnode->readsfrom_empty())
325                 if (!next_backtrack || *curr > *next_backtrack)
326                         next_backtrack = curr;
327
328         set_backtracking(curr);
329 }
330
331 /** @returns whether the current trace is feasible. */
332 bool ModelChecker::isfeasible() {
333         return !cyclegraph->checkForCycles();
334 }
335
336 /** Close out a RMWR by converting previous RMWR into a RMW or READ. */
337 ModelAction * ModelChecker::process_rmw(ModelAction * act) {
338         int tid = id_to_int(act->get_tid());
339         ModelAction *lastread=get_last_action(tid);
340         lastread->process_rmw(act);
341         if (act->is_rmw())
342                 cyclegraph->addRMWEdge(lastread, lastread->get_reads_from());
343         return lastread;
344 }
345
346 /**
347  * Updates the cyclegraph with the constraints imposed from the current read.
348  * @param curr The current action. Must be a read.
349  * @param rf The action that curr reads from. Must be a write.
350  */
351 void ModelChecker::r_modification_order(ModelAction * curr, const ModelAction *rf) {
352         std::vector<action_list_t> *thrd_lists = obj_thrd_map->ensureptr(curr->get_location());
353         unsigned int i;
354         ASSERT(curr->is_read());
355
356         /* Iterate over all threads */
357         for (i = 0; i < thrd_lists->size(); i++) {
358                 /* Iterate over actions in thread, starting from most recent */
359                 action_list_t *list = &(*thrd_lists)[i];
360                 action_list_t::reverse_iterator rit;
361                 for (rit = list->rbegin(); rit != list->rend(); rit++) {
362                         ModelAction *act = *rit;
363
364                         /* Include at most one act per-thread that "happens before" curr */
365                         if (act->happens_before(curr)) {
366                                 if (act->is_read()) {
367                                         const ModelAction * prevreadfrom=act->get_reads_from();
368                                         if (rf!=prevreadfrom)
369                                                 cyclegraph->addEdge(rf, prevreadfrom);
370                                 } else if (rf!=act) {
371                                         cyclegraph->addEdge(rf, act);
372                                 }
373                                 break;
374                         }
375                 }
376         }
377 }
378
379 /**
380  * Updates the cyclegraph with the constraints imposed from the current write.
381  * @param curr The current action. Must be a write.
382  */
383 void ModelChecker::w_modification_order(ModelAction * curr) {
384         std::vector<action_list_t> *thrd_lists = obj_thrd_map->ensureptr(curr->get_location());
385         unsigned int i;
386         ASSERT(curr->is_write());
387
388         if (curr->is_seqcst()) {
389                 /* We have to at least see the last sequentially consistent write,
390                          so we are initialized. */
391                 ModelAction * last_seq_cst=get_last_seq_cst(curr->get_location());
392                 if (last_seq_cst!=NULL)
393                         cyclegraph->addEdge(curr, last_seq_cst);
394         }
395
396         /* Iterate over all threads */
397         for (i = 0; i < thrd_lists->size(); i++) {
398                 /* Iterate over actions in thread, starting from most recent */
399                 action_list_t *list = &(*thrd_lists)[i];
400                 action_list_t::reverse_iterator rit;
401                 for (rit = list->rbegin(); rit != list->rend(); rit++) {
402                         ModelAction *act = *rit;
403
404                         /* Include at most one act per-thread that "happens before" curr */
405                         if (act->happens_before(curr)) {
406                                 if (act->is_read()) {
407                                         cyclegraph->addEdge(curr, act->get_reads_from());
408                                 } else
409                                         cyclegraph->addEdge(curr, act);
410                                 break;
411                         }
412                 }
413         }
414 }
415
416 /**
417  * Performs various bookkeeping operations for the current ModelAction. For
418  * instance, adds action to the per-object, per-thread action vector and to the
419  * action trace list of all thread actions.
420  *
421  * @param act is the ModelAction to add.
422  */
423 void ModelChecker::add_action_to_lists(ModelAction *act)
424 {
425         int tid = id_to_int(act->get_tid());
426         action_trace->push_back(act);
427
428         obj_map->ensureptr(act->get_location())->push_back(act);
429
430         std::vector<action_list_t> *vec = obj_thrd_map->ensureptr(act->get_location());
431         if (tid >= (int)vec->size())
432                 vec->resize(next_thread_id);
433         (*vec)[tid].push_back(act);
434
435         if ((int)thrd_last_action->size() <= tid)
436                 thrd_last_action->resize(get_num_threads());
437         (*thrd_last_action)[tid] = act;
438 }
439
440 ModelAction * ModelChecker::get_last_action(thread_id_t tid)
441 {
442         int nthreads = get_num_threads();
443         if ((int)thrd_last_action->size() < nthreads)
444                 thrd_last_action->resize(nthreads);
445         return (*thrd_last_action)[id_to_int(tid)];
446 }
447
448 /**
449  * Gets the last memory_order_seq_cst action (in the total global sequence)
450  * performed on a particular object (i.e., memory location).
451  * @param location The object location to check
452  * @return The last seq_cst action performed
453  */
454 ModelAction * ModelChecker::get_last_seq_cst(const void *location)
455 {
456         action_list_t *list = obj_map->ensureptr(location);
457         /* Find: max({i in dom(S) | seq_cst(t_i) && isWrite(t_i) && samevar(t_i, t)}) */
458         action_list_t::reverse_iterator rit;
459         for (rit = list->rbegin(); rit != list->rend(); rit++)
460                 if ((*rit)->is_write() && (*rit)->is_seqcst())
461                         return *rit;
462         return NULL;
463 }
464
465 ModelAction * ModelChecker::get_parent_action(thread_id_t tid)
466 {
467         ModelAction *parent = get_last_action(tid);
468         if (!parent)
469                 parent = get_thread(tid)->get_creation();
470         return parent;
471 }
472
473 /**
474  * Returns the clock vector for a given thread.
475  * @param tid The thread whose clock vector we want
476  * @return Desired clock vector
477  */
478 ClockVector * ModelChecker::get_cv(thread_id_t tid) {
479         return get_parent_action(tid)->get_cv();
480 }
481
482 /**
483  * Build up an initial set of all past writes that this 'read' action may read
484  * from. This set is determined by the clock vector's "happens before"
485  * relationship.
486  * @param curr is the current ModelAction that we are exploring; it must be a
487  * 'read' operation.
488  */
489 void ModelChecker::build_reads_from_past(ModelAction *curr)
490 {
491         std::vector<action_list_t> *thrd_lists = obj_thrd_map->ensureptr(curr->get_location());
492         unsigned int i;
493         ASSERT(curr->is_read());
494
495         ModelAction *last_seq_cst = NULL;
496
497         /* Track whether this object has been initialized */
498         bool initialized = false;
499
500         if (curr->is_seqcst()) {
501                 last_seq_cst = get_last_seq_cst(curr->get_location());
502                 /* We have to at least see the last sequentially consistent write,
503                          so we are initialized. */
504                 if (last_seq_cst != NULL)
505                         initialized = true;
506         }
507
508         /* Iterate over all threads */
509         for (i = 0; i < thrd_lists->size(); i++) {
510                 /* Iterate over actions in thread, starting from most recent */
511                 action_list_t *list = &(*thrd_lists)[i];
512                 action_list_t::reverse_iterator rit;
513                 for (rit = list->rbegin(); rit != list->rend(); rit++) {
514                         ModelAction *act = *rit;
515
516                         /* Only consider 'write' actions */
517                         if (!act->is_write())
518                                 continue;
519
520                         /* Don't consider more than one seq_cst write if we are a seq_cst read. */
521                         if (!act->is_seqcst() || !curr->is_seqcst() || act == last_seq_cst) {
522                                 DEBUG("Adding action to may_read_from:\n");
523                                 if (DBG_ENABLED()) {
524                                         act->print();
525                                         curr->print();
526                                 }
527                                 curr->get_node()->add_read_from(act);
528                         }
529
530                         /* Include at most one act per-thread that "happens before" curr */
531                         if (act->happens_before(curr)) {
532                                 initialized = true;
533                                 break;
534                         }
535                 }
536         }
537
538         if (!initialized) {
539                 /** @todo Need a more informative way of reporting errors. */
540                 printf("ERROR: may read from uninitialized atomic\n");
541         }
542
543         if (DBG_ENABLED() || !initialized) {
544                 printf("Reached read action:\n");
545                 curr->print();
546                 printf("Printing may_read_from\n");
547                 curr->get_node()->print_may_read_from();
548                 printf("End printing may_read_from\n");
549         }
550
551         ASSERT(initialized);
552 }
553
554 static void print_list(action_list_t *list)
555 {
556         action_list_t::iterator it;
557
558         printf("---------------------------------------------------------------------\n");
559         printf("Trace:\n");
560
561         for (it = list->begin(); it != list->end(); it++) {
562                 (*it)->print();
563         }
564         printf("---------------------------------------------------------------------\n");
565 }
566
567 void ModelChecker::print_summary(void)
568 {
569         printf("\n");
570         printf("Number of executions: %d\n", num_executions);
571         printf("Total nodes created: %d\n", node_stack->get_total_nodes());
572
573         scheduler->print();
574
575         if (!isfeasible())
576                 printf("INFEASIBLE EXECUTION!\n");
577         print_list(action_trace);
578         printf("\n");
579 }
580
581 int ModelChecker::add_thread(Thread *t)
582 {
583         thread_map->put(id_to_int(t->get_id()), t);
584         scheduler->add_thread(t);
585         return 0;
586 }
587
588 void ModelChecker::remove_thread(Thread *t)
589 {
590         scheduler->remove_thread(t);
591 }
592
593 /**
594  * Switch from a user-context to the "master thread" context (a.k.a. system
595  * context). This switch is made with the intention of exploring a particular
596  * model-checking action (described by a ModelAction object). Must be called
597  * from a user-thread context.
598  * @param act The current action that will be explored. May be NULL, although
599  * there is little reason to switch to the model-checker without an action to
600  * explore (note: act == NULL is sometimes used as a hack to allow a thread to
601  * yield control without performing any progress; see thrd_join()).
602  * @return Return status from the 'swap' call (i.e., success/fail, 0/-1)
603  */
604 int ModelChecker::switch_to_master(ModelAction *act)
605 {
606         DBG();
607         Thread * old = thread_current();
608         set_current_action(act);
609         old->set_state(THREAD_READY);
610         return Thread::swap(old, get_system_context());
611 }