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