1 /* Copyright (c) 2011-2015. The SimGrid Team.
2 * All rights reserved. */
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. */
12 #include <xbt/automaton.h>
14 #include <xbt/sysdep.h>
15 #include <xbt/dynar.h>
18 #include "src/mc/mc_comm_pattern.h"
19 #include "src/mc/mc_safety.h"
20 #include "src/mc/mc_liveness.h"
21 #include "src/mc/mc_private.h"
22 #include "src/mc/Process.hpp"
23 #include "src/mc/mc_smx.h"
25 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_visited, mc,
26 "Logging specific to state equaity detection mechanisms");
31 xbt_dynar_t visited_pairs;
32 std::vector<std::unique_ptr<simgrid::mc::VisitedState>> visited_states;
34 static int is_exploration_stack_state(simgrid::mc::VisitedState* state){
35 xbt_fifo_item_t item = xbt_fifo_get_first_item(mc_stack);
37 if (((mc_state_t)xbt_fifo_get_item_content(item))->num == state->num){
38 ((mc_state_t)xbt_fifo_get_item_content(item))->in_visited_states = 0;
41 item = xbt_fifo_get_next_item(item);
47 * \brief Save the current state
48 * \return Snapshot of the current state.
50 VisitedState::VisitedState()
52 simgrid::mc::Process* process = &(mc_model_checker->process());
53 this->heap_bytes_used = mmalloc_get_bytes_used_remote(
54 process->get_heap()->heaplimit,
55 process->get_malloc_info());
58 mc_model_checker->process().simix_processes().size();
60 this->system_state = simgrid::mc::take_snapshot(mc_stats->expanded_states);
61 this->num = mc_stats->expanded_states;
65 VisitedState::~VisitedState()
67 if(!is_exploration_stack_state(this))
68 delete this->system_state;
71 VisitedPair::VisitedPair(int pair_num, xbt_automaton_state_t automaton_state, xbt_dynar_t atomic_propositions, mc_state_t graph_state)
73 simgrid::mc::Process* process = &(mc_model_checker->process());
75 this->graph_state = graph_state;
76 if(this->graph_state->system_state == nullptr)
77 this->graph_state->system_state = simgrid::mc::take_snapshot(pair_num);
78 this->heap_bytes_used = mmalloc_get_bytes_used_remote(
79 process->get_heap()->heaplimit,
80 process->get_malloc_info());
83 mc_model_checker->process().simix_processes().size();
85 this->automaton_state = automaton_state;
88 this->acceptance_removed = 0;
89 this->visited_removed = 0;
90 this->acceptance_pair = 0;
91 this->atomic_propositions = simgrid::xbt::unique_ptr<s_xbt_dynar_t>(
92 xbt_dynar_new(sizeof(int), nullptr));
94 unsigned int cursor = 0;
96 xbt_dynar_foreach(atomic_propositions, cursor, value)
97 xbt_dynar_push_as(this->atomic_propositions.get(), int, value);
100 static int is_exploration_stack_pair(simgrid::mc::VisitedPair* pair){
101 xbt_fifo_item_t item = xbt_fifo_get_first_item(mc_stack);
103 if (((simgrid::mc::Pair*)xbt_fifo_get_item_content(item))->num == pair->num){
104 ((simgrid::mc::Pair*)xbt_fifo_get_item_content(item))->visited_pair_removed = 1;
107 item = xbt_fifo_get_next_item(item);
112 VisitedPair::~VisitedPair()
114 if( !is_exploration_stack_pair(this))
115 MC_state_delete(this->graph_state, 1);
122 * \brief Find a suitable subrange of candidate duplicates for a given state
123 * \param list dynamic array of states/pairs with candidate duplicates of the current state;
124 * \param ref current state/pair;
125 * \param min (output) index of the beginning of the the subrange
126 * \param max (output) index of the enf of the subrange
128 * Given a suitably ordered array of states/pairs, this function extracts a subrange
129 * (with index *min <= i <= *max) with candidate duplicates of the given state/pair.
130 * This function uses only fast discriminating criterions and does not use the
131 * full state/pair comparison algorithms.
133 * The states/pairs in list MUST be ordered using a (given) weak order
134 * (based on nb_processes and heap_bytes_used).
135 * The subrange is the subrange of "equivalence" of the given state/pair.
137 int get_search_interval(xbt_dynar_t list, void *ref, int *min, int *max)
139 int cursor = 0, previous_cursor;
140 int nb_processes, heap_bytes_used, nb_processes_test, heap_bytes_used_test;
143 if (_sg_mc_liveness) {
144 nb_processes = ((simgrid::mc::VisitedPair*) ref)->nb_processes;
145 heap_bytes_used = ((simgrid::mc::VisitedPair*) ref)->heap_bytes_used;
147 nb_processes = ((simgrid::mc::VisitedState*) ref)->nb_processes;
148 heap_bytes_used = ((simgrid::mc::VisitedState*) ref)->heap_bytes_used;
152 int end = xbt_dynar_length(list) - 1;
154 while (start <= end) {
155 cursor = (start + end) / 2;
156 if (_sg_mc_liveness) {
157 ref_test = (simgrid::mc::VisitedPair*) xbt_dynar_get_as(list, cursor, simgrid::mc::VisitedPair*);
158 nb_processes_test = ((simgrid::mc::VisitedPair*) ref_test)->nb_processes;
159 heap_bytes_used_test = ((simgrid::mc::VisitedPair*) ref_test)->heap_bytes_used;
161 ref_test = (simgrid::mc::VisitedState*) xbt_dynar_get_as(list, cursor, simgrid::mc::VisitedState*);
162 nb_processes_test = ((simgrid::mc::VisitedState*) ref_test)->nb_processes;
163 heap_bytes_used_test = ((simgrid::mc::VisitedState*) ref_test)->heap_bytes_used;
165 if (nb_processes_test < nb_processes)
167 else if (nb_processes_test > nb_processes)
169 else if (heap_bytes_used_test < heap_bytes_used)
171 else if (heap_bytes_used_test > heap_bytes_used)
174 *min = *max = cursor;
175 previous_cursor = cursor - 1;
176 while (previous_cursor >= 0) {
177 if (_sg_mc_liveness) {
178 ref_test = (simgrid::mc::VisitedPair*) xbt_dynar_get_as(list, previous_cursor, simgrid::mc::VisitedPair*);
179 nb_processes_test = ((simgrid::mc::VisitedPair*) ref_test)->nb_processes;
180 heap_bytes_used_test = ((simgrid::mc::VisitedPair*) ref_test)->heap_bytes_used;
182 ref_test = (simgrid::mc::VisitedState*) xbt_dynar_get_as(list, previous_cursor, simgrid::mc::VisitedState*);
183 nb_processes_test = ((simgrid::mc::VisitedState*) ref_test)->nb_processes;
184 heap_bytes_used_test = ((simgrid::mc::VisitedState*) ref_test)->heap_bytes_used;
186 if (nb_processes_test != nb_processes || heap_bytes_used_test != heap_bytes_used)
188 *min = previous_cursor;
191 size_t next_cursor = cursor + 1;
192 while (next_cursor < xbt_dynar_length(list)) {
193 if (_sg_mc_liveness) {
194 ref_test = (simgrid::mc::VisitedPair*) xbt_dynar_get_as(list, next_cursor, simgrid::mc::VisitedPair*);
195 nb_processes_test = ((simgrid::mc::VisitedPair*) ref_test)->nb_processes;
196 heap_bytes_used_test = ((simgrid::mc::VisitedPair*) ref_test)->heap_bytes_used;
198 ref_test = (simgrid::mc::VisitedState*) xbt_dynar_get_as(list, next_cursor, simgrid::mc::VisitedState*);
199 nb_processes_test = ((simgrid::mc::VisitedState*) ref_test)->nb_processes;
200 heap_bytes_used_test = ((simgrid::mc::VisitedState*) ref_test)->heap_bytes_used;
202 if (nb_processes_test != nb_processes || heap_bytes_used_test != heap_bytes_used)
213 // TODO, it would make sense to use std::set instead
215 int get_search_interval(std::vector<std::unique_ptr<T>> const& list, T *ref, int *min, int *max)
217 int nb_processes = ref->nb_processes;
218 int heap_bytes_used = ref->heap_bytes_used;
222 int end = list.size() - 1;
223 while (start <= end) {
224 cursor = (start + end) / 2;
225 int nb_processes_test = list[cursor]->nb_processes;
226 int heap_bytes_used_test = list[cursor]->heap_bytes_used;
228 if (nb_processes_test < nb_processes)
230 else if (nb_processes_test > nb_processes)
232 else if (heap_bytes_used_test < heap_bytes_used)
234 else if (heap_bytes_used_test > heap_bytes_used)
237 *min = *max = cursor;
238 int previous_cursor = cursor - 1;
239 while (previous_cursor >= 0) {
240 nb_processes_test = list[previous_cursor]->nb_processes;
241 heap_bytes_used_test = list[previous_cursor]->heap_bytes_used;
242 if (nb_processes_test != nb_processes || heap_bytes_used_test != heap_bytes_used)
244 *min = previous_cursor;
247 size_t next_cursor = cursor + 1;
248 while (next_cursor < list.size()) {
249 nb_processes_test = list[next_cursor]->nb_processes;
250 heap_bytes_used_test = list[next_cursor]->heap_bytes_used;
251 if (nb_processes_test != nb_processes || heap_bytes_used_test != heap_bytes_used)
263 bool some_communications_are_not_finished()
265 for (size_t current_process = 1; current_process < MC_smx_get_maxpid(); current_process++) {
266 xbt_dynar_t pattern = xbt_dynar_get_as(
267 incomplete_communications_pattern, current_process, xbt_dynar_t);
268 if (!xbt_dynar_is_empty(pattern)) {
269 XBT_DEBUG("Some communications are not finished, cannot stop the exploration ! State not visited.");
280 * \brief Checks whether a given state has already been visited by the algorithm.
282 std::unique_ptr<simgrid::mc::VisitedState> is_visited_state(mc_state_t graph_state)
284 if (_sg_mc_visited == 0)
287 /* If comm determinism verification, we cannot stop the exploration if some
288 communications are not finished (at least, data are transfered). These communications
289 are incomplete and they cannot be analyzed and compared with the initial pattern. */
290 int partial_comm = (_sg_mc_comms_determinism || _sg_mc_send_determinism) &&
291 some_communications_are_not_finished();
293 std::unique_ptr<simgrid::mc::VisitedState> new_state =
294 std::unique_ptr<simgrid::mc::VisitedState>(new VisitedState());
295 graph_state->system_state = new_state->system_state;
296 graph_state->in_visited_states = 1;
297 XBT_DEBUG("Snapshot %p of visited state %d (exploration stack state %d)", new_state->system_state, new_state->num, graph_state->num);
299 if (visited_states.empty()) {
300 visited_states.push_back(std::move(new_state));
304 int min = -1, max = -1, index;
306 index = get_search_interval(visited_states, new_state.get(), &min, &max);
308 if (min != -1 && max != -1) {
310 if (_sg_mc_safety || (!partial_comm
311 && initial_global_state->initial_communications_pattern_done)) {
314 while (cursor <= max) {
315 if (snapshot_compare(visited_states[cursor].get(), new_state.get()) == 0) {
316 // The state has been visited:
318 std::unique_ptr<simgrid::mc::VisitedState> old_state =
319 std::move(visited_states[cursor]);
321 if (old_state->other_num == -1)
322 new_state->other_num = old_state->num;
324 new_state->other_num = old_state->other_num;
326 if (dot_output == nullptr)
327 XBT_DEBUG("State %d already visited ! (equal to state %d)",
328 new_state->num, old_state->num);
331 "State %d already visited ! (equal to state %d (state %d in dot_output))",
332 new_state->num, old_state->num, new_state->other_num);
334 /* Replace the old state with the new one (with a bigger num)
335 (when the max number of visited states is reached, the oldest
336 one is removed according to its number (= with the min number) */
337 XBT_DEBUG("Replace visited state %d with the new visited state %d",
338 old_state->num, new_state->num);
340 simgrid::mc::visited_states[cursor] = std::move(new_state);
341 return std::move(old_state);
347 XBT_DEBUG("Insert new visited state %d (total : %lu)", new_state->num, (unsigned long) visited_states.size());
348 visited_states.insert(visited_states.begin() + min, std::move(new_state));
352 // The state has not been visited: insert the state in the dynamic array.
353 simgrid::mc::VisitedState* state_test = &*visited_states[index];
354 std::size_t position;
355 if (state_test->nb_processes < new_state->nb_processes)
356 position = index + 1;
357 else if (state_test->heap_bytes_used < new_state->heap_bytes_used)
358 position = index + 1;
361 visited_states.insert(visited_states.begin() + position, std::move(new_state));
362 XBT_DEBUG("Insert new visited state %d (total : %lu)",
363 visited_states[index]->num,
364 (unsigned long) visited_states.size());
368 if (visited_states.size() <= (std::size_t) _sg_mc_visited)
371 // We have reached the maximum number of stored states;
373 XBT_DEBUG("Try to remove visited state (maximum number of stored states reached)");
375 // Find the (index of the) older state (with the smallest num):
376 int min2 = mc_stats->expanded_states;
377 unsigned int index2 = 0;
379 for (std::size_t cursor2 = 0; cursor2 != visited_states.size(); ++cursor2)
380 if (!mc_model_checker->is_important_snapshot(
381 *visited_states[cursor2]->system_state)
382 && visited_states[cursor2]->num < min2) {
384 min2 = visited_states[cursor2]->num;
388 visited_states.erase(visited_states.begin() + index2);
389 XBT_DEBUG("Remove visited state (maximum number of stored states reached)");
395 * \brief Checks whether a given pair has already been visited by the algorithm.
397 int is_visited_pair(simgrid::mc::VisitedPair* visited_pair, simgrid::mc::Pair* pair) {
399 if (_sg_mc_visited == 0)
402 simgrid::mc::VisitedPair* new_visited_pair = nullptr;
403 if (visited_pair == nullptr)
404 new_visited_pair = new simgrid::mc::VisitedPair(
405 pair->num, pair->automaton_state, pair->atomic_propositions.get(),
408 new_visited_pair = visited_pair;
410 if (xbt_dynar_is_empty(visited_pairs)) {
411 xbt_dynar_push(visited_pairs, &new_visited_pair);
415 int min = -1, max = -1, index;
417 simgrid::mc::VisitedPair* pair_test;
420 index = get_search_interval(visited_pairs, new_visited_pair, &min, &max);
422 if (min != -1 && max != -1) { // Visited pair with same number of processes and same heap bytes used exists
424 while (cursor <= max) {
425 pair_test = (simgrid::mc::VisitedPair*) xbt_dynar_get_as(visited_pairs, cursor, simgrid::mc::VisitedPair*);
426 if (xbt_automaton_state_compare(pair_test->automaton_state, new_visited_pair->automaton_state) == 0) {
427 if (xbt_automaton_propositional_symbols_compare_value(
428 pair_test->atomic_propositions.get(),
429 new_visited_pair->atomic_propositions.get()) == 0) {
430 if (snapshot_compare(pair_test, new_visited_pair) == 0) {
431 if (pair_test->other_num == -1)
432 new_visited_pair->other_num = pair_test->num;
434 new_visited_pair->other_num = pair_test->other_num;
435 if (dot_output == nullptr)
436 XBT_DEBUG("Pair %d already visited ! (equal to pair %d)", new_visited_pair->num, pair_test->num);
438 XBT_DEBUG("Pair %d already visited ! (equal to pair %d (pair %d in dot_output))", new_visited_pair->num, pair_test->num, new_visited_pair->other_num);
439 xbt_dynar_remove_at(visited_pairs, cursor, &pair_test);
440 xbt_dynar_insert_at(visited_pairs, cursor, &new_visited_pair);
441 pair_test->visited_removed = 1;
442 if (!pair_test->acceptance_pair
443 || pair_test->acceptance_removed == 1)
445 return new_visited_pair->other_num;
451 xbt_dynar_insert_at(visited_pairs, min, &new_visited_pair);
453 pair_test = (simgrid::mc::VisitedPair*) xbt_dynar_get_as(visited_pairs, index, simgrid::mc::VisitedPair*);
454 if (pair_test->nb_processes < new_visited_pair->nb_processes)
455 xbt_dynar_insert_at(visited_pairs, index + 1, &new_visited_pair);
456 else if (pair_test->heap_bytes_used < new_visited_pair->heap_bytes_used)
457 xbt_dynar_insert_at(visited_pairs, index + 1, &new_visited_pair);
459 xbt_dynar_insert_at(visited_pairs, index, &new_visited_pair);
462 if ((ssize_t) xbt_dynar_length(visited_pairs) > _sg_mc_visited) {
463 int min2 = mc_stats->expanded_pairs;
464 unsigned int cursor2 = 0;
465 unsigned int index2 = 0;
466 xbt_dynar_foreach(visited_pairs, cursor2, pair_test) {
467 if (!mc_model_checker->is_important_snapshot(*pair_test->graph_state->system_state)
468 && pair_test->num < min2) {
470 min2 = pair_test->num;
473 xbt_dynar_remove_at(visited_pairs, index2, &pair_test);
474 pair_test->visited_removed = 1;
475 if (!pair_test->acceptance_pair || pair_test->acceptance_removed)