ltlcross: recover from out-of-memory during state-space product
Fixes #96, reported by Vitus Lam & Christopher Ziegler. * src/bin/ltlcross.cc: Catch std::bad_alloc, skip those products and the related tests. Display a count of those skipped tests at the end. * NEWS: Mention it. * src/tests/ltlcross3.test: Adjust expected error message.
This commit is contained in:
parent
63969b13fa
commit
0510e4dfe8
3 changed files with 125 additions and 77 deletions
2
NEWS
2
NEWS
|
|
@ -74,6 +74,8 @@ New in spot 1.99.2a (not yet released)
|
||||||
- the spot.translate() function of the Python binding had a typo
|
- the spot.translate() function of the Python binding had a typo
|
||||||
preventing the use of 'low'/'medium'/'high' as argument.
|
preventing the use of 'low'/'medium'/'high' as argument.
|
||||||
- Fix spurious failure of uniq.test under different locales.
|
- Fix spurious failure of uniq.test under different locales.
|
||||||
|
- ltlcross now recovers from out-of-memory errors during
|
||||||
|
state-space products.
|
||||||
|
|
||||||
New in spot 1.99.2 (2015-07-18)
|
New in spot 1.99.2 (2015-07-18)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <argp.h>
|
#include <argp.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <cmath>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "argmatch.h"
|
#include "argmatch.h"
|
||||||
|
|
@ -208,6 +209,7 @@ static unsigned ignored_exec_fail = 0;
|
||||||
static bool opt_automata = false;
|
static bool opt_automata = false;
|
||||||
|
|
||||||
static bool global_error_flag = false;
|
static bool global_error_flag = false;
|
||||||
|
static unsigned oom_count = 0U;
|
||||||
|
|
||||||
static std::ostream&
|
static std::ostream&
|
||||||
global_error()
|
global_error()
|
||||||
|
|
@ -756,13 +758,14 @@ namespace
|
||||||
std::cerr << "info: cross_check {";
|
std::cerr << "info: cross_check {";
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (size_t i = 0; i < m; ++i)
|
for (size_t i = 0; i < m; ++i)
|
||||||
{
|
if (maps[i])
|
||||||
if (first)
|
{
|
||||||
first = false;
|
if (first)
|
||||||
else
|
first = false;
|
||||||
std::cerr << ',';
|
else
|
||||||
std::cerr << l << i;
|
std::cerr << ',';
|
||||||
}
|
std::cerr << l << i;
|
||||||
|
}
|
||||||
std::cerr << "}, state-space #" << p << '/' << products << '\n';
|
std::cerr << "}, state-space #" << p << '/' << products << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -958,6 +961,31 @@ namespace
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void product_stats(statistics_formula* stats, unsigned i,
|
||||||
|
spot::scc_info* sm)
|
||||||
|
{
|
||||||
|
if (verbose && sm)
|
||||||
|
std::cerr << "info: " << sm->scc_count()
|
||||||
|
<< " SCCs\n";
|
||||||
|
// Statistics
|
||||||
|
if (want_stats)
|
||||||
|
{
|
||||||
|
if (sm)
|
||||||
|
{
|
||||||
|
(*stats)[i].product_scc.push_back(sm->scc_count());
|
||||||
|
spot::tgba_statistics s = spot::stats_reachable(sm->get_aut());
|
||||||
|
(*stats)[i].product_states.push_back(s.states);
|
||||||
|
(*stats)[i].product_transitions.push_back(s.transitions);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double n = nan("");
|
||||||
|
(*stats)[i].product_scc.push_back(n);
|
||||||
|
(*stats)[i].product_states.push_back(n);
|
||||||
|
(*stats)[i].product_transitions.push_back(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
process_formula(const spot::ltl::formula* f,
|
process_formula(const spot::ltl::formula* f,
|
||||||
|
|
@ -1209,10 +1237,6 @@ namespace
|
||||||
<< statespace->num_edges()
|
<< statespace->num_edges()
|
||||||
<< " edges\n";
|
<< " edges\n";
|
||||||
|
|
||||||
// Products of the state space with the positive automata.
|
|
||||||
std::vector<spot::const_twa_graph_ptr> pos_prod(m);
|
|
||||||
// Products of the state space with the negative automata.
|
|
||||||
std::vector<spot::const_twa_graph_ptr> neg_prod(m);
|
|
||||||
// Associated SCC maps.
|
// Associated SCC maps.
|
||||||
std::vector<spot::scc_info*> pos_map(m);
|
std::vector<spot::scc_info*> pos_map(m);
|
||||||
std::vector<spot::scc_info*> neg_map(m);
|
std::vector<spot::scc_info*> neg_map(m);
|
||||||
|
|
@ -1224,27 +1248,28 @@ namespace
|
||||||
" P") << i
|
" P") << i
|
||||||
<< " (" << pos[i]->num_states() << " st., "
|
<< " (" << pos[i]->num_states() << " st., "
|
||||||
<< pos[i]->num_edges() << " ed.)\n";
|
<< pos[i]->num_edges() << " ed.)\n";
|
||||||
auto p = spot::product(pos[i], statespace);
|
|
||||||
pos_prod[i] = p;
|
|
||||||
if (verbose)
|
|
||||||
std::cerr << "info: product has " << p->num_states()
|
|
||||||
<< " st., " << p->num_edges()
|
|
||||||
<< " ed.\n";
|
|
||||||
|
|
||||||
auto sm = new spot::scc_info(p);
|
spot::scc_info* sm = nullptr;
|
||||||
pos_map[i] = sm;
|
try
|
||||||
if (verbose)
|
|
||||||
std::cerr << "info: " << sm->scc_count()
|
|
||||||
<< " SCCs\n";
|
|
||||||
|
|
||||||
// Statistics
|
|
||||||
if (want_stats)
|
|
||||||
{
|
{
|
||||||
(*pstats)[i].product_scc.push_back(sm->scc_count());
|
auto p = spot::product(pos[i], statespace);
|
||||||
spot::tgba_statistics s = spot::stats_reachable(p);
|
if (verbose)
|
||||||
(*pstats)[i].product_states.push_back(s.states);
|
std::cerr << "info: product has " << p->num_states()
|
||||||
(*pstats)[i].product_transitions.push_back(s.transitions);
|
<< " st., " << p->num_edges()
|
||||||
|
<< " ed.\n";
|
||||||
|
sm = new spot::scc_info(p);
|
||||||
}
|
}
|
||||||
|
catch (std::bad_alloc&)
|
||||||
|
{
|
||||||
|
std::cerr << ("warning: not enough memory to build "
|
||||||
|
"product of P") << i << " with state-space";
|
||||||
|
if (products > 1)
|
||||||
|
std::cerr << " #" << p << '/' << products << '\n';
|
||||||
|
std::cerr << '\n';
|
||||||
|
++oom_count;
|
||||||
|
}
|
||||||
|
pos_map[i] = sm;
|
||||||
|
product_stats(pstats, i, sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!no_checks)
|
if (!no_checks)
|
||||||
|
|
@ -1257,26 +1282,28 @@ namespace
|
||||||
<< " (" << neg[i]->num_states() << " st., "
|
<< " (" << neg[i]->num_states() << " st., "
|
||||||
<< neg[i]->num_edges() << " ed.)\n";
|
<< neg[i]->num_edges() << " ed.)\n";
|
||||||
|
|
||||||
auto p = spot::product(neg[i], statespace);
|
spot::scc_info* sm = nullptr;
|
||||||
neg_prod[i] = p;
|
try
|
||||||
if (verbose)
|
|
||||||
std::cerr << "info: product has " << p->num_states()
|
|
||||||
<< " st., " << p->num_edges()
|
|
||||||
<< " ed.\n";
|
|
||||||
|
|
||||||
auto sm = new spot::scc_info(p);
|
|
||||||
neg_map[i] = sm;
|
|
||||||
if (verbose)
|
|
||||||
std::cerr << "info: " << sm->scc_count()
|
|
||||||
<< " SCCs\n";
|
|
||||||
// Statistics
|
|
||||||
if (want_stats)
|
|
||||||
{
|
{
|
||||||
(*nstats)[i].product_scc.push_back(sm->scc_count());
|
auto p = spot::product(neg[i], statespace);
|
||||||
spot::tgba_statistics s = spot::stats_reachable(p);
|
if (verbose)
|
||||||
(*nstats)[i].product_states.push_back(s.states);
|
std::cerr << "info: product has " << p->num_states()
|
||||||
(*nstats)[i].product_transitions.push_back(s.transitions);
|
<< " st., " << p->num_edges()
|
||||||
|
<< " ed.\n";
|
||||||
|
sm = new spot::scc_info(p);
|
||||||
}
|
}
|
||||||
|
catch (std::bad_alloc&)
|
||||||
|
{
|
||||||
|
std::cerr << ("warning: not enough memory to build "
|
||||||
|
"product of N") << i << " with state-space";
|
||||||
|
if (products > 1)
|
||||||
|
std::cerr << " #" << p << '/' << products << '\n';
|
||||||
|
std::cerr << '\n';
|
||||||
|
++oom_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
neg_map[i] = sm;
|
||||||
|
product_stats(nstats, i, sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!no_checks)
|
if (!no_checks)
|
||||||
|
|
@ -1456,42 +1483,58 @@ main(int argc, char** argv)
|
||||||
" please search for 'error:' messages in the above"
|
" please search for 'error:' messages in the above"
|
||||||
" trace.");
|
" trace.");
|
||||||
err << std::endl;
|
err << std::endl;
|
||||||
if (timeout_count > 0 || ignored_exec_fail > 0)
|
|
||||||
{
|
|
||||||
err << "Additionally, ";
|
|
||||||
if (timeout_count == 1)
|
|
||||||
err << "1 timeout occurred";
|
|
||||||
else if (timeout_count > 1)
|
|
||||||
err << timeout_count << " timeouts occurred";
|
|
||||||
if (timeout_count > 0 && ignored_exec_fail > 0)
|
|
||||||
err << " and ";
|
|
||||||
if (ignored_exec_fail == 1)
|
|
||||||
err << "1 non-zero exit status was ignored";
|
|
||||||
if (ignored_exec_fail > 1)
|
|
||||||
err << ignored_exec_fail
|
|
||||||
<< " non-zero exit statuses were ignored";
|
|
||||||
err << '.' << std::endl;
|
|
||||||
}
|
|
||||||
end_error();
|
end_error();
|
||||||
}
|
}
|
||||||
else if (timeout_count == 0 && ignored_exec_fail == 0)
|
else if (timeout_count == 0 && ignored_exec_fail == 0 && oom_count == 0)
|
||||||
{
|
{
|
||||||
std::cerr << "No problem detected." << std::endl;
|
std::cerr << "No problem detected." << std::endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (timeout_count == 1)
|
std::cerr << "No major problem detected." << std::endl;
|
||||||
std::cerr << "1 timeout";
|
}
|
||||||
if (timeout_count > 1)
|
|
||||||
std::cerr << timeout_count << " timeouts";
|
unsigned additional_errors = 0U;
|
||||||
if (timeout_count > 0 && ignored_exec_fail > 0)
|
additional_errors += timeout_count > 0;
|
||||||
std::cerr << " and ";
|
additional_errors += ignored_exec_fail > 0;
|
||||||
if (ignored_exec_fail == 1)
|
additional_errors += oom_count > 0;
|
||||||
std::cerr << "1 non-zero exit status";
|
if (additional_errors)
|
||||||
if (ignored_exec_fail > 1)
|
{
|
||||||
std::cerr << ignored_exec_fail
|
std::cerr << (global_error_flag ? "Additionally, " : "However, ");
|
||||||
<< " non-zero exit statuses";
|
if (timeout_count)
|
||||||
std::cerr << ", but no other problem detected." << std::endl;
|
{
|
||||||
|
if (additional_errors > 1)
|
||||||
|
std::cerr << "\n - ";
|
||||||
|
if (timeout_count == 1)
|
||||||
|
std::cerr << "1 timeout occurred";
|
||||||
|
else
|
||||||
|
std::cerr << timeout_count << " timeouts occurred";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oom_count)
|
||||||
|
{
|
||||||
|
if (additional_errors > 1)
|
||||||
|
std::cerr << "\n - ";
|
||||||
|
if (oom_count == 1)
|
||||||
|
std::cerr << "1 state-space product was";
|
||||||
|
else
|
||||||
|
std::cerr << oom_count << "state-space products were";
|
||||||
|
std::cerr << " skipped by lack of memory";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ignored_exec_fail)
|
||||||
|
{
|
||||||
|
if (additional_errors > 1)
|
||||||
|
std::cerr << "\n - ";
|
||||||
|
if (ignored_exec_fail == 1)
|
||||||
|
std::cerr << "1 non-zero exit status was ignored";
|
||||||
|
else
|
||||||
|
std::cerr << ignored_exec_fail
|
||||||
|
<< " non-zero exit statuses were ignored";
|
||||||
|
}
|
||||||
|
if (additional_errors == 1)
|
||||||
|
std::cerr << '.';
|
||||||
|
std::cerr << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,10 @@ test `grep 'warning:.*timeout' stderr | wc -l` -eq 2
|
||||||
test `grep 'warning:.*exit code 1' stderr | wc -l` -eq 2
|
test `grep 'warning:.*exit code 1' stderr | wc -l` -eq 2
|
||||||
test `grep '"timeout",-1' out.csv | wc -l` -eq 2
|
test `grep '"timeout",-1' out.csv | wc -l` -eq 2
|
||||||
test `grep '"exit code",1' out.csv | wc -l` -eq 2
|
test `grep '"exit code",1' out.csv | wc -l` -eq 2
|
||||||
grep '2 timeouts and 2 non-zero exit statuses, but no other problem' stderr
|
|
||||||
|
grep 'No major problem detected' stderr
|
||||||
|
grep '2 timeouts occurred' stderr
|
||||||
|
grep '2 non-zero exit statuses were ignored' stderr
|
||||||
check_csv out.csv
|
check_csv out.csv
|
||||||
# 'bug' should exist but be empty
|
# 'bug' should exist but be empty
|
||||||
test -f bug
|
test -f bug
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue