Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
rename a method
[simgrid.git] / src / mc / checker / LivenessChecker.cpp
1 /* Copyright (c) 2011-2015. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 #include <cstring>
8
9 #include <memory>
10 #include <list>
11
12 #include <boost/range/algorithm.hpp>
13
14 #include <unistd.h>
15 #include <sys/wait.h>
16
17 #include <xbt/automaton.h>
18 #include <xbt/dynar.h>
19 #include <xbt/dynar.hpp>
20 #include <xbt/log.h>
21 #include <xbt/sysdep.h>
22
23 #include "src/mc/Session.hpp"
24 #include "src/mc/Transition.hpp"
25 #include "src/mc/checker/LivenessChecker.hpp"
26 #include "src/mc/mc_exit.h"
27 #include "src/mc/mc_private.h"
28 #include "src/mc/mc_private.h"
29 #include "src/mc/mc_record.h"
30 #include "src/mc/mc_replay.h"
31 #include "src/mc/mc_request.h"
32 #include "src/mc/mc_smx.h"
33 #include "src/mc/remote/Client.hpp"
34
35 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_liveness, mc, "Logging specific to algorithms for liveness properties verification");
36
37 /********* Static functions *********/
38
39 namespace simgrid {
40 namespace mc {
41
42 VisitedPair::VisitedPair(
43   int pair_num, xbt_automaton_state_t automaton_state,
44   std::shared_ptr<const std::vector<int>> atomic_propositions,
45   std::shared_ptr<simgrid::mc::State> graph_state)
46 {
47   simgrid::mc::Process* process = &(mc_model_checker->process());
48
49   this->graph_state = std::move(graph_state);
50   if(this->graph_state->system_state == nullptr)
51     this->graph_state->system_state = simgrid::mc::take_snapshot(pair_num);
52   this->heap_bytes_used = mmalloc_get_bytes_used_remote(
53     process->get_heap()->heaplimit,
54     process->get_malloc_info());
55
56   this->actors_count = mc_model_checker->process().actors().size();
57
58   this->automaton_state = automaton_state;
59   this->num = pair_num;
60   this->other_num = -1;
61   this->atomic_propositions = std::move(atomic_propositions);
62 }
63
64 static bool evaluate_label(xbt_automaton_exp_label_t l, std::vector<int> const& values)
65 {
66   switch (l->type) {
67   case xbt_automaton_exp_label::AUT_OR:
68     return evaluate_label(l->u.or_and.left_exp, values)
69       || evaluate_label(l->u.or_and.right_exp, values);
70   case xbt_automaton_exp_label::AUT_AND:
71     return evaluate_label(l->u.or_and.left_exp, values)
72       && evaluate_label(l->u.or_and.right_exp, values);
73   case xbt_automaton_exp_label::AUT_NOT:
74     return not evaluate_label(l->u.exp_not, values);
75   case xbt_automaton_exp_label::AUT_PREDICAT:{
76       unsigned int cursor = 0;
77       xbt_automaton_propositional_symbol_t p = nullptr;
78       xbt_dynar_foreach(simgrid::mc::property_automaton->propositional_symbols, cursor, p) {
79         if (std::strcmp(xbt_automaton_propositional_symbol_get_name(p), l->u.predicat) == 0)
80           return values[cursor] != 0;
81       }
82       xbt_die("Missing predicate");
83     }
84   case xbt_automaton_exp_label::AUT_ONE:
85     return true;
86   default:
87     xbt_die("Unexpected vaue for automaton");
88   }
89 }
90
91 Pair::Pair(unsigned long expanded_pairs) : num(expanded_pairs)
92 {}
93
94 std::shared_ptr<const std::vector<int>> LivenessChecker::getPropositionValues()
95 {
96   std::vector<int> values;
97   unsigned int cursor = 0;
98   xbt_automaton_propositional_symbol_t ps = nullptr;
99   xbt_dynar_foreach(simgrid::mc::property_automaton->propositional_symbols, cursor, ps)
100     values.push_back(xbt_automaton_propositional_symbol_evaluate(ps));
101   return std::make_shared<const std::vector<int>>(std::move(values));
102 }
103
104 int LivenessChecker::compare(simgrid::mc::VisitedPair* state1, simgrid::mc::VisitedPair* state2)
105 {
106   simgrid::mc::Snapshot* s1 = state1->graph_state->system_state.get();
107   simgrid::mc::Snapshot* s2 = state2->graph_state->system_state.get();
108   int num1 = state1->num;
109   int num2 = state2->num;
110   return simgrid::mc::snapshot_compare(num1, s1, num2, s2);
111 }
112
113 std::shared_ptr<VisitedPair> LivenessChecker::insertAcceptancePair(simgrid::mc::Pair* pair)
114 {
115   std::shared_ptr<VisitedPair> new_pair = std::make_shared<VisitedPair>(
116     pair->num, pair->automaton_state, pair->atomic_propositions,
117     pair->graph_state);
118
119   auto res = boost::range::equal_range(acceptancePairs_, new_pair.get(),
120                                        simgrid::mc::DerefAndCompareByActorsCountAndUsedHeap());
121
122   if (pair->search_cycle) for (auto i = res.first; i != res.second; ++i) {
123     std::shared_ptr<simgrid::mc::VisitedPair> const& pair_test = *i;
124     if (xbt_automaton_state_compare(
125           pair_test->automaton_state, new_pair->automaton_state) != 0
126         || *(pair_test->atomic_propositions) != *(new_pair->atomic_propositions)
127         || this->compare(pair_test.get(), new_pair.get()) != 0)
128       continue;
129     XBT_INFO("Pair %d already reached (equal to pair %d) !", new_pair->num, pair_test->num);
130     explorationStack_.pop_back();
131     if (dot_output != nullptr)
132       fprintf(dot_output, "\"%d\" -> \"%d\" [%s];\n", this->previousPair_, pair_test->num,
133               this->previousRequest_.c_str());
134     return nullptr;
135   }
136
137   acceptancePairs_.insert(res.first, new_pair);
138   return new_pair;
139 }
140
141 void LivenessChecker::removeAcceptancePair(int pair_num)
142 {
143   for (auto i = acceptancePairs_.begin(); i != acceptancePairs_.end(); ++i)
144     if ((*i)->num == pair_num) {
145       acceptancePairs_.erase(i);
146       break;
147     }
148 }
149
150 void LivenessChecker::replay()
151 {
152   XBT_DEBUG("**** Begin Replay ****");
153
154   /* Intermediate backtracking */
155   if(_sg_mc_checkpoint > 0) {
156     simgrid::mc::Pair* pair = explorationStack_.back().get();
157     if(pair->graph_state->system_state){
158       simgrid::mc::restore_snapshot(pair->graph_state->system_state);
159       return;
160     }
161   }
162
163   /* Restore the initial state */
164   simgrid::mc::session->restoreInitialState();
165
166   /* Traverse the stack from the initial state and re-execute the transitions */
167   int depth = 1;
168   for (std::shared_ptr<Pair> const& pair : explorationStack_) {
169     if (pair == explorationStack_.back())
170       break;
171
172     std::shared_ptr<State> state = pair->graph_state;
173
174     if (pair->exploration_started) {
175
176       int req_num = state->transition.argument;
177       smx_simcall_t saved_req = &state->executed_req;
178
179       smx_simcall_t req = nullptr;
180
181       if (saved_req != nullptr) {
182         /* because we got a copy of the executed request, we have to fetch the
183              real one, pointed by the request field of the issuer process */
184         const smx_actor_t issuer = MC_smx_simcall_get_issuer(saved_req);
185         req = &issuer->simcall;
186
187         /* Debug information */
188         XBT_DEBUG("Replay (depth = %d) : %s (%p)",
189           depth,
190           simgrid::mc::request_to_string(
191             req, req_num, simgrid::mc::RequestType::simix).c_str(),
192           state.get());
193       }
194
195       this->getSession().execute(state->transition);
196     }
197
198     /* Update statistics */
199     visitedPairsCount_++;
200     mc_model_checker->executed_transitions++;
201
202     depth++;
203
204   }
205
206   XBT_DEBUG("**** End Replay ****");
207 }
208
209 /**
210  * \brief Checks whether a given pair has already been visited by the algorithm.
211  */
212 int LivenessChecker::insertVisitedPair(std::shared_ptr<VisitedPair> visited_pair, simgrid::mc::Pair* pair)
213 {
214   if (_sg_mc_max_visited_states == 0)
215     return -1;
216
217   if (visited_pair == nullptr)
218     visited_pair =
219         std::make_shared<VisitedPair>(pair->num, pair->automaton_state, pair->atomic_propositions, pair->graph_state);
220
221   auto range = boost::range::equal_range(visitedPairs_, visited_pair.get(),
222                                          simgrid::mc::DerefAndCompareByActorsCountAndUsedHeap());
223
224   for (auto i = range.first; i != range.second; ++i) {
225     VisitedPair* pair_test = i->get();
226     if (xbt_automaton_state_compare(
227           pair_test->automaton_state, visited_pair->automaton_state) != 0
228         || *(pair_test->atomic_propositions) != *(visited_pair->atomic_propositions)
229         || this->compare(pair_test, visited_pair.get()) != 0)
230         continue;
231     if (pair_test->other_num == -1)
232       visited_pair->other_num = pair_test->num;
233     else
234       visited_pair->other_num = pair_test->other_num;
235     if (dot_output == nullptr)
236       XBT_DEBUG("Pair %d already visited ! (equal to pair %d)", visited_pair->num, pair_test->num);
237     else
238       XBT_DEBUG("Pair %d already visited ! (equal to pair %d (pair %d in dot_output))",
239         visited_pair->num, pair_test->num, visited_pair->other_num);
240     (*i) = std::move(visited_pair);
241     return (*i)->other_num;
242   }
243
244   visitedPairs_.insert(range.first, std::move(visited_pair));
245   this->purgeVisitedPairs();
246   return -1;
247 }
248
249 void LivenessChecker::purgeVisitedPairs()
250 {
251   if (_sg_mc_max_visited_states != 0 && visitedPairs_.size() > (std::size_t)_sg_mc_max_visited_states) {
252     // Remove the oldest entry with a linear search:
253     visitedPairs_.erase(boost::min_element(visitedPairs_,
254       [](std::shared_ptr<VisitedPair> const a, std::shared_ptr<VisitedPair> const& b) {
255         return a->num < b->num; } ));
256   }
257 }
258
259 LivenessChecker::LivenessChecker(Session& session) : Checker(session)
260 {
261 }
262
263 RecordTrace LivenessChecker::getRecordTrace() // override
264 {
265   RecordTrace res;
266   for (std::shared_ptr<Pair> const& pair : explorationStack_)
267     res.push_back(pair->graph_state->getTransition());
268   return res;
269 }
270
271 void LivenessChecker::logState() // override
272 {
273   Checker::logState();
274   XBT_INFO("Expanded pairs = %lu", expandedPairsCount_);
275   XBT_INFO("Visited pairs = %lu", visitedPairsCount_);
276   XBT_INFO("Executed transitions = %lu", mc_model_checker->executed_transitions);
277 }
278
279 void LivenessChecker::showAcceptanceCycle(std::size_t depth)
280 {
281   XBT_INFO("*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*");
282   XBT_INFO("|             ACCEPTANCE CYCLE            |");
283   XBT_INFO("*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*");
284   XBT_INFO("Counter-example that violates formula :");
285   simgrid::mc::dumpRecordPath();
286   for (auto& s : this->getTextualTrace())
287     XBT_INFO("%s", s.c_str());
288   simgrid::mc::session->logState();
289   XBT_INFO("Counter-example depth : %zd", depth);
290 }
291
292 std::vector<std::string> LivenessChecker::getTextualTrace() // override
293 {
294   std::vector<std::string> trace;
295   for (std::shared_ptr<Pair> const& pair : explorationStack_) {
296     int req_num = pair->graph_state->transition.argument;
297     smx_simcall_t req = &pair->graph_state->executed_req;
298     if (req && req->call != SIMCALL_NONE)
299       trace.push_back(simgrid::mc::request_to_string(
300         req, req_num, simgrid::mc::RequestType::executed));
301   }
302   return trace;
303 }
304
305 std::shared_ptr<Pair> LivenessChecker::newPair(Pair* current_pair, xbt_automaton_state_t state,
306                                                std::shared_ptr<const std::vector<int>> propositions)
307 {
308   std::shared_ptr<Pair> next_pair = std::make_shared<Pair>(++expandedPairsCount_);
309   next_pair->automaton_state      = state;
310   next_pair->graph_state          = std::shared_ptr<simgrid::mc::State>(new simgrid::mc::State(++expandedStatesCount_));
311   next_pair->atomic_propositions  = std::move(propositions);
312   if (current_pair)
313     next_pair->depth = current_pair->depth + 1;
314   else
315     next_pair->depth = 1;
316   /* Get enabled actors and insert them in the interleave set of the next graph_state */
317   for (auto& actor : mc_model_checker->process().actors())
318     if (simgrid::mc::actor_is_enabled(actor.copy.getBuffer()))
319       next_pair->graph_state->addInterleavingSet(actor.copy.getBuffer());
320   next_pair->requests = next_pair->graph_state->interleaveSize();
321   /* FIXME : get search_cycle value for each accepting state */
322   if (next_pair->automaton_state->type == 1 || (current_pair && current_pair->search_cycle))
323     next_pair->search_cycle = true;
324   else
325     next_pair->search_cycle = false;
326   return next_pair;
327 }
328
329 void LivenessChecker::backtrack()
330 {
331   /* Traverse the stack backwards until a pair with a non empty interleave
332      set is found, deleting all the pairs that have it empty in the way. */
333   while (not explorationStack_.empty()) {
334     std::shared_ptr<simgrid::mc::Pair> current_pair = explorationStack_.back();
335     explorationStack_.pop_back();
336     if (current_pair->requests > 0) {
337       /* We found a backtracking point */
338       XBT_DEBUG("Backtracking to depth %d", current_pair->depth);
339       explorationStack_.push_back(std::move(current_pair));
340       this->replay();
341       XBT_DEBUG("Backtracking done");
342       break;
343     } else {
344       XBT_DEBUG("Delete pair %d at depth %d", current_pair->num, current_pair->depth);
345       if (current_pair->automaton_state->type == 1)
346         this->removeAcceptancePair(current_pair->num);
347     }
348   }
349 }
350
351 void LivenessChecker::run()
352 {
353   XBT_INFO("Check the liveness property %s", _sg_mc_property_file);
354   MC_automaton_load(_sg_mc_property_file);
355
356   XBT_DEBUG("Starting the liveness algorithm");
357   simgrid::mc::session->initialize();
358
359   /* Initialize */
360   this->previousPair_ = 0;
361
362   std::shared_ptr<const std::vector<int>> propos = this->getPropositionValues();
363
364   // For each initial state of the property automaton, push a
365   // (application_state, automaton_state) pair to the exploration stack:
366   unsigned int cursor = 0;
367   xbt_automaton_state_t automaton_state;
368   xbt_dynar_foreach (simgrid::mc::property_automaton->states, cursor, automaton_state)
369     if (automaton_state->type == -1)
370       explorationStack_.push_back(this->newPair(nullptr, automaton_state, propos));
371
372   /* Actually run the double DFS search for counter-examples */
373   while (not explorationStack_.empty()) {
374     std::shared_ptr<Pair> current_pair = explorationStack_.back();
375
376     /* Update current state in buchi automaton */
377     simgrid::mc::property_automaton->current_state = current_pair->automaton_state;
378
379     XBT_DEBUG("********************* ( Depth = %d, search_cycle = %d, interleave size = %zd, pair_num = %d, requests = %d)",
380        current_pair->depth, current_pair->search_cycle,
381        current_pair->graph_state->interleaveSize(),
382        current_pair->num,
383        current_pair->requests);
384
385     if (current_pair->requests == 0) {
386       this->backtrack();
387       continue;
388     }
389
390     std::shared_ptr<VisitedPair> reached_pair;
391     if (current_pair->automaton_state->type == 1 && not current_pair->exploration_started &&
392         (reached_pair = this->insertAcceptancePair(current_pair.get())) == nullptr) {
393       this->showAcceptanceCycle(current_pair->depth);
394       throw simgrid::mc::LivenessError();
395     }
396
397     /* Pair already visited ? stop the exploration on the current path */
398     int visited_num = -1;
399     if ((not current_pair->exploration_started) &&
400         (visited_num = this->insertVisitedPair(reached_pair, current_pair.get())) != -1) {
401       if (dot_output != nullptr){
402         fprintf(dot_output, "\"%d\" -> \"%d\" [%s];\n",
403           this->previousPair_, visited_num,
404           this->previousRequest_.c_str());
405         fflush(dot_output);
406       }
407       XBT_DEBUG("Pair already visited (equal to pair %d), exploration on the current path stopped.", visited_num);
408       current_pair->requests = 0;
409       this->backtrack();
410       continue;
411     }
412
413     smx_simcall_t req = MC_state_get_request(current_pair->graph_state.get());
414     int req_num = current_pair->graph_state->transition.argument;
415
416     if (dot_output != nullptr) {
417       if (this->previousPair_ != 0 && this->previousPair_ != current_pair->num) {
418         fprintf(dot_output, "\"%d\" -> \"%d\" [%s];\n",
419           this->previousPair_, current_pair->num,
420           this->previousRequest_.c_str());
421         this->previousRequest_.clear();
422       }
423       this->previousPair_ = current_pair->num;
424       this->previousRequest_ = simgrid::mc::request_get_dot_output(req, req_num);
425       if (current_pair->search_cycle)
426         fprintf(dot_output, "%d [shape=doublecircle];\n", current_pair->num);
427       fflush(dot_output);
428     }
429
430     XBT_DEBUG("Execute: %s",
431       simgrid::mc::request_to_string(
432         req, req_num, simgrid::mc::RequestType::simix).c_str());
433
434     /* Update stats */
435     mc_model_checker->executed_transitions++;
436     if (not current_pair->exploration_started)
437       visitedPairsCount_++;
438
439     /* Answer the request */
440     mc_model_checker->handle_simcall(current_pair->graph_state->transition);
441
442     /* Wait for requests (schedules processes) */
443     mc_model_checker->wait_for_requests();
444
445     current_pair->requests--;
446     current_pair->exploration_started = true;
447
448     /* Get values of atomic propositions (variables used in the property formula) */
449     std::shared_ptr<const std::vector<int>> prop_values = this->getPropositionValues();
450
451     // For each enabled transition in the property automaton, push a
452     // (application_state, automaton_state) pair to the exploration stack:
453     for (int cursor = xbt_dynar_length(current_pair->automaton_state->out) - 1; cursor >= 0; cursor--) {
454       xbt_automaton_transition_t transition_succ = (xbt_automaton_transition_t)xbt_dynar_get_as(current_pair->automaton_state->out, cursor, xbt_automaton_transition_t);
455       if (evaluate_label(transition_succ->label, *prop_values))
456           explorationStack_.push_back(this->newPair(
457             current_pair.get(), transition_succ->dst, prop_values));
458      }
459
460   }
461
462   XBT_INFO("No property violation found.");
463   simgrid::mc::session->logState();
464 }
465
466 Checker* createLivenessChecker(Session& session)
467 {
468   return new LivenessChecker(session);
469 }
470
471 }
472 }