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