builds
[c11tester.git] / model.cc
1 #include <stdio.h>
2
3 #include "model.h"
4 #include "action.h"
5 #include "nodestack.h"
6 #include "schedule.h"
7 #include "snapshot-interface.h"
8 #include "common.h"
9
10 #define INITIAL_THREAD_ID       0
11
12 ModelChecker *model;
13
14 /** @brief Constructor */
15 ModelChecker::ModelChecker()
16         :
17         /* Initialize default scheduler */
18         scheduler(new Scheduler()),
19         /* First thread created will have id INITIAL_THREAD_ID */
20         next_thread_id(INITIAL_THREAD_ID),
21         used_sequence_numbers(0),
22
23         num_executions(0),
24         current_action(NULL),
25         diverge(NULL),
26         nextThread(THREAD_ID_T_NONE),
27         action_trace(new action_list_t()),
28         thread_map(new std::map<int, class Thread *>),
29         obj_thrd_map(new std::map<void *, std::vector<action_list_t> >()),
30         thrd_last_action(new std::vector<ModelAction *>(1)),
31         node_stack(new NodeStack()),
32         next_backtrack(NULL)
33 {
34 }
35
36 /** @brief Destructor */
37 ModelChecker::~ModelChecker()
38 {
39         std::map<int, class Thread *>::iterator it;
40         for (it = thread_map->begin(); it != thread_map->end(); it++)
41                 delete (*it).second;
42         delete thread_map;
43
44         delete obj_thrd_map;
45         delete action_trace;
46         delete thrd_last_action;
47         delete node_stack;
48         delete scheduler;
49 }
50
51 /**
52  * Restores user program to initial state and resets all model-checker data
53  * structures.
54  */
55 void ModelChecker::reset_to_initial_state()
56 {
57         DEBUG("+++ Resetting to initial state +++\n");
58         node_stack->reset_execution();
59         current_action = NULL;
60         next_thread_id = INITIAL_THREAD_ID;
61         used_sequence_numbers = 0;
62         nextThread = 0;
63         next_backtrack = NULL;
64         snapshotObject->backTrackBeforeStep(0);
65 }
66
67 /** @returns a thread ID for a new Thread */
68 thread_id_t ModelChecker::get_next_id()
69 {
70         return next_thread_id++;
71 }
72
73 /** @returns the number of user threads created during this execution */
74 int ModelChecker::get_num_threads()
75 {
76         return next_thread_id;
77 }
78
79 /** @returns a sequence number for a new ModelAction */
80 int ModelChecker::get_next_seq_num()
81 {
82         return ++used_sequence_numbers;
83 }
84
85 /**
86  * Performs the "scheduling" for the model-checker. That is, it checks if the
87  * model-checker has selected a "next thread to run" and returns it, if
88  * available. This function should be called from the Scheduler routine, where
89  * the Scheduler falls back to a default scheduling routine if needed.
90  *
91  * @return The next thread chosen by the model-checker. If the model-checker
92  * makes no selection, retuns NULL.
93  */
94 Thread * ModelChecker::schedule_next_thread()
95 {
96         Thread *t;
97         if (nextThread == THREAD_ID_T_NONE)
98                 return NULL;
99         t = (*thread_map)[id_to_int(nextThread)];
100
101         ASSERT(t != NULL);
102
103         return t;
104 }
105
106 /**
107  * Choose the next thread in the replay sequence.
108  *
109  * If the replay sequence has reached the 'diverge' point, returns a thread
110  * from the backtracking set. Otherwise, simply returns the next thread in the
111  * sequence that is being replayed.
112  */
113 thread_id_t ModelChecker::get_next_replay_thread()
114 {
115         ModelAction *next;
116         thread_id_t tid;
117
118         /* Have we completed exploring the preselected path? */
119         if (diverge == NULL)
120                 return THREAD_ID_T_NONE;
121
122         /* Else, we are trying to replay an execution */
123         next = node_stack->get_next()->get_action();
124
125         if (next == diverge) {
126                 Node *node = next->get_node();
127
128                 /* Reached divergence point */
129                 DEBUG("*** Divergence point ***\n");
130                 tid = node->get_next_backtrack();
131                 diverge = NULL;
132         } else {
133                 tid = next->get_tid();
134         }
135         DEBUG("*** ModelChecker chose next thread = %d ***\n", tid);
136         return tid;
137 }
138
139 /**
140  * Queries the model-checker for more executions to explore and, if one
141  * exists, resets the model-checker state to execute a new execution.
142  *
143  * @return If there are more executions to explore, return true. Otherwise,
144  * return false.
145  */
146 bool ModelChecker::next_execution()
147 {
148         DBG();
149
150         num_executions++;
151         print_summary();
152         if ((diverge = model->get_next_backtrack()) == NULL)
153                 return false;
154
155         if (DBG_ENABLED()) {
156                 printf("Next execution will diverge at:\n");
157                 diverge->print();
158         }
159
160         model->reset_to_initial_state();
161         return true;
162 }
163
164 ModelAction * ModelChecker::get_last_conflict(ModelAction *act)
165 {
166         action_type type = act->get_type();
167
168         switch (type) {
169                 case THREAD_CREATE:
170                 case THREAD_YIELD:
171                 case THREAD_JOIN:
172                         return NULL;
173                 case ATOMIC_READ:
174                 case ATOMIC_WRITE:
175                 default:
176                         break;
177         }
178         /* linear search: from most recent to oldest */
179         action_list_t::reverse_iterator rit;
180         for (rit = action_trace->rbegin(); rit != action_trace->rend(); rit++) {
181                 ModelAction *prev = *rit;
182                 if (act->is_synchronizing(prev))
183                         return prev;
184         }
185         return NULL;
186 }
187
188 void ModelChecker::set_backtracking(ModelAction *act)
189 {
190         ModelAction *prev;
191         Node *node;
192         Thread *t = get_thread(act->get_tid());
193
194         prev = get_last_conflict(act);
195         if (prev == NULL)
196                 return;
197
198         node = prev->get_node();
199
200         while (!node->is_enabled(t))
201                 t = t->get_parent();
202
203         /* Check if this has been explored already */
204         if (node->has_been_explored(t->get_id()))
205                 return;
206
207         if (!next_backtrack || *prev > *next_backtrack)
208                 next_backtrack = prev;
209
210         /* If this is a new backtracking point, mark the tree */
211         if (!node->set_backtrack(t->get_id()))
212                 return;
213         DEBUG("Setting backtrack: conflict = %d, instead tid = %d\n",
214                         prev->get_tid(), t->get_id());
215         if (DBG_ENABLED()) {
216                 prev->print();
217                 act->print();
218         }
219 }
220
221 ModelAction * ModelChecker::get_next_backtrack()
222 {
223         ModelAction *next = next_backtrack;
224         next_backtrack = NULL;
225         return next;
226 }
227
228 void ModelChecker::check_current_action(void)
229 {
230         Node *currnode;
231
232         ModelAction *curr = this->current_action;
233         current_action = NULL;
234         if (!curr) {
235                 DEBUG("trying to push NULL action...\n");
236                 return;
237         }
238
239         curr = node_stack->explore_action(curr);
240         curr->create_cv(get_parent_action(curr->get_tid()));
241
242         /* Assign 'creation' parent */
243         if (curr->get_type() == THREAD_CREATE) {
244                 Thread *th = (Thread *)curr->get_location();
245                 th->set_creation(curr);
246         }
247
248         nextThread = get_next_replay_thread();
249
250         currnode = curr->get_node();
251
252         if (!currnode->backtrack_empty())
253                 if (!next_backtrack || *curr > *next_backtrack)
254                         next_backtrack = curr;
255
256         set_backtracking(curr);
257
258         add_action_to_lists(curr);
259 }
260
261
262 /**
263  * Adds an action to the per-object, per-thread action vector.
264  * @param act is the ModelAction to add.
265  */
266
267 void ModelChecker::add_action_to_lists(ModelAction *act)
268 {
269         action_trace->push_back(act);
270
271         std::vector<action_list_t> *vec = &(*obj_thrd_map)[act->get_location()];
272         if (id_to_int(act->get_tid()) >= (int)vec->size())
273                 vec->resize(next_thread_id);
274         (*vec)[id_to_int(act->get_tid())].push_back(act);
275
276         (*thrd_last_action)[id_to_int(act->get_tid())] = act;
277 }
278
279 ModelAction * ModelChecker::get_last_action(thread_id_t tid)
280 {
281         int nthreads = get_num_threads();
282         if ((int)thrd_last_action->size() < nthreads)
283                 thrd_last_action->resize(nthreads);
284         return (*thrd_last_action)[id_to_int(tid)];
285 }
286
287 ModelAction * ModelChecker::get_parent_action(thread_id_t tid)
288 {
289         ModelAction *parent = get_last_action(tid);
290         if (!parent)
291                 parent = get_thread(tid)->get_creation();
292         return parent;
293 }
294
295 void ModelChecker::print_summary(void)
296 {
297         printf("\n");
298         printf("Number of executions: %d\n", num_executions);
299         printf("Total nodes created: %d\n", node_stack->get_total_nodes());
300
301         scheduler->print();
302
303         print_list(action_trace);
304         printf("\n");
305 }
306
307 void ModelChecker::print_list(action_list_t *list)
308 {
309         action_list_t::iterator it;
310
311         printf("---------------------------------------------------------------------\n");
312         printf("Trace:\n");
313
314         for (it = list->begin(); it != list->end(); it++) {
315                 (*it)->print();
316         }
317         printf("---------------------------------------------------------------------\n");
318 }
319
320 int ModelChecker::add_thread(Thread *t)
321 {
322         (*thread_map)[id_to_int(t->get_id())] = t;
323         scheduler->add_thread(t);
324         return 0;
325 }
326
327 void ModelChecker::remove_thread(Thread *t)
328 {
329         scheduler->remove_thread(t);
330 }
331
332 int ModelChecker::switch_to_master(ModelAction *act)
333 {
334         Thread *old;
335
336         DBG();
337         old = thread_current();
338         set_current_action(act);
339         old->set_state(THREAD_READY);
340         return Thread::swap(old, get_system_context());
341 }