76789684df9e641fd4417ff5eccf0116b33f2133
[model-checker.git] / model.cc
1 #include <stdio.h>
2
3 #include "model.h"
4 #include "action.h"
5 #include "tree.h"
6 #include "schedule.h"
7 #include "common.h"
8
9 #define INITIAL_THREAD_ID       0
10
11 class Backtrack {
12 public:
13         Backtrack(ModelAction *d, action_list_t *t) {
14                 diverge = d;
15                 actionTrace = t;
16                 iter = actionTrace->begin();
17         }
18         ModelAction * get_diverge() { return diverge; }
19         action_list_t * get_trace() { return actionTrace; }
20         void advance_state() { iter++; }
21         ModelAction * get_state() {
22                 return iter == actionTrace->end() ? NULL : *iter;
23         }
24 private:
25         ModelAction *diverge;
26         action_list_t *actionTrace;
27         /* points to position in actionTrace as we replay */
28         action_list_t::iterator iter;
29 };
30
31 ModelChecker *model;
32
33 ModelChecker::ModelChecker()
34 {
35         /* First thread created will have id INITIAL_THREAD_ID */
36         this->next_thread_id = INITIAL_THREAD_ID;
37         used_sequence_numbers = 0;
38         /* Initialize default scheduler */
39         this->scheduler = new Scheduler();
40
41         num_executions = 0;
42         this->current_action = NULL;
43         this->exploring = NULL;
44         this->nextThread = THREAD_ID_T_NONE;
45
46         rootNode = new TreeNode();
47         currentNode = rootNode;
48         action_trace = new action_list_t();
49 }
50
51 ModelChecker::~ModelChecker()
52 {
53         delete action_trace;
54         delete this->scheduler;
55         delete rootNode;
56 }
57
58 void ModelChecker::reset_to_initial_state()
59 {
60         DEBUG("+++ Resetting to initial state +++\n");
61         std::map<int, class Thread *>::iterator it;
62         for (it = thread_map.begin(); it != thread_map.end(); it++)
63                 delete (*it).second;
64         thread_map.clear();
65         action_trace = new action_list_t();
66         currentNode = rootNode;
67         current_action = NULL;
68         next_thread_id = INITIAL_THREAD_ID;
69         used_sequence_numbers = 0;
70         /* scheduler reset ? */
71 }
72
73 thread_id_t ModelChecker::get_next_id()
74 {
75         return next_thread_id++;
76 }
77
78 int ModelChecker::get_next_seq_num()
79 {
80         return ++used_sequence_numbers;
81 }
82
83 Thread * ModelChecker::schedule_next_thread()
84 {
85         Thread *t;
86         if (nextThread == THREAD_ID_T_NONE)
87                 return NULL;
88         t = thread_map[id_to_int(nextThread)];
89
90         ASSERT(t != NULL);
91
92         return t;
93 }
94
95 /*
96  * get_next_replay_thread() - Choose the next thread in the replay sequence
97  *
98  * If we've reached the 'diverge' point, then we pick a thread from the
99  *   backtracking set.
100  * Otherwise, we simply return the next thread in the sequence.
101  */
102 thread_id_t ModelChecker::get_next_replay_thread()
103 {
104         ModelAction *next;
105         thread_id_t tid;
106
107         next = exploring->get_state();
108
109         if (next == exploring->get_diverge()) {
110                 TreeNode *node = next->get_node();
111
112                 /* Reached divergence point; discard our current 'exploring' */
113                 DEBUG("*** Discard 'Backtrack' object ***\n");
114                 tid = node->getNextBacktrack();
115                 delete exploring;
116                 exploring = NULL;
117         } else {
118                 tid = next->get_tid();
119         }
120         DEBUG("*** ModelChecker chose next thread = %d ***\n", tid);
121         return tid;
122 }
123
124 thread_id_t ModelChecker::advance_backtracking_state()
125 {
126         /* Have we completed exploring the preselected path? */
127         if (exploring == NULL)
128                 return THREAD_ID_T_NONE;
129
130         /* Else, we are trying to replay an execution */
131         exploring->advance_state();
132
133         ASSERT(exploring->get_state() != NULL);
134
135         return get_next_replay_thread();
136 }
137
138 bool ModelChecker::next_execution()
139 {
140         DBG();
141
142         num_executions++;
143         print_summary();
144         if ((exploring = model->get_next_backtrack()) == NULL)
145                 return false;
146
147         if (DBG_ENABLED()) {
148                 printf("Next execution will diverge at:\n");
149                 exploring->get_diverge()->print();
150                 print_list(exploring->get_trace());
151         }
152
153         model->reset_to_initial_state();
154         nextThread = get_next_replay_thread();
155         return true;
156 }
157
158 ModelAction * ModelChecker::get_last_conflict(ModelAction *act)
159 {
160         action_type type = act->get_type();
161
162         switch (type) {
163                 case THREAD_CREATE:
164                 case THREAD_YIELD:
165                 case THREAD_JOIN:
166                         return NULL;
167                 case ATOMIC_READ:
168                 case ATOMIC_WRITE:
169                 default:
170                         break;
171         }
172         /* linear search: from most recent to oldest */
173         action_list_t::reverse_iterator rit;
174         for (rit = action_trace->rbegin(); rit != action_trace->rend(); rit++) {
175                 ModelAction *prev = *rit;
176                 if (act->is_dependent(prev))
177                         return prev;
178         }
179         return NULL;
180 }
181
182 void ModelChecker::set_backtracking(ModelAction *act)
183 {
184         ModelAction *prev;
185         TreeNode *node;
186         Thread *t = get_thread(act->get_tid());
187
188         prev = get_last_conflict(act);
189         if (prev == NULL)
190                 return;
191
192         node = prev->get_node();
193
194         while (t && !node->is_enabled(t))
195                 t = t->get_parent();
196
197         /* Check if this has been explored already */
198         if (node->hasBeenExplored(t->get_id()))
199                 return;
200         /* If this is a new backtracking point, mark the tree */
201         if (node->setBacktrack(t->get_id()) != 0)
202                 return;
203
204         DEBUG("Setting backtrack: conflict = %d, instead tid = %d\n",
205                         prev->get_tid(), t->get_id());
206         if (DBG_ENABLED()) {
207                 prev->print();
208                 act->print();
209         }
210
211         Backtrack *back = new Backtrack(prev, action_trace);
212         backtrack_list.push_back(back);
213 }
214
215 Backtrack * ModelChecker::get_next_backtrack()
216 {
217         Backtrack *next;
218         if (backtrack_list.empty())
219                 return NULL;
220         next = backtrack_list.back();
221         backtrack_list.pop_back();
222         return next;
223 }
224
225 void ModelChecker::check_current_action(void)
226 {
227         ModelAction *next = this->current_action;
228
229         if (!next) {
230                 DEBUG("trying to push NULL action...\n");
231                 return;
232         }
233         current_action = NULL;
234         nextThread = advance_backtracking_state();
235         next->set_node(currentNode);
236         set_backtracking(next);
237         currentNode = currentNode->explore_child(next);
238         this->action_trace->push_back(next);
239 }
240
241 void ModelChecker::print_summary(void)
242 {
243         printf("\n");
244         printf("Number of executions: %d\n", num_executions);
245         printf("Total nodes created: %d\n", TreeNode::getTotalNodes());
246
247         scheduler->print();
248
249         print_list(action_trace);
250         printf("\n");
251
252 }
253
254 void ModelChecker::print_list(action_list_t *list)
255 {
256         action_list_t::iterator it;
257
258         printf("---------------------------------------------------------------------\n");
259         printf("Trace:\n");
260
261         for (it = list->begin(); it != list->end(); it++) {
262                 (*it)->print();
263         }
264         printf("---------------------------------------------------------------------\n");
265 }
266
267 int ModelChecker::add_thread(Thread *t)
268 {
269         thread_map[id_to_int(t->get_id())] = t;
270         scheduler->add_thread(t);
271         return 0;
272 }
273
274 void ModelChecker::remove_thread(Thread *t)
275 {
276         scheduler->remove_thread(t);
277 }
278
279 int ModelChecker::switch_to_master(ModelAction *act)
280 {
281         Thread *old;
282
283         DBG();
284         old = thread_current();
285         set_current_action(act);
286         old->set_state(THREAD_READY);
287         return Thread::swap(old, get_system_context());
288 }
289
290 ModelAction::ModelAction(action_type_t type, memory_order order, void *loc, int value)
291 {
292         Thread *t = thread_current();
293         ModelAction *act = this;
294
295         act->type = type;
296         act->order = order;
297         act->location = loc;
298         act->tid = t->get_id();
299         act->value = value;
300         act->seq_number = model->get_next_seq_num();
301 }
302
303 bool ModelAction::is_read()
304 {
305         return type == ATOMIC_READ;
306 }
307
308 bool ModelAction::is_write()
309 {
310         return type == ATOMIC_WRITE;
311 }
312
313 bool ModelAction::is_acquire()
314 {
315         switch (order) {
316         case memory_order_acquire:
317         case memory_order_acq_rel:
318         case memory_order_seq_cst:
319                 return true;
320         default:
321                 return false;
322         }
323 }
324
325 bool ModelAction::is_release()
326 {
327         switch (order) {
328         case memory_order_release:
329         case memory_order_acq_rel:
330         case memory_order_seq_cst:
331                 return true;
332         default:
333                 return false;
334         }
335 }
336
337 bool ModelAction::same_var(ModelAction *act)
338 {
339         return location == act->location;
340 }
341
342 bool ModelAction::same_thread(ModelAction *act)
343 {
344         return tid == act->tid;
345 }
346
347 bool ModelAction::is_dependent(ModelAction *act)
348 {
349         if (!is_read() && !is_write())
350                 return false;
351         if (!act->is_read() && !act->is_write())
352                 return false;
353         if (same_var(act) && !same_thread(act) &&
354                         (is_write() || act->is_write()))
355                 return true;
356         return false;
357 }
358
359 void ModelAction::print(void)
360 {
361         const char *type_str;
362         switch (this->type) {
363         case THREAD_CREATE:
364                 type_str = "thread create";
365                 break;
366         case THREAD_YIELD:
367                 type_str = "thread yield";
368                 break;
369         case THREAD_JOIN:
370                 type_str = "thread join";
371                 break;
372         case ATOMIC_READ:
373                 type_str = "atomic read";
374                 break;
375         case ATOMIC_WRITE:
376                 type_str = "atomic write";
377                 break;
378         default:
379                 type_str = "unknown type";
380         }
381
382         printf("(%4d) Thread: %d\tAction: %s\tMO: %d\tLoc: %14p\tValue: %d\n",
383                         seq_number, id_to_int(tid), type_str, order, location, value);
384 }