model: don't clear thread_map (snapshot)
[model-checker.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 ModelChecker::ModelChecker()
15         :
16         /* Initialize default scheduler */
17         scheduler(new Scheduler()),
18         /* First thread created will have id INITIAL_THREAD_ID */
19         next_thread_id(INITIAL_THREAD_ID),
20         used_sequence_numbers(0),
21
22         num_executions(0),
23         current_action(NULL),
24         diverge(NULL),
25         nextThread(THREAD_ID_T_NONE),
26         action_trace(new action_list_t()),
27         thread_map(new std::map<int, class Thread *>),
28         node_stack(new NodeStack()),
29         next_backtrack(NULL)
30 {
31 }
32
33 ModelChecker::~ModelChecker()
34 {
35         std::map<int, class Thread *>::iterator it;
36         for (it = thread_map->begin(); it != thread_map->end(); it++)
37                 delete (*it).second;
38         delete thread_map;
39
40         delete action_trace;
41
42         delete node_stack;
43         delete scheduler;
44 }
45
46 void ModelChecker::reset_to_initial_state()
47 {
48         DEBUG("+++ Resetting to initial state +++\n");
49         node_stack->reset_execution();
50         current_action = NULL;
51         next_thread_id = INITIAL_THREAD_ID;
52         used_sequence_numbers = 0;
53         nextThread = 0;
54         next_backtrack = NULL;
55         /* scheduler reset ? */
56         snapshotObject->backTrackBeforeStep(0);
57 }
58
59 thread_id_t ModelChecker::get_next_id()
60 {
61         return next_thread_id++;
62 }
63
64 int ModelChecker::get_next_seq_num()
65 {
66         return ++used_sequence_numbers;
67 }
68
69 Thread * ModelChecker::schedule_next_thread()
70 {
71         Thread *t;
72         if (nextThread == THREAD_ID_T_NONE)
73                 return NULL;
74         t = (*thread_map)[id_to_int(nextThread)];
75
76         ASSERT(t != NULL);
77
78         return t;
79 }
80
81 /*
82  * get_next_replay_thread() - Choose the next thread in the replay sequence
83  *
84  * If we've reached the 'diverge' point, then we pick a thread from the
85  *   backtracking set.
86  * Otherwise, we simply return the next thread in the sequence.
87  */
88 thread_id_t ModelChecker::get_next_replay_thread()
89 {
90         ModelAction *next;
91         thread_id_t tid;
92
93         /* Have we completed exploring the preselected path? */
94         if (diverge == NULL)
95                 return THREAD_ID_T_NONE;
96
97         /* Else, we are trying to replay an execution */
98         next = node_stack->get_next()->get_action();
99
100         if (next == diverge) {
101                 Node *node = next->get_node();
102
103                 /* Reached divergence point */
104                 DEBUG("*** Divergence point ***\n");
105                 tid = node->get_next_backtrack();
106                 diverge = NULL;
107         } else {
108                 tid = next->get_tid();
109         }
110         DEBUG("*** ModelChecker chose next thread = %d ***\n", tid);
111         return tid;
112 }
113
114 bool ModelChecker::next_execution()
115 {
116         DBG();
117
118         num_executions++;
119         print_summary();
120         if ((diverge = model->get_next_backtrack()) == NULL)
121                 return false;
122
123         if (DBG_ENABLED()) {
124                 printf("Next execution will diverge at:\n");
125                 diverge->print();
126         }
127
128         model->reset_to_initial_state();
129         return true;
130 }
131
132 ModelAction * ModelChecker::get_last_conflict(ModelAction *act)
133 {
134         action_type type = act->get_type();
135
136         switch (type) {
137                 case THREAD_CREATE:
138                 case THREAD_YIELD:
139                 case THREAD_JOIN:
140                         return NULL;
141                 case ATOMIC_READ:
142                 case ATOMIC_WRITE:
143                 default:
144                         break;
145         }
146         /* linear search: from most recent to oldest */
147         action_list_t::reverse_iterator rit;
148         for (rit = action_trace->rbegin(); rit != action_trace->rend(); rit++) {
149                 ModelAction *prev = *rit;
150                 if (act->is_dependent(prev))
151                         return prev;
152         }
153         return NULL;
154 }
155
156 void ModelChecker::set_backtracking(ModelAction *act)
157 {
158         ModelAction *prev;
159         Node *node;
160         Thread *t = get_thread(act->get_tid());
161
162         prev = get_last_conflict(act);
163         if (prev == NULL)
164                 return;
165
166         node = prev->get_node();
167
168         while (!node->is_enabled(t))
169                 t = t->get_parent();
170
171         /* Check if this has been explored already */
172         if (node->has_been_explored(t->get_id()))
173                 return;
174
175         if (!next_backtrack || *prev > *next_backtrack)
176                 next_backtrack = prev;
177
178         /* If this is a new backtracking point, mark the tree */
179         if (!node->set_backtrack(t->get_id()))
180                 return;
181         DEBUG("Setting backtrack: conflict = %d, instead tid = %d\n",
182                         prev->get_tid(), t->get_id());
183         if (DBG_ENABLED()) {
184                 prev->print();
185                 act->print();
186         }
187 }
188
189 ModelAction * ModelChecker::get_next_backtrack()
190 {
191         ModelAction *next = next_backtrack;
192         next_backtrack = NULL;
193         return next;
194 }
195
196 void ModelChecker::check_current_action(void)
197 {
198         Node *currnode;
199
200         ModelAction *curr = this->current_action;
201         current_action = NULL;
202         if (!curr) {
203                 DEBUG("trying to push NULL action...\n");
204                 return;
205         }
206
207         curr = node_stack->explore_action(curr);
208         nextThread = get_next_replay_thread();
209
210         currnode = curr->get_node();
211
212         if (!currnode->backtrack_empty())
213                 if (!next_backtrack || *curr > *next_backtrack)
214                         next_backtrack = curr;
215
216         set_backtracking(curr);
217         this->action_trace->push_back(curr);
218 }
219
220 void ModelChecker::print_summary(void)
221 {
222         printf("\n");
223         printf("Number of executions: %d\n", num_executions);
224         printf("Total nodes created: %d\n", Node::get_total_nodes());
225
226         scheduler->print();
227
228         print_list(action_trace);
229         printf("\n");
230 }
231
232 void ModelChecker::print_list(action_list_t *list)
233 {
234         action_list_t::iterator it;
235
236         printf("---------------------------------------------------------------------\n");
237         printf("Trace:\n");
238
239         for (it = list->begin(); it != list->end(); it++) {
240                 (*it)->print();
241         }
242         printf("---------------------------------------------------------------------\n");
243 }
244
245 int ModelChecker::add_thread(Thread *t)
246 {
247         (*thread_map)[id_to_int(t->get_id())] = t;
248         scheduler->add_thread(t);
249         return 0;
250 }
251
252 void ModelChecker::remove_thread(Thread *t)
253 {
254         scheduler->remove_thread(t);
255 }
256
257 int ModelChecker::switch_to_master(ModelAction *act)
258 {
259         Thread *old;
260
261         DBG();
262         old = thread_current();
263         set_current_action(act);
264         old->set_state(THREAD_READY);
265         return Thread::swap(old, get_system_context());
266 }