1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2007, 2009 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include <libpspp/model-checker.h>
29 #include <libpspp/argv-parser.h>
30 #include <libpspp/bit-vector.h>
31 #include <libpspp/compiler.h>
32 #include <libpspp/deque.h>
33 #include <libpspp/misc.h>
34 #include <libpspp/str.h>
40 /* Initializes PATH as an empty path. */
42 mc_path_init (struct mc_path *path)
49 /* Copies the contents of OLD into NEW. */
51 mc_path_copy (struct mc_path *new, const struct mc_path *old)
53 if (old->length > new->capacity)
55 new->capacity = old->length;
57 new->ops = xnmalloc (new->capacity, sizeof *new->ops);
59 new->length = old->length;
60 memcpy (new->ops, old->ops, old->length * sizeof *new->ops);
63 /* Adds OP to the end of PATH. */
65 mc_path_push (struct mc_path *path, int op)
67 if (path->length >= path->capacity)
68 path->ops = xnrealloc (path->ops, ++path->capacity, sizeof *path->ops);
69 path->ops[path->length++] = op;
72 /* Removes and returns the operation at the end of PATH. */
74 mc_path_pop (struct mc_path *path)
76 int back = mc_path_back (path);
81 /* Returns the operation at the end of PATH. */
83 mc_path_back (const struct mc_path *path)
85 assert (path->length > 0);
86 return path->ops[path->length - 1];
91 mc_path_destroy (struct mc_path *path)
97 /* Returns the operation in position INDEX in PATH.
98 INDEX must be less than the length of PATH. */
100 mc_path_get_operation (const struct mc_path *path, size_t index)
102 assert (index < path->length);
103 return path->ops[index];
106 /* Returns the number of operations in PATH. */
108 mc_path_get_length (const struct mc_path *path)
113 /* Appends the operations in PATH to STRING, separating each one
114 with a single space. */
116 mc_path_to_string (const struct mc_path *path, struct string *string)
120 for (i = 0; i < mc_path_get_length (path); i++)
123 ds_put_char (string, ' ');
124 ds_put_format (string, "%d", mc_path_get_operation (path, i));
128 /* Search options. */
131 /* Search strategy. */
132 enum mc_strategy strategy; /* Type of strategy. */
133 int max_depth; /* Limit on depth (or INT_MAX). */
134 int hash_bits; /* Number of bits to hash (or 0). */
135 unsigned int seed; /* Random seed for MC_RANDOM
136 or MC_DROP_RANDOM. */
137 struct mc_path follow_path; /* Path for MC_PATH. */
139 /* Queue configuration. */
140 int queue_limit; /* Maximum length of queue. */
141 enum mc_queue_limit_strategy queue_limit_strategy;
142 /* How to choose state to drop
145 /* Stop conditions. */
146 int max_unique_states; /* Maximum unique states to process. */
147 int max_errors; /* Maximum errors to detect. */
148 double time_limit; /* Maximum time in seconds. */
150 /* Output configuration. */
151 int verbosity; /* 0=low, 1=normal, 2+=high. */
152 int failure_verbosity; /* If greater than verbosity,
153 verbosity of error replays. */
154 FILE *output_file; /* File to receive output. */
156 /* How to report intermediate progress. */
157 int progress_usec; /* Microseconds between reports. */
158 mc_progress_func *progress_func; /* Function to call on each report. */
164 /* Default progress function. */
166 mc_progress_dots (struct mc *mc)
168 if (mc_results_get_stop_reason (mc_get_results (mc)) == MC_CONTINUING)
175 /* Progress function that prints a one-line summary of the
176 current state on stderr. */
178 mc_progress_fancy (struct mc *mc)
180 const struct mc_results *results = mc_get_results (mc);
181 if (mc_results_get_stop_reason (results) == MC_CONTINUING)
182 fprintf (stderr, "Processed %d unique states, max depth %d, "
183 "dropped %d duplicates...\r",
184 mc_results_get_unique_state_count (results),
185 mc_results_get_max_depth_reached (results),
186 mc_results_get_duplicate_dropped_states (results));
192 /* Progress function that displays a detailed summary of the
193 current state on stderr. */
195 mc_progress_verbose (struct mc *mc)
197 const struct mc_results *results = mc_get_results (mc);
199 /* VT100 clear screen and home cursor. */
200 fprintf (stderr, "\033[H\033[2J");
202 if (mc_results_get_stop_reason (results) == MC_CONTINUING)
203 mc_results_print (results, stderr);
208 /* Do-nothing progress function. */
210 null_progress (struct mc *mc UNUSED)
215 /* Creates and returns a set of options initialized to the
218 mc_options_create (void)
220 struct mc_options *options = xmalloc (sizeof *options);
222 options->strategy = MC_BROAD;
223 options->max_depth = INT_MAX;
224 options->hash_bits = 20;
226 mc_path_init (&options->follow_path);
228 options->queue_limit = 10000;
229 options->queue_limit_strategy = MC_DROP_RANDOM;
231 options->max_unique_states = INT_MAX;
232 options->max_errors = 1;
233 options->time_limit = 0.0;
235 options->verbosity = 1;
236 options->failure_verbosity = 2;
237 options->output_file = stdout;
238 options->progress_usec = 250000;
239 options->progress_func = mc_progress_dots;
246 /* Returns a copy of the given OPTIONS. */
248 mc_options_clone (const struct mc_options *options)
250 return xmemdup (options, sizeof *options);
253 /* Destroys OPTIONS. */
255 mc_options_destroy (struct mc_options *options)
257 mc_path_destroy (&options->follow_path);
261 /* Returns the search strategy used for OPTIONS. The choices
264 - MC_BROAD (the default): Breadth-first search. First tries
265 all the operations with depth 1, then those with depth 2,
266 then those with depth 3, and so on.
268 This search algorithm finds the least number of operations
269 needed to trigger a given bug.
271 - MC_DEEP: Depth-first search. Searches downward in the tree
272 of states as fast as possible. Good for finding bugs that
273 require long sequences of operations to trigger.
275 - MC_RANDOM: Random-first search. Searches through the tree
276 of states in random order. The standard C library's rand
277 function selects the search path; you can control the seed
278 passed to srand using mc_options_set_seed.
280 - MC_PATH: Explicit path. Applies an explicitly specified
281 sequence of operations. */
283 mc_options_get_strategy (const struct mc_options *options)
285 return options->strategy;
288 /* Sets the search strategy used for OPTIONS to STRATEGY.
290 This function cannot be used to set MC_PATH as the search
291 strategy. Use mc_options_set_follow_path instead. */
293 mc_options_set_strategy (struct mc_options *options, enum mc_strategy strategy)
295 assert (strategy == MC_BROAD
296 || strategy == MC_DEEP
297 || strategy == MC_RANDOM);
298 options->strategy = strategy;
301 /* Returns OPTION's random seed used by MC_RANDOM and
304 mc_options_get_seed (const struct mc_options *options)
306 return options->seed;
309 /* Set OPTION's random seed used by MC_RANDOM and MC_DROP_RANDOM
312 mc_options_set_seed (struct mc_options *options, unsigned int seed)
314 options->seed = seed;
317 /* Returns the maximum depth to which OPTIONS's search will
318 descend. The initial states are at depth 1, states produced
319 as their mutations are at depth 2, and so on. */
321 mc_options_get_max_depth (const struct mc_options *options)
323 return options->max_depth;
326 /* Sets the maximum depth to which OPTIONS's search will descend
327 to MAX_DEPTH. The initial states are at depth 1, states
328 produced as their mutations are at depth 2, and so on. */
330 mc_options_set_max_depth (struct mc_options *options, int max_depth)
332 options->max_depth = max_depth;
335 /* Returns the base-2 log of the number of bits in OPTIONS's hash
336 table. The hash table is used for dropping states that are
337 probably duplicates: any state with a given hash value, as
338 will only be processed once. A return value of 0 indicates
339 that the model checker will not discard duplicate states based
342 The hash table is a power of 2 bits long, by default 2**20
343 bits (128 kB). Depending on how many states you expect the
344 model checker to check, how much memory you're willing to let
345 the hash table take up, and how worried you are about missing
346 states due to hash collisions, you could make it larger or
349 The "birthday paradox" points to a reasonable way to size your
350 hash table. If you expect the model checker to check about
351 2**N states, then, assuming a perfect hash, you need a hash
352 table of 2**(N+1) bits to have a 50% chance of seeing a hash
353 collision, 2**(N+2) bits to have a 25% chance, and so on. */
355 mc_options_get_hash_bits (const struct mc_options *options)
357 return options->hash_bits;
360 /* Sets the base-2 log of the number of bits in OPTIONS's hash
361 table to HASH_BITS. A HASH_BITS value of 0 requests that the
362 model checker not discard duplicate states based on their
363 hashes. (This causes the model checker to never terminate in
366 mc_options_set_hash_bits (struct mc_options *options, int hash_bits)
368 assert (hash_bits >= 0);
369 options->hash_bits = MIN (hash_bits, CHAR_BIT * sizeof (unsigned int) - 1);
372 /* Returns the path set in OPTIONS by mc_options_set_follow_path.
373 May be used only if the search strategy is MC_PATH. */
374 const struct mc_path *
375 mc_options_get_follow_path (const struct mc_options *options)
377 assert (options->strategy == MC_PATH);
378 return &options->follow_path;
381 /* Sets, in OPTIONS, the search algorithm to MC_PATH and the path
382 to be the explicit path specified in FOLLOW_PATH. */
384 mc_options_set_follow_path (struct mc_options *options,
385 const struct mc_path *follow_path)
387 assert (mc_path_get_length (follow_path) > 0);
388 options->strategy = MC_PATH;
389 mc_path_copy (&options->follow_path, follow_path);
392 /* Returns the maximum number of queued states in OPTIONS. The
393 default value is 10,000. The primary reason to limit the
394 number of queued states is to conserve memory, so if you can
395 afford the memory and your model needs more room in the queue,
396 you can raise the limit. Conversely, if your models are large
397 or memory is constrained, you can reduce the limit.
399 Following the execution of the model checker, you can find out
400 the maximum queue length during the run by calling
401 mc_results_get_max_queue_length. */
403 mc_options_get_queue_limit (const struct mc_options *options)
405 return options->queue_limit;
408 /* Sets the maximum number of queued states in OPTIONS to
411 mc_options_set_queue_limit (struct mc_options *options, int queue_limit)
413 assert (queue_limit > 0);
414 options->queue_limit = queue_limit;
417 /* Returns the queue limit strategy used by OPTIONS, that is,
418 when a new state must be inserted into a full state queue is
419 full, how the state to be dropped is chosen. The choices are:
421 - MC_DROP_NEWEST: Drop the newest state; that is, do not
422 insert the new state into the queue at all.
424 - MC_DROP_OLDEST: Drop the state that has been enqueued for
427 - MC_DROP_RANDOM (the default): Drop a randomly selected state
428 from the queue. The standard C library's rand function
429 selects the state to drop; you can control the seed passed
430 to srand using mc_options_set_seed. */
431 enum mc_queue_limit_strategy
432 mc_options_get_queue_limit_strategy (const struct mc_options *options)
434 return options->queue_limit_strategy;
437 /* Sets the queue limit strategy used by OPTIONS to STRATEGY.
439 This setting has no effect unless the model being checked
440 causes the state queue to overflow (see
441 mc_options_get_queue_limit). */
443 mc_options_set_queue_limit_strategy (struct mc_options *options,
444 enum mc_queue_limit_strategy strategy)
446 assert (strategy == MC_DROP_NEWEST
447 || strategy == MC_DROP_OLDEST
448 || strategy == MC_DROP_RANDOM);
449 options->queue_limit_strategy = strategy;
452 /* Returns OPTIONS's maximum number of unique states that the
453 model checker will examine before terminating. The default is
456 mc_options_get_max_unique_states (const struct mc_options *options)
458 return options->max_unique_states;
461 /* Sets OPTIONS's maximum number of unique states that the model
462 checker will examine before terminating to
465 mc_options_set_max_unique_states (struct mc_options *options,
466 int max_unique_states)
468 options->max_unique_states = max_unique_states;
471 /* Returns the maximum number of errors that OPTIONS will allow
472 the model checker to encounter before terminating. The
475 mc_options_get_max_errors (const struct mc_options *options)
477 return options->max_errors;
480 /* Sets the maximum number of errors that OPTIONS will allow the
481 model checker to encounter before terminating to
484 mc_options_set_max_errors (struct mc_options *options, int max_errors)
486 options->max_errors = max_errors;
489 /* Returns the maximum amount of time, in seconds, that OPTIONS will allow the
490 model checker to consume before terminating. The
491 default of 0.0 means that time consumption is unlimited. */
493 mc_options_get_time_limit (const struct mc_options *options)
495 return options->time_limit;
498 /* Sets the maximum amount of time, in seconds, that OPTIONS will
499 allow the model checker to consume before terminating to
500 TIME_LIMIT. A value of 0.0 means that time consumption is
501 unlimited; otherwise, the return value will be positive. */
503 mc_options_set_time_limit (struct mc_options *options, double time_limit)
505 assert (time_limit >= 0.0);
506 options->time_limit = time_limit;
509 /* Returns the level of verbosity for output messages specified
510 by OPTIONS. The default verbosity level is 1.
512 A verbosity level of 0 inhibits all messages except for
513 errors; a verbosity level of 1 also allows warnings; a
514 verbosity level of 2 also causes a description of each state
515 added to be output; a verbosity level of 3 also causes a
516 description of each duplicate state to be output. Verbosity
517 levels less than 0 or greater than 3 are allowed but currently
518 have no additional effect. */
520 mc_options_get_verbosity (const struct mc_options *options)
522 return options->verbosity;
525 /* Sets the level of verbosity for output messages specified
526 by OPTIONS to VERBOSITY. */
528 mc_options_set_verbosity (struct mc_options *options, int verbosity)
530 options->verbosity = verbosity;
533 /* Returns the level of verbosity for failures specified by
534 OPTIONS. The default failure verbosity level is 2.
536 The failure verbosity level has an effect only when an error
537 is reported, and only when the failure verbosity level is
538 higher than the regular verbosity level. When this is the
539 case, the model checker replays the error path at the higher
540 verbosity level specified. This has the effect of outputting
541 an explicit, human-readable description of the sequence of
542 operations that caused the error. */
544 mc_options_get_failure_verbosity (const struct mc_options *options)
546 return options->failure_verbosity;
549 /* Sets the level of verbosity for failures specified by OPTIONS
550 to FAILURE_VERBOSITY. */
552 mc_options_set_failure_verbosity (struct mc_options *options,
553 int failure_verbosity)
555 options->failure_verbosity = failure_verbosity;
558 /* Returns the output file used for messages printed by the model
559 checker specified by OPTIONS. The default is stdout. */
561 mc_options_get_output_file (const struct mc_options *options)
563 return options->output_file;
566 /* Sets the output file used for messages printed by the model
567 checker specified by OPTIONS to OUTPUT_FILE.
569 The model checker does not automatically close the specified
570 output file. If this is desired, the model checker's client
573 mc_options_set_output_file (struct mc_options *options,
576 options->output_file = output_file;
579 /* Returns the number of microseconds between calls to the
580 progress function specified by OPTIONS. The default is
581 250,000 (1/4 second). A value of 0 disables progress
584 mc_options_get_progress_usec (const struct mc_options *options)
586 return options->progress_usec;
589 /* Sets the number of microseconds between calls to the progress
590 function specified by OPTIONS to PROGRESS_USEC. A value of 0
591 disables progress reporting. */
593 mc_options_set_progress_usec (struct mc_options *options, int progress_usec)
595 assert (progress_usec >= 0);
596 options->progress_usec = progress_usec;
599 /* Returns the function called to report progress specified by
600 OPTIONS. The function used by default prints '.' to
603 mc_options_get_progress_func (const struct mc_options *options)
605 return options->progress_func;
608 /* Sets the function called to report progress specified by
609 OPTIONS to PROGRESS_FUNC. A non-null function must be
610 specified; to disable progress reporting, set the progress
611 reporting interval to 0.
613 PROGRESS_FUNC will be called zero or more times while the
614 model checker's run is ongoing. For these calls to the
615 progress function, mc_results_get_stop_reason will return
616 MC_CONTINUING. It will also be called exactly once soon
617 before mc_run returns, in which case
618 mc_results_get_stop_reason will return a different value. */
620 mc_options_set_progress_func (struct mc_options *options,
621 mc_progress_func *progress_func)
623 assert (options->progress_func != NULL);
624 options->progress_func = progress_func;
627 /* Returns the auxiliary data set in OPTIONS by the client. The
628 default is a null pointer.
630 This auxiliary data value can be retrieved by the
631 client-specified functions in struct mc_class during a model
632 checking run using mc_get_aux. */
634 mc_options_get_aux (const struct mc_options *options)
639 /* Sets the auxiliary data in OPTIONS to AUX. */
641 mc_options_set_aux (struct mc_options *options, void *aux)
646 /* Options command-line parser. */
650 /* Search strategies. */
661 /* Stop conditions. */
666 /* User interface. */
669 OPT_FAILURE_VERBOSITY,
672 static const struct argv_option mc_argv_options[] =
674 {"strategy", 0, required_argument, OPT_STRATEGY},
675 {"max-depth", 0, required_argument, OPT_MAX_DEPTH},
676 {"hash-bits", 0, required_argument, OPT_HASH_BITS},
677 {"path", 0, required_argument, OPT_PATH},
678 {"queue-limit", 0, required_argument, OPT_QUEUE_LIMIT},
679 {"queue-drop", 0, required_argument, OPT_QUEUE_DROP},
680 {"seed", 0, required_argument, OPT_SEED},
681 {"max-states", 0, required_argument, OPT_MAX_STATES},
682 {"max-errors", 0, required_argument, OPT_MAX_ERRORS},
683 {"time-limit", 0, required_argument, OPT_TIME_LIMIT},
684 {"progress", 0, required_argument, OPT_PROGRESS},
685 {"verbosity", 0, required_argument, OPT_VERBOSITY},
686 {"failure-verbosity", 0, required_argument, OPT_FAILURE_VERBOSITY},
688 enum { N_MC_ARGV_OPTIONS = sizeof mc_argv_options / sizeof *mc_argv_options };
690 /* Called by argv_parser_run to indicate that option ID has been
693 mc_parser_option_callback (int id, void *mc_options_)
695 struct mc_options *options = mc_options_;
699 if (mc_options_get_strategy (options) == MC_PATH)
700 error (1, 0, "--strategy and --path are mutually exclusive");
702 if (!strcmp (optarg, "broad"))
703 mc_options_set_strategy (options, MC_BROAD);
704 else if (!strcmp (optarg, "deep"))
705 mc_options_set_strategy (options, MC_DEEP);
706 else if (!strcmp (optarg, "random"))
707 mc_options_set_strategy (options, MC_RANDOM);
710 "strategy must be \"broad\", \"deep\", or \"random\"");
714 mc_options_set_max_depth (options, atoi (optarg));
719 int requested_hash_bits = atoi (optarg);
721 mc_options_set_hash_bits (options, requested_hash_bits);
722 hash_bits = mc_options_get_hash_bits (options);
723 if (hash_bits != requested_hash_bits)
724 error (0, 0, "hash bits adjusted to %d.", hash_bits);
733 if (mc_options_get_strategy (options) != MC_BROAD)
734 error (1, 0, "--strategy and --path are mutually exclusive");
736 mc_path_init (&path);
737 for (op = strtok (optarg, ":, \t"); op != NULL;
738 op = strtok (NULL, ":, \t"))
739 mc_path_push (&path, atoi (op));
740 if (mc_path_get_length (&path) == 0)
741 error (1, 0, "at least one value must be specified on --path");
742 mc_options_set_follow_path (options, &path);
743 mc_path_destroy (&path);
747 case OPT_QUEUE_LIMIT:
748 mc_options_set_queue_limit (options, atoi (optarg));
752 if (!strcmp (optarg, "newest"))
753 mc_options_set_queue_limit_strategy (options, MC_DROP_NEWEST);
754 else if (!strcmp (optarg, "oldest"))
755 mc_options_set_queue_limit_strategy (options, MC_DROP_OLDEST);
756 else if (!strcmp (optarg, "random"))
757 mc_options_set_queue_limit_strategy (options, MC_DROP_RANDOM);
759 error (1, 0, "--queue-drop argument must be \"newest\", "
760 "\"oldest\", or \"random\"");
764 mc_options_set_seed (options, atoi (optarg));
768 mc_options_set_max_unique_states (options, atoi (optarg));
772 mc_options_set_max_errors (options, atoi (optarg));
776 mc_options_set_time_limit (options, atof (optarg));
780 if (!strcmp (optarg, "none"))
781 mc_options_set_progress_usec (options, 0);
782 else if (!strcmp (optarg, "dots"))
783 mc_options_set_progress_func (options, mc_progress_dots);
784 else if (!strcmp (optarg, "fancy"))
785 mc_options_set_progress_func (options, mc_progress_fancy);
786 else if (!strcmp (optarg, "verbose"))
787 mc_options_set_progress_func (options, mc_progress_verbose);
791 mc_options_set_verbosity (options, atoi (optarg));
794 case OPT_FAILURE_VERBOSITY:
795 mc_options_set_failure_verbosity (options, atoi (optarg));
803 /* Adds the model checker command line options to the specified
804 command line PARSER. Model checker options parsed from the
805 command line will be applied to OPTIONS. */
807 mc_options_register_argv_parser (struct mc_options *options,
808 struct argv_parser *parser)
810 argv_parser_add_options (parser, mc_argv_options, N_MC_ARGV_OPTIONS,
811 mc_parser_option_callback, options);
814 /* Prints a reference for the model checker command line options
817 mc_options_usage (void)
820 "\nModel checker search algorithm options:\n"
821 " --strategy=STRATEGY Basic search strategy. One of:\n"
822 " broad: breadth-first search (default)\n"
823 " deep: depth-first search\n"
824 " random: randomly ordered search\n"
825 " --path=#[,#]... Fixes the exact search path to follow;\n"
826 " mutually exclusive with --strategy\n"
827 " --max-depth=MAX Limits search depth to MAX. The initial\n"
828 " states are at depth 1.\n"
829 " --hash-bits=BITS Use 2**BITS size hash table to avoid\n"
830 " duplicate states (0 will disable hashing)\n"
831 " --seed=SEED Sets the random number seed\n"
832 "\nModel checker queuing options:\n"
833 " --queue-limit=N Limit queue to N states (default: 10000)\n"
834 " --queue-drop=TYPE How to drop states when queue overflows:\n"
835 " newest: drop most recently added state\n"
836 " oldest: drop least recently added state\n"
837 " random (default): drop a random state\n"
838 "\nModel checker stop condition options:\n"
839 " --max-states=N Stop after visiting N unique states\n"
840 " --max-errors=N Stop after N errors (default: 1)\n"
841 " --time-limit=SECS Stop after SECS seconds\n"
842 "\nModel checker user interface options:\n"
843 " --progress=TYPE Show progress according to TYPE. One of:\n"
844 " none: Do not output progress message\n"
845 " dots (default): Output lines of dots\n"
846 " fancy: Show a few stats\n"
847 " verbose: Show all available stats\n"
848 " --verbosity=LEVEL Verbosity level before an error (default: 1)\n"
849 " --failure-verbosity=LEVEL Verbosity level for replaying failure\n"
850 " cases (default: 2)\n",
854 /* Results of a model checking run. */
857 /* Overall results. */
858 enum mc_stop_reason stop_reason; /* Why the run ended. */
859 int unique_state_count; /* Number of unique states checked. */
860 int error_count; /* Number of errors found. */
862 /* Depth statistics. */
863 int max_depth_reached; /* Max depth state examined. */
864 unsigned long int depth_sum; /* Sum of depths. */
865 int n_depths; /* Number of depths in depth_sum. */
867 /* If error_count > 0, path to the last error reported. */
868 struct mc_path error_path;
870 /* States dropped... */
871 int duplicate_dropped_states; /* ...as duplicates. */
872 int off_path_dropped_states; /* ...as off-path (MC_PATH only). */
873 int depth_dropped_states; /* ...due to excessive depth. */
874 int queue_dropped_states; /* ...due to queue overflow. */
876 /* Queue statistics. */
877 int queued_unprocessed_states; /* Enqueued but never dequeued. */
878 int max_queue_length; /* Maximum queue length observed. */
881 struct timeval start; /* Start of model checking run. */
882 struct timeval end; /* End of model checking run. */
885 /* Creates, initializes, and returns a new set of results. */
886 static struct mc_results *
887 mc_results_create (void)
889 struct mc_results *results = xcalloc (1, sizeof (struct mc_results));
890 results->stop_reason = MC_CONTINUING;
891 gettimeofday (&results->start, NULL);
895 /* Destroys RESULTS. */
897 mc_results_destroy (struct mc_results *results)
901 mc_path_destroy (&results->error_path);
906 /* Returns RESULTS's reason that the model checking run
907 terminated. The possible reasons are:
909 - MC_CONTINUING: The run is not actually yet complete. This
910 can only be returned before mc_run has returned, e.g. when
911 the progress function set by mc_options_set_progress_func
912 examines the run's results.
914 - MC_SUCCESS: The run completed because the queue emptied.
915 The entire state space might not have been explored due to a
916 requested limit on maximum depth, hash collisions, etc.
918 - MC_MAX_UNIQUE_STATES: The run completed because as many
919 unique states have been checked as were requested (using
920 mc_options_set_max_unique_states).
922 - MC_MAX_ERROR_COUNT: The run completed because the maximum
923 requested number of errors (by default, 1 error) was
926 - MC_END_OF_PATH: The run completed because the path specified
927 with mc_options_set_follow_path was fully traversed.
929 - MC_TIMEOUT: The run completed because the time limit set
930 with mc_options_set_time_limit was exceeded.
932 - MC_INTERRUPTED: The run completed because SIGINT was caught
933 (typically, due to the user typing Ctrl+C). */
935 mc_results_get_stop_reason (const struct mc_results *results)
937 return results->stop_reason;
940 /* Returns the number of unique states checked specified by
943 mc_results_get_unique_state_count (const struct mc_results *results)
945 return results->unique_state_count;
948 /* Returns the number of errors found specified by RESULTS. */
950 mc_results_get_error_count (const struct mc_results *results)
952 return results->error_count;
955 /* Returns the maximum depth reached during the model checker run
956 represented by RESULTS. The initial states are at depth 1,
957 their child states at depth 2, and so on. */
959 mc_results_get_max_depth_reached (const struct mc_results *results)
961 return results->max_depth_reached;
964 /* Returns the mean depth reached during the model checker run
965 represented by RESULTS. */
967 mc_results_get_mean_depth_reached (const struct mc_results *results)
969 return (results->n_depths == 0
971 : (double) results->depth_sum / results->n_depths);
974 /* Returns the path traversed to obtain the last error
975 encountered during the model checker run represented by
976 RESULTS. Returns a null pointer if the run did not report any
978 const struct mc_path *
979 mc_results_get_error_path (const struct mc_results *results)
981 return results->error_count > 0 ? &results->error_path : NULL;
984 /* Returns the number of states dropped as duplicates (based on
985 hash value) during the model checker run represented by
988 mc_results_get_duplicate_dropped_states (const struct mc_results *results)
990 return results->duplicate_dropped_states;
993 /* Returns the number of states dropped because they were off the
994 path specified by mc_options_set_follow_path during the model
995 checker run represented by RESULTS. A nonzero value here
996 indicates a missing call to mc_include_state in the
997 client-supplied mutation function. */
999 mc_results_get_off_path_dropped_states (const struct mc_results *results)
1001 return results->off_path_dropped_states;
1004 /* Returns the number of states dropped because their depth
1005 exceeded the maximum specified with mc_options_set_max_depth
1006 during the model checker run represented by RESULTS. */
1008 mc_results_get_depth_dropped_states (const struct mc_results *results)
1010 return results->depth_dropped_states;
1013 /* Returns the number of states dropped from the queue due to
1014 queue overflow during the model checker run represented by
1017 mc_results_get_queue_dropped_states (const struct mc_results *results)
1019 return results->queue_dropped_states;
1022 /* Returns the number of states that were checked and enqueued
1023 but never dequeued and processed during the model checker run
1024 represented by RESULTS. This is zero if the stop reason is
1025 MC_CONTINUING or MC_SUCCESS; otherwise, it is the number of
1026 states in the queue at the time that the checking run
1029 mc_results_get_queued_unprocessed_states (const struct mc_results *results)
1031 return results->queued_unprocessed_states;
1034 /* Returns the maximum length of the queue during the model
1035 checker run represented by RESULTS. If this is equal to the
1036 maximum queue length, then the queue (probably) overflowed
1037 during the run; otherwise, it did not overflow. */
1039 mc_results_get_max_queue_length (const struct mc_results *results)
1041 return results->max_queue_length;
1044 /* Returns the time at which the model checker run represented by
1047 mc_results_get_start (const struct mc_results *results)
1049 return results->start;
1052 /* Returns the time at which the model checker run represented by
1053 RESULTS ended. (This function may not be called while the run
1054 is still ongoing.) */
1056 mc_results_get_end (const struct mc_results *results)
1058 assert (results->stop_reason != MC_CONTINUING);
1059 return results->end;
1062 /* Returns the number of seconds obtained by subtracting time Y
1065 timeval_subtract (struct timeval x, struct timeval y)
1067 /* From libc.info. */
1070 /* Perform the carry for the later subtraction by updating Y. */
1071 if (x.tv_usec < y.tv_usec) {
1072 int nsec = (y.tv_usec - x.tv_usec) / 1000000 + 1;
1073 y.tv_usec -= 1000000 * nsec;
1076 if (x.tv_usec - y.tv_usec > 1000000) {
1077 int nsec = (x.tv_usec - y.tv_usec) / 1000000;
1078 y.tv_usec += 1000000 * nsec;
1082 /* Compute the time remaining to wait.
1083 `tv_usec' is certainly positive. */
1084 difference = (x.tv_sec - y.tv_sec) + (x.tv_usec - y.tv_usec) / 1000000.0;
1085 if (x.tv_sec < y.tv_sec)
1086 difference = -difference;
1091 /* Returns the duration, in seconds, of the model checker run
1092 represented by RESULTS. (This function may not be called
1093 while the run is still ongoing.) */
1095 mc_results_get_duration (const struct mc_results *results)
1097 assert (results->stop_reason != MC_CONTINUING);
1098 return timeval_subtract (results->end, results->start);
1101 /* Prints a description of RESULTS to stream F. */
1103 mc_results_print (const struct mc_results *results, FILE *f)
1105 enum mc_stop_reason reason = mc_results_get_stop_reason (results);
1107 if (reason != MC_CONTINUING)
1108 fprintf (f, "Stopped by: %s\n",
1109 reason == MC_SUCCESS ? "state space exhaustion"
1110 : reason == MC_MAX_UNIQUE_STATES ? "reaching max unique states"
1111 : reason == MC_MAX_ERROR_COUNT ? "reaching max error count"
1112 : reason == MC_END_OF_PATH ? "reached end of specified path"
1113 : reason == MC_TIMEOUT ? "reaching time limit"
1114 : reason == MC_INTERRUPTED ? "user interruption"
1115 : "unknown reason");
1116 fprintf (f, "Errors found: %d\n\n", mc_results_get_error_count (results));
1118 fprintf (f, "Unique states checked: %d\n",
1119 mc_results_get_unique_state_count (results));
1120 fprintf (f, "Maximum depth reached: %d\n",
1121 mc_results_get_max_depth_reached (results));
1122 fprintf (f, "Mean depth reached: %.2f\n\n",
1123 mc_results_get_mean_depth_reached (results));
1125 fprintf (f, "Dropped duplicate states: %d\n",
1126 mc_results_get_duplicate_dropped_states (results));
1127 fprintf (f, "Dropped off-path states: %d\n",
1128 mc_results_get_off_path_dropped_states (results));
1129 fprintf (f, "Dropped too-deep states: %d\n",
1130 mc_results_get_depth_dropped_states (results));
1131 fprintf (f, "Dropped queue-overflow states: %d\n",
1132 mc_results_get_queue_dropped_states (results));
1133 fprintf (f, "Checked states still queued when stopped: %d\n",
1134 mc_results_get_queued_unprocessed_states (results));
1135 fprintf (f, "Maximum queue length reached: %d\n",
1136 mc_results_get_max_queue_length (results));
1138 if (reason != MC_CONTINUING)
1139 fprintf (f, "\nRuntime: %.2f seconds\n",
1140 mc_results_get_duration (results));
1143 /* An active model checking run. */
1146 /* Related data structures. */
1147 const struct mc_class *class;
1148 struct mc_options *options;
1149 struct mc_results *results;
1151 /* Array of 2**(options->hash_bits) bits representing states
1153 unsigned char *hash;
1156 struct mc_state **queue; /* Array of pointers to states. */
1157 struct deque queue_deque; /* Deque. */
1159 /* State currently being built by "init" or "mutate". */
1160 struct mc_path path; /* Path to current state. */
1161 struct string path_string; /* Buffer for path_string function. */
1162 bool state_named; /* mc_name_operation called? */
1163 bool state_error; /* mc_error called? */
1165 /* Statistics for calling the progress function. */
1166 unsigned int progress; /* Current progress value. */
1167 unsigned int next_progress; /* Next value to call progress func. */
1168 unsigned int prev_progress; /* Last value progress func called. */
1169 struct timeval prev_progress_time; /* Last time progress func called. */
1171 /* Information for handling and restoring SIGINT. */
1172 bool interrupted; /* SIGINT received? */
1173 bool *saved_interrupted_ptr; /* Saved value of interrupted_ptr. */
1174 void (*saved_sigint) (int); /* Saved SIGINT handler. */
1177 /* A state in the queue. */
1180 struct mc_path path; /* Path to this state. */
1181 void *data; /* Client-supplied data. */
1184 /* Points to the current struct mc's "interrupted" member. */
1185 static bool *interrupted_ptr = NULL;
1187 static const char *path_string (struct mc *);
1188 static void free_state (const struct mc *, struct mc_state *);
1189 static void stop (struct mc *, enum mc_stop_reason);
1190 static struct mc_state *make_state (const struct mc *, void *);
1191 static size_t random_queue_index (struct mc *);
1192 static void enqueue_state (struct mc *, struct mc_state *);
1193 static void do_error_state (struct mc *);
1194 static void next_operation (struct mc *);
1195 static bool is_off_path (const struct mc *);
1196 static void sigint_handler (int signum);
1197 static void init_mc (struct mc *,
1198 const struct mc_class *, struct mc_options *);
1199 static void finish_mc (struct mc *);
1201 /* Runs the model checker on the client-specified CLASS with the
1202 client-specified OPTIONS. OPTIONS may be a null pointer if
1203 the defaults are acceptable. Destroys OPTIONS; use
1204 mc_options_clone if a copy is needed.
1206 Returns the results of the model checking run, which must be
1207 destroyed by the client with mc_results_destroy.
1209 To pass auxiliary data to the functions in CLASS, use
1210 mc_options_set_aux on OPTIONS, which may be retrieved from the
1211 CLASS functions using mc_get_aux. */
1213 mc_run (const struct mc_class *class, struct mc_options *options)
1217 init_mc (&mc, class, options);
1218 while (!deque_is_empty (&mc.queue_deque)
1219 && mc.results->stop_reason == MC_CONTINUING)
1221 struct mc_state *state = mc.queue[deque_pop_front (&mc.queue_deque)];
1222 mc_path_copy (&mc.path, &state->path);
1223 mc_path_push (&mc.path, 0);
1224 class->mutate (&mc, state->data);
1225 free_state (&mc, state);
1227 stop (&mc, MC_INTERRUPTED);
1234 /* Tests whether the current operation is one that should be
1235 performed, checked, and enqueued. If so, returns true.
1236 Otherwise, returns false and, unless checking is stopped,
1237 advances to the next state. The caller should then advance
1238 to the next operation.
1240 This function should be called from the client-provided
1241 "mutate" function, according to the pattern explained in the
1242 big comment at the top of model-checker.h. */
1244 mc_include_state (struct mc *mc)
1246 if (mc->results->stop_reason != MC_CONTINUING)
1248 else if (is_off_path (mc))
1250 next_operation (mc);
1257 /* Tests whether HASH represents a state that has (probably)
1258 already been enqueued. If not, returns false and marks HASH
1259 so that it will be treated as a duplicate in the future. If
1260 so, returns true and advances to the next state. The
1261 caller should then advance to the next operation.
1263 This function should be called from the client-provided
1264 "mutate" function, according to the pattern explained in the
1265 big comment at the top of model-checker.h. */
1267 mc_discard_dup_state (struct mc *mc, unsigned int hash)
1269 if (!mc->state_error && mc->options->hash_bits > 0)
1271 hash &= (1u << mc->options->hash_bits) - 1;
1272 if (TEST_BIT (mc->hash, hash))
1274 if (mc->options->verbosity > 2)
1275 fprintf (mc->options->output_file,
1276 " [%s] discard duplicate state\n", path_string (mc));
1277 mc->results->duplicate_dropped_states++;
1278 next_operation (mc);
1281 SET_BIT (mc->hash, hash);
1286 /* Names the current state NAME, which may contain
1287 printf-style format specifications. NAME should be a
1288 human-readable name for the current operation.
1290 This function should be called from the client-provided
1291 "mutate" function, according to the pattern explained in the
1292 big comment at the top of model-checker.h. */
1294 mc_name_operation (struct mc *mc, const char *name, ...)
1298 va_start (args, name);
1299 mc_vname_operation (mc, name, args);
1303 /* Names the current state NAME, which may contain
1304 printf-style format specifications, for which the
1305 corresponding arguments must be given in ARGS. NAME should be
1306 a human-readable name for the current operation.
1308 This function should be called from the client-provided
1309 "mutate" function, according to the pattern explained in the
1310 big comment at the top of model-checker.h. */
1312 mc_vname_operation (struct mc *mc, const char *name, va_list args)
1314 if (mc->state_named && mc->options->verbosity > 0)
1315 fprintf (mc->options->output_file, " [%s] warning: duplicate call "
1316 "to mc_name_operation (missing call to mc_add_state?)\n",
1318 mc->state_named = true;
1320 if (mc->options->verbosity > 1)
1322 fprintf (mc->options->output_file, " [%s] ", path_string (mc));
1323 vfprintf (mc->options->output_file, name, args);
1324 putc ('\n', mc->options->output_file);
1328 /* Reports the given error MESSAGE for the current operation.
1329 The resulting state should still be passed to mc_add_state
1330 when all relevant error messages have been issued. The state
1331 will not, however, be enqueued for later mutation of its own.
1333 By default, model checking stops after the first error
1336 This function should be called from the client-provided
1337 "mutate" function, according to the pattern explained in the
1338 big comment at the top of model-checker.h. */
1340 mc_error (struct mc *mc, const char *message, ...)
1344 if (mc->results->stop_reason != MC_CONTINUING)
1347 if (mc->options->verbosity > 1)
1348 fputs (" ", mc->options->output_file);
1349 fprintf (mc->options->output_file, "[%s] error: ",
1351 va_start (args, message);
1352 vfprintf (mc->options->output_file, message, args);
1354 putc ('\n', mc->options->output_file);
1356 mc->state_error = true;
1359 /* Enqueues DATA as the state corresponding to the current
1360 operation. The operation should have been named with a call
1361 to mc_name_operation, and it should have been checked by the
1362 caller (who should have reported any errors with mc_error).
1364 This function should be called from the client-provided
1365 "mutate" function, according to the pattern explained in the
1366 big comment at the top of model-checker.h. */
1368 mc_add_state (struct mc *mc, void *data)
1370 if (!mc->state_named && mc->options->verbosity > 0)
1371 fprintf (mc->options->output_file, " [%s] warning: unnamed state\n",
1374 if (mc->results->stop_reason != MC_CONTINUING)
1376 /* Nothing to do. */
1378 else if (mc->state_error)
1379 do_error_state (mc);
1380 else if (is_off_path (mc))
1381 mc->results->off_path_dropped_states++;
1382 else if (mc->path.length + 1 > mc->options->max_depth)
1383 mc->results->depth_dropped_states++;
1386 /* This is the common case. */
1387 mc->results->unique_state_count++;
1388 if (mc->results->unique_state_count >= mc->options->max_unique_states)
1389 stop (mc, MC_MAX_UNIQUE_STATES);
1390 enqueue_state (mc, make_state (mc, data));
1391 next_operation (mc);
1395 mc->class->destroy (mc, data);
1396 next_operation (mc);
1399 /* Returns the options that were passed to mc_run for model
1401 const struct mc_options *
1402 mc_get_options (const struct mc *mc)
1407 /* Returns the current state of the results for model checker
1408 MC. This function is appropriate for use from the progress
1409 function set by mc_options_set_progress_func.
1411 Not all of the results are meaningful before model checking
1413 const struct mc_results *
1414 mc_get_results (const struct mc *mc)
1419 /* Returns the auxiliary data set on the options passed to mc_run
1420 with mc_options_set_aux. */
1422 mc_get_aux (const struct mc *mc)
1424 return mc_options_get_aux (mc_get_options (mc));
1427 /* Expresses MC->path as a string and returns the string. */
1429 path_string (struct mc *mc)
1431 ds_clear (&mc->path_string);
1432 mc_path_to_string (&mc->path, &mc->path_string);
1433 return ds_cstr (&mc->path_string);
1436 /* Frees STATE, including client data. */
1438 free_state (const struct mc *mc, struct mc_state *state)
1440 mc->class->destroy (mc, state->data);
1441 mc_path_destroy (&state->path);
1445 /* Sets STOP_REASON as the reason that MC's processing stopped,
1446 unless MC is already stopped. */
1448 stop (struct mc *mc, enum mc_stop_reason stop_reason)
1450 if (mc->results->stop_reason == MC_CONTINUING)
1451 mc->results->stop_reason = stop_reason;
1454 /* Creates and returns a new state whose path is copied from
1455 MC->path and whose data is specified by DATA. */
1456 static struct mc_state *
1457 make_state (const struct mc *mc, void *data)
1459 struct mc_state *new = xmalloc (sizeof *new);
1460 mc_path_init (&new->path);
1461 mc_path_copy (&new->path, &mc->path);
1466 /* Returns the index in MC->queue of a random element in the
1469 random_queue_index (struct mc *mc)
1471 assert (!deque_is_empty (&mc->queue_deque));
1472 return deque_front (&mc->queue_deque,
1473 rand () % deque_count (&mc->queue_deque));
1476 /* Adds NEW to MC's state queue, dropping a state if necessary
1479 enqueue_state (struct mc *mc, struct mc_state *new)
1483 if (new->path.length > mc->results->max_depth_reached)
1484 mc->results->max_depth_reached = new->path.length;
1485 mc->results->depth_sum += new->path.length;
1486 mc->results->n_depths++;
1488 if (deque_count (&mc->queue_deque) < mc->options->queue_limit)
1490 /* Add new state to queue. */
1491 if (deque_is_full (&mc->queue_deque))
1492 mc->queue = deque_expand (&mc->queue_deque,
1493 mc->queue, sizeof *mc->queue);
1494 switch (mc->options->strategy)
1497 idx = deque_push_back (&mc->queue_deque);
1500 idx = deque_push_front (&mc->queue_deque);
1503 if (!deque_is_empty (&mc->queue_deque))
1505 idx = random_queue_index (mc);
1506 mc->queue[deque_push_front (&mc->queue_deque)]
1510 idx = deque_push_front (&mc->queue_deque);
1513 assert (deque_is_empty (&mc->queue_deque));
1514 assert (!is_off_path (mc));
1515 idx = deque_push_back (&mc->queue_deque);
1517 >= mc_path_get_length (&mc->options->follow_path))
1518 stop (mc, MC_END_OF_PATH);
1523 if (deque_count (&mc->queue_deque) > mc->results->max_queue_length)
1524 mc->results->max_queue_length = deque_count (&mc->queue_deque);
1528 /* Queue has reached limit, so replace an existing
1530 assert (mc->options->strategy != MC_PATH);
1531 assert (!deque_is_empty (&mc->queue_deque));
1532 mc->results->queue_dropped_states++;
1533 switch (mc->options->queue_limit_strategy)
1535 case MC_DROP_NEWEST:
1536 free_state (mc, new);
1538 case MC_DROP_OLDEST:
1539 switch (mc->options->strategy)
1542 idx = deque_front (&mc->queue_deque, 0);
1545 idx = deque_back (&mc->queue_deque, 0);
1553 case MC_DROP_RANDOM:
1554 idx = random_queue_index (mc);
1559 free_state (mc, mc->queue[idx]);
1561 mc->queue[idx] = new;
1564 /* Process an error state being added to MC. */
1566 do_error_state (struct mc *mc)
1568 mc->results->error_count++;
1569 if (mc->results->error_count >= mc->options->max_errors)
1570 stop (mc, MC_MAX_ERROR_COUNT);
1572 mc_path_copy (&mc->results->error_path, &mc->path);
1574 if (mc->options->failure_verbosity > mc->options->verbosity)
1576 struct mc_options *path_options;
1578 fprintf (mc->options->output_file, "[%s] retracing error path:\n",
1580 path_options = mc_options_clone (mc->options);
1581 mc_options_set_verbosity (path_options, mc->options->failure_verbosity);
1582 mc_options_set_failure_verbosity (path_options, 0);
1583 mc_options_set_follow_path (path_options, &mc->path);
1585 mc_results_destroy (mc_run (mc->class, path_options));
1587 putc ('\n', mc->options->output_file);
1591 /* Advances MC to start processing the operation following the
1594 next_operation (struct mc *mc)
1596 mc_path_push (&mc->path, mc_path_pop (&mc->path) + 1);
1597 mc->state_error = false;
1598 mc->state_named = false;
1600 if (++mc->progress >= mc->next_progress)
1603 double elapsed, delta;
1605 if (mc->results->stop_reason == MC_CONTINUING
1606 && !mc->options->progress_func (mc))
1607 stop (mc, MC_INTERRUPTED);
1609 gettimeofday (&now, NULL);
1611 if (mc->options->time_limit > 0.0
1612 && (timeval_subtract (now, mc->results->start)
1613 > mc->options->time_limit))
1614 stop (mc, MC_TIMEOUT);
1616 elapsed = timeval_subtract (now, mc->prev_progress_time);
1619 /* Re-estimate the amount of progress to take
1620 progress_usec microseconds. */
1621 unsigned int progress = mc->progress - mc->prev_progress;
1622 double progress_sec = mc->options->progress_usec / 1000000.0;
1623 delta = progress / elapsed * progress_sec;
1627 /* No measurable time at all elapsed during that amount
1628 of progress. Try doubling the amount of progress
1630 delta = (mc->progress - mc->prev_progress) * 2;
1633 if (delta > 0.0 && delta + mc->progress + 1.0 < UINT_MAX)
1634 mc->next_progress = mc->progress + delta + 1.0;
1636 mc->next_progress = mc->progress + (mc->progress - mc->prev_progress);
1638 mc->prev_progress = mc->progress;
1639 mc->prev_progress_time = now;
1643 /* Returns true if we're tracing an explicit path but the current
1644 operation produces a state off that path, false otherwise. */
1646 is_off_path (const struct mc *mc)
1648 return (mc->options->strategy == MC_PATH
1649 && (mc_path_back (&mc->path)
1650 != mc_path_get_operation (&mc->options->follow_path,
1651 mc->path.length - 1)));
1654 /* Handler for SIGINT. */
1656 sigint_handler (int signum UNUSED)
1658 /* Just mark the model checker as interrupted. */
1659 *interrupted_ptr = true;
1662 /* Initializes MC as a model checker with the given CLASS and
1663 OPTIONS. OPTIONS may be null to use the default options. */
1665 init_mc (struct mc *mc, const struct mc_class *class,
1666 struct mc_options *options)
1668 /* Validate and adjust OPTIONS. */
1669 if (options == NULL)
1670 options = mc_options_create ();
1671 assert (options->queue_limit_strategy != MC_DROP_OLDEST
1672 || options->strategy != MC_RANDOM);
1673 if (options->strategy == MC_PATH)
1675 options->max_depth = INT_MAX;
1676 options->hash_bits = 0;
1678 if (options->progress_usec == 0)
1680 options->progress_func = null_progress;
1681 if (options->time_limit > 0.0)
1682 options->progress_usec = 250000;
1685 /* Initialize MC. */
1687 mc->options = options;
1688 mc->results = mc_results_create ();
1690 mc->hash = (mc->options->hash_bits > 0
1691 ? xcalloc (1, DIV_RND_UP (1 << mc->options->hash_bits, CHAR_BIT))
1695 deque_init_null (&mc->queue_deque);
1697 mc_path_init (&mc->path);
1698 mc_path_push (&mc->path, 0);
1699 ds_init_empty (&mc->path_string);
1700 mc->state_named = false;
1701 mc->state_error = false;
1704 mc->next_progress = mc->options->progress_usec != 0 ? 100 : UINT_MAX;
1705 mc->prev_progress = 0;
1706 mc->prev_progress_time = mc->results->start;
1708 if (mc->options->strategy == MC_RANDOM
1709 || options->queue_limit_strategy == MC_DROP_RANDOM)
1710 srand (mc->options->seed);
1712 mc->interrupted = false;
1713 mc->saved_interrupted_ptr = interrupted_ptr;
1714 interrupted_ptr = &mc->interrupted;
1715 mc->saved_sigint = signal (SIGINT, sigint_handler);
1720 /* Complete the model checker run for MC. */
1722 finish_mc (struct mc *mc)
1724 /* Restore signal handlers. */
1725 signal (SIGINT, mc->saved_sigint);
1726 interrupted_ptr = mc->saved_interrupted_ptr;
1728 /* Mark the run complete. */
1729 stop (mc, MC_SUCCESS);
1730 gettimeofday (&mc->results->end, NULL);
1732 /* Empty the queue. */
1733 mc->results->queued_unprocessed_states = deque_count (&mc->queue_deque);
1734 while (!deque_is_empty (&mc->queue_deque))
1736 struct mc_state *state = mc->queue[deque_pop_front (&mc->queue_deque)];
1737 free_state (mc, state);
1740 /* Notify the progress function of completion. */
1741 mc->options->progress_func (mc);
1744 mc_path_destroy (&mc->path);
1745 ds_destroy (&mc->path_string);