Logo AND Algorithmique Numérique Distribuée

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