Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
5f791a97469da88514eca221a3e5adc7e7bedf15
[simgrid.git] / src / mc / mc_dpor.c
1 /* Copyright (c) 2008-2013. Da SimGrid Team. All rights reserved.           */
2
3 /* This program is free software; you can redistribute it and/or modify it
4  * under the terms of the license (GNU LGPL) which comes with this package. */
5
6 #include "mc_private.h"
7
8 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_dpor, mc,
9                                 "Logging specific to MC DPOR exploration");
10
11 xbt_dynar_t visited_states;
12 xbt_dict_t first_enabled_state;
13
14 static void visited_state_free(mc_visited_state_t state){
15   if(state){
16     MC_free_snapshot(state->system_state);
17     xbt_free(state);
18   }
19 }
20
21 static void visited_state_free_voidp(void *s){
22   visited_state_free((mc_visited_state_t) * (void **) s);
23 }
24
25 static mc_visited_state_t visited_state_new(){
26
27   mc_visited_state_t new_state = NULL;
28   new_state = xbt_new0(s_mc_visited_state_t, 1);
29   new_state->heap_bytes_used = mmalloc_get_bytes_used(std_heap);
30   new_state->nb_processes = xbt_swag_size(simix_global->process_list);
31   new_state->system_state = MC_take_snapshot();
32   new_state->num = mc_stats->expanded_states - 1;
33
34   return new_state;
35   
36 }
37
38 /* Dichotomic search in visited_states dynar. 
39  * States are ordered by the number of processes then the number of bytes used in std_heap */
40
41 static int is_visited_state(){
42
43   if(_sg_mc_visited == 0)
44     return -1;
45
46   int raw_mem_set = (mmalloc_get_current_heap() == raw_heap);
47
48   MC_SET_RAW_MEM;
49   mc_visited_state_t new_state = visited_state_new();
50   MC_UNSET_RAW_MEM;
51   
52   if(xbt_dynar_is_empty(visited_states)){
53
54     MC_SET_RAW_MEM;
55     xbt_dynar_push(visited_states, &new_state); 
56     MC_UNSET_RAW_MEM;
57
58     if(raw_mem_set)
59       MC_SET_RAW_MEM;
60
61     return -1;
62
63   }else{
64
65     MC_SET_RAW_MEM;
66     
67     size_t current_bytes_used = new_state->heap_bytes_used;
68     int current_nb_processes = new_state->nb_processes;
69
70     unsigned int cursor = 0;
71     int previous_cursor = 0, next_cursor = 0;
72     int start = 0;
73     int end = xbt_dynar_length(visited_states) - 1;
74
75     mc_visited_state_t state_test = NULL;
76     size_t bytes_used_test;
77     int nb_processes_test;
78     int same_processes_and_bytes_not_found = 1;
79
80     //XBT_INFO("Is visited state (2) - bytes used in std_heap : %d, bytes used in raw_heap : %d", mmalloc_get_bytes_used(std_heap), mmalloc_get_bytes_used(raw_heap));
81
82     while(start <= end && same_processes_and_bytes_not_found){
83       cursor = (start + end) / 2;
84       state_test = (mc_visited_state_t)xbt_dynar_get_as(visited_states, cursor, mc_visited_state_t);
85       bytes_used_test = state_test->heap_bytes_used;
86       nb_processes_test = state_test->nb_processes;
87       if(nb_processes_test < current_nb_processes){
88         start = cursor + 1;
89       }else if(nb_processes_test > current_nb_processes){
90         end = cursor - 1;
91       }else if(nb_processes_test == current_nb_processes){
92         if(bytes_used_test < current_bytes_used)
93           start = cursor + 1;
94         if(bytes_used_test > current_bytes_used)
95           end = cursor - 1;
96         if(bytes_used_test == current_bytes_used){
97           same_processes_and_bytes_not_found = 0;
98           if(snapshot_compare(new_state->system_state, state_test->system_state) == 0){
99             xbt_dynar_remove_at(visited_states, cursor, NULL);
100             xbt_dynar_insert_at(visited_states, cursor, &new_state);
101             XBT_DEBUG("State %d already visited ! (equal to state %d)", new_state->num, state_test->num);
102             if(raw_mem_set)
103               MC_SET_RAW_MEM;
104             else
105               MC_UNSET_RAW_MEM;
106             return state_test->num;
107           }else{
108             /* Search another state with same number of bytes used in std_heap */
109             previous_cursor = cursor - 1;
110             while(previous_cursor >= 0){
111               state_test = (mc_visited_state_t)xbt_dynar_get_as(visited_states, previous_cursor, mc_visited_state_t);
112               bytes_used_test = state_test->system_state->heap_bytes_used;
113               if(bytes_used_test != current_bytes_used)
114                 break;
115               if(snapshot_compare(new_state->system_state, state_test->system_state) == 0){
116                 xbt_dynar_remove_at(visited_states, previous_cursor, NULL);
117                 xbt_dynar_insert_at(visited_states, previous_cursor, &new_state);
118                 XBT_DEBUG("State %d already visited ! (equal to state %d)", new_state->num, state_test->num);
119                 if(raw_mem_set)
120                   MC_SET_RAW_MEM;
121                 else
122                   MC_UNSET_RAW_MEM;
123                 return state_test->num;
124               }
125               previous_cursor--;
126             }
127             next_cursor = cursor + 1;
128             while(next_cursor < xbt_dynar_length(visited_states)){
129               state_test = (mc_visited_state_t)xbt_dynar_get_as(visited_states, next_cursor, mc_visited_state_t);
130               bytes_used_test = state_test->system_state->heap_bytes_used;
131               if(bytes_used_test != current_bytes_used)
132                 break;
133               if(snapshot_compare(new_state->system_state, state_test->system_state) == 0){
134                 xbt_dynar_remove_at(visited_states, next_cursor, NULL);
135                 xbt_dynar_insert_at(visited_states, next_cursor, &new_state);
136                 XBT_DEBUG("State %d already visited ! (equal to state %d)", new_state->num, state_test->num);
137                 if(raw_mem_set)
138                   MC_SET_RAW_MEM;
139                 else
140                   MC_UNSET_RAW_MEM;
141                 return state_test->num;
142               }
143               next_cursor++;
144             }
145           }   
146         }
147       }
148     }
149
150     state_test = (mc_visited_state_t)xbt_dynar_get_as(visited_states, cursor, mc_visited_state_t);
151     bytes_used_test = state_test->heap_bytes_used;
152
153     if(bytes_used_test < current_bytes_used)
154       xbt_dynar_insert_at(visited_states, cursor + 1, &new_state);
155     else
156       xbt_dynar_insert_at(visited_states, cursor, &new_state);
157
158     if(xbt_dynar_length(visited_states) > _sg_mc_visited){
159       int min = mc_stats->expanded_states;
160       unsigned int cursor2 = 0;
161       unsigned int index = 0;
162       xbt_dynar_foreach(visited_states, cursor2, state_test){
163         if(state_test->num < min){
164           index = cursor2;
165           min = state_test->num;
166         }
167       }
168       xbt_dynar_remove_at(visited_states, index, NULL);
169     }
170     
171     MC_UNSET_RAW_MEM;
172
173     if(raw_mem_set)
174       MC_SET_RAW_MEM;
175     
176     return -1;
177     
178   }
179 }
180
181 /**
182  *  \brief Initialize the DPOR exploration algorithm
183  */
184 void MC_dpor_init()
185 {
186   
187   int raw_mem_set = (mmalloc_get_current_heap() == raw_heap);
188
189   mc_state_t initial_state = NULL;
190   smx_process_t process;
191   
192   /* Create the initial state and push it into the exploration stack */
193   MC_SET_RAW_MEM;
194
195   initial_state = MC_state_new();
196   visited_states = xbt_dynar_new(sizeof(mc_visited_state_t), visited_state_free_voidp);
197
198   first_enabled_state = xbt_dict_new_homogeneous(&xbt_free_f);
199
200   MC_UNSET_RAW_MEM;
201
202   XBT_DEBUG("**************************************************");
203   XBT_DEBUG("Initial state");
204
205   /* Wait for requests (schedules processes) */
206   MC_wait_for_requests();
207
208   MC_SET_RAW_MEM;
209  
210   /* Get an enabled process and insert it in the interleave set of the initial state */
211   xbt_swag_foreach(process, simix_global->process_list){
212     if(MC_process_is_enabled(process)){
213       MC_state_interleave_process(initial_state, process);
214       if(mc_reduce_kind != e_mc_reduce_none)
215         break;
216     }
217   }
218
219   xbt_fifo_unshift(mc_stack_safety, initial_state);
220
221   /* To ensure the soundness of DPOR, we have to keep a list of 
222      processes which are still enabled at each step of the exploration. 
223      If max depth is reached, we interleave them in the state in which they have 
224      been enabled for the first time. */
225   xbt_swag_foreach(process, simix_global->process_list){
226     if(MC_process_is_enabled(process)){
227       char *key = bprintf("%lu", process->pid);
228       char *data = bprintf("%d", xbt_fifo_size(mc_stack_safety));
229       xbt_dict_set(first_enabled_state, key, data, NULL);
230       xbt_free(key);
231     }
232   }
233
234   MC_UNSET_RAW_MEM;
235
236   if(raw_mem_set)
237     MC_SET_RAW_MEM;
238   else
239     MC_UNSET_RAW_MEM;
240   
241 }
242
243
244 /**
245  *   \brief Perform the model-checking operation using a depth-first search exploration
246  *         with Dynamic Partial Order Reductions
247  */
248 void MC_dpor(void)
249 {
250
251   char *req_str;
252   int value;
253   smx_simcall_t req = NULL, prev_req = NULL;
254   mc_state_t state = NULL, prev_state = NULL, next_state = NULL, restore_state=NULL;
255   smx_process_t process = NULL;
256   xbt_fifo_item_t item = NULL;
257   int pos;
258   int visited_state;
259   int enabled = 0, max_depth_reached = 0;
260
261   while (xbt_fifo_size(mc_stack_safety) > 0) {
262
263     /* Get current state */
264     state = (mc_state_t) 
265       xbt_fifo_get_item_content(xbt_fifo_get_first_item(mc_stack_safety));
266
267     XBT_DEBUG("**************************************************");
268     XBT_DEBUG("Exploration depth=%d (state=%p)(%u interleave)",
269               xbt_fifo_size(mc_stack_safety), state,
270               MC_state_interleave_size(state));
271
272     /* Update statistics */
273     mc_stats->visited_states++;
274
275     /* If there are processes to interleave and the maximum depth has not been reached
276        then perform one step of the exploration algorithm */
277     if (xbt_fifo_size(mc_stack_safety) <= _sg_mc_max_depth &&
278         (req = MC_state_get_request(state, &value))) {
279
280       /* Debug information */
281       if(XBT_LOG_ISENABLED(mc_dpor, xbt_log_priority_debug)){
282         req_str = MC_request_to_string(req, value);
283         XBT_DEBUG("Execute: %s", req_str);
284         xbt_free(req_str);
285       }
286         
287       if(dot_output != NULL)
288         req_str = MC_request_get_dot_output(req, value);
289
290       MC_state_set_executed_request(state, req, value);
291       mc_stats->executed_transitions++;
292
293       MC_SET_RAW_MEM;
294       char *key = bprintf("%lu", req->issuer->pid);
295       xbt_dict_remove(first_enabled_state, key); 
296       xbt_free(key);
297       MC_UNSET_RAW_MEM;
298
299       /* Answer the request */
300       SIMIX_simcall_pre(req, value); /* After this call req is no longer usefull */
301
302       /* Wait for requests (schedules processes) */
303       MC_wait_for_requests();
304
305       /* Create the new expanded state */
306       MC_SET_RAW_MEM;
307
308       next_state = MC_state_new();
309
310       if((visited_state = is_visited_state()) == -1){
311      
312         /* Get an enabled process and insert it in the interleave set of the next state */
313         xbt_swag_foreach(process, simix_global->process_list){
314           if(MC_process_is_enabled(process)){
315             MC_state_interleave_process(next_state, process);
316             if(mc_reduce_kind != e_mc_reduce_none)
317               break;
318           }
319         }
320
321         if(_sg_mc_checkpoint && ((xbt_fifo_size(mc_stack_safety) + 1) % _sg_mc_checkpoint == 0)){
322           next_state->system_state = MC_take_snapshot();
323         }
324
325         if(dot_output != NULL)
326           fprintf(dot_output, "\"%d\" -> \"%d\" [%s];\n", state->num, next_state->num, req_str);
327
328       }else{
329
330         if(dot_output != NULL)
331           fprintf(dot_output, "\"%d\" -> \"%d\" [%s];\n", state->num, visited_state, req_str);
332
333       }
334
335       xbt_fifo_unshift(mc_stack_safety, next_state);
336
337       /* Insert in dict all enabled processes, if not included yet */
338       xbt_swag_foreach(process, simix_global->process_list){
339         if(MC_process_is_enabled(process)){
340           char *key = bprintf("%lu", process->pid);
341           if(xbt_dict_get_or_null(first_enabled_state, key) == NULL){
342             char *data = bprintf("%d", xbt_fifo_size(mc_stack_safety));
343             xbt_dict_set(first_enabled_state, key, data, NULL); 
344           }
345           xbt_free(key);
346         }
347       }
348
349       MC_UNSET_RAW_MEM;
350
351       if(dot_output != NULL)
352         xbt_free(req_str);
353
354       /* Let's loop again */
355
356       /* The interleave set is empty or the maximum depth is reached, let's back-track */
357     } else {
358
359       if(xbt_fifo_size(mc_stack_safety) > _sg_mc_max_depth){  
360
361         XBT_WARN("/!\\ Max depth reached ! /!\\ ");
362
363         /* Interleave enabled processes in the state in which they have been enabled for the first time */
364         xbt_swag_foreach(process, simix_global->process_list){
365           if(MC_process_is_enabled(process)){
366             char *key = bprintf("%lu", process->pid);
367             enabled = (int)strtoul(xbt_dict_get_or_null(first_enabled_state, key), 0, 10);
368             xbt_free(key);
369             mc_state_t state_test = NULL;
370             xbt_fifo_item_t item = NULL;
371             int cursor = xbt_fifo_size(mc_stack_safety);
372             xbt_fifo_foreach(mc_stack_safety, item, state_test, mc_state_t){
373               if(cursor-- == enabled){ 
374                 if(!MC_state_process_is_done(state_test, process)){ 
375                   MC_state_interleave_process(state_test, process);
376                   break;
377                 }
378               }
379             } 
380           }
381         }
382
383         if(MC_state_interleave_size(state) > 0){
384           max_depth_reached = 1;
385         }else{
386           /* Trash the current state, no longer needed */
387           MC_SET_RAW_MEM;
388           xbt_fifo_shift(mc_stack_safety);
389           MC_state_delete(state);
390           MC_UNSET_RAW_MEM;
391
392           max_depth_reached = 0;
393         }
394         
395
396       }else{
397
398         XBT_DEBUG("There are no more processes to interleave.");
399
400         /* Trash the current state, no longer needed */
401         MC_SET_RAW_MEM;
402         xbt_fifo_shift(mc_stack_safety);
403         MC_state_delete(state);
404         MC_UNSET_RAW_MEM;
405
406         max_depth_reached = 0;
407       }
408       
409       /* Check for deadlocks */
410       if(MC_deadlock_check()){
411         MC_show_deadlock(NULL);
412         return;
413       }
414
415       MC_SET_RAW_MEM;
416       /* Traverse the stack backwards until a state with a non empty interleave
417          set is found, deleting all the states that have it empty in the way.
418          For each deleted state, check if the request that has generated it 
419          (from it's predecesor state), depends on any other previous request 
420          executed before it. If it does then add it to the interleave set of the
421          state that executed that previous request. */
422       
423       while ((state = xbt_fifo_shift(mc_stack_safety)) != NULL) {
424         if(mc_reduce_kind != e_mc_reduce_none){
425           if((xbt_fifo_size(mc_stack_safety) == _sg_mc_max_depth) && max_depth_reached){
426             req = MC_state_get_request(state, &value);
427             MC_state_set_executed_request(state, req, value);
428           }
429           req = MC_state_get_internal_request(state);
430           xbt_fifo_foreach(mc_stack_safety, item, prev_state, mc_state_t) {
431             if(MC_request_depend(req, MC_state_get_internal_request(prev_state))){
432               if(XBT_LOG_ISENABLED(mc_dpor, xbt_log_priority_debug)){
433                 XBT_DEBUG("Dependent Transitions:");
434                 prev_req = MC_state_get_executed_request(prev_state, &value);
435                 req_str = MC_request_to_string(prev_req, value);
436                 XBT_DEBUG("%s (state=%p)", req_str, prev_state);
437                 xbt_free(req_str);
438                 prev_req = MC_state_get_executed_request(state, &value);
439                 req_str = MC_request_to_string(prev_req, value);
440                 XBT_DEBUG("%s (state=%p)", req_str, state);
441                 xbt_free(req_str);              
442               }
443
444               if(!MC_state_process_is_done(prev_state, req->issuer))
445                 MC_state_interleave_process(prev_state, req->issuer);
446               else
447                 XBT_DEBUG("Process %p is in done set", req->issuer);
448
449               break;
450
451             }else if(req->issuer == MC_state_get_internal_request(prev_state)->issuer){
452
453               XBT_DEBUG("Simcall %d and %d with same issuer", req->call, MC_state_get_internal_request(prev_state)->call);
454               break;
455
456             }
457           }
458         }
459              
460         if (MC_state_interleave_size(state) && xbt_fifo_size(mc_stack_safety) < _sg_mc_max_depth) {
461           /* We found a back-tracking point, let's loop */
462           XBT_DEBUG("Back-tracking to depth %d", xbt_fifo_size(mc_stack_safety) + 1);
463           if(_sg_mc_checkpoint){
464             if(state->system_state != NULL){
465               MC_restore_snapshot(state->system_state);
466               xbt_fifo_unshift(mc_stack_safety, state);
467               MC_UNSET_RAW_MEM;
468             }else{
469               pos = xbt_fifo_size(mc_stack_safety);
470               item = xbt_fifo_get_first_item(mc_stack_safety);
471               while(pos>0){
472                 restore_state = (mc_state_t) xbt_fifo_get_item_content(item);
473                 if(restore_state->system_state != NULL){
474                   break;
475                 }else{
476                   item = xbt_fifo_get_next_item(item);
477                   pos--;
478                 }
479               }
480               MC_restore_snapshot(restore_state->system_state);
481               xbt_fifo_unshift(mc_stack_safety, state);
482               MC_UNSET_RAW_MEM;
483               MC_replay(mc_stack_safety, pos);
484             }
485           }else{
486             xbt_fifo_unshift(mc_stack_safety, state);
487             MC_UNSET_RAW_MEM;
488             MC_replay(mc_stack_safety, -1);
489           }
490           XBT_DEBUG("Back-tracking to depth %d", xbt_fifo_size(mc_stack_safety));
491           break;
492         } else {
493           MC_state_delete(state);
494         }
495       }
496       MC_UNSET_RAW_MEM;
497     }
498   }
499   MC_print_statistics(mc_stats);
500   MC_UNSET_RAW_MEM;  
501
502   return;
503 }
504
505
506
507