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