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