GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/main/command_executor.cpp Lines: 74 97 76.3 %
Date: 2021-08-01 Branches: 106 190 55.8 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Kshitij Bansal, Andrew Reynolds, Morgan Deters
4
 *
5
 * This file is part of the cvc5 project.
6
 *
7
 * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS
8
 * in the top-level source directory and their institutional affiliations.
9
 * All rights reserved.  See the file COPYING in the top-level source
10
 * directory for licensing information.
11
 * ****************************************************************************
12
 *
13
 * An additional layer between commands and invoking them.
14
 */
15
16
#include "main/command_executor.h"
17
18
#ifndef __WIN32__
19
#  include <sys/resource.h>
20
#endif /* ! __WIN32__ */
21
22
#include <iomanip>
23
#include <iostream>
24
#include <memory>
25
#include <string>
26
#include <vector>
27
28
#include "main/main.h"
29
#include "options/base_options.h"
30
#include "options/main_options.h"
31
#include "smt/command.h"
32
33
namespace cvc5 {
34
namespace main {
35
36
// Function to cancel any (externally-imposed) limit on CPU time.
37
// This is used for competitions while a solution (proof or model)
38
// is being dumped (so that we don't give "sat" or "unsat" then get
39
// interrupted partway through outputting a proof!).
40
void setNoLimitCPU() {
41
  // Windows doesn't have these things, just ignore
42
#ifndef __WIN32__
43
  struct rlimit rlc;
44
  int st = getrlimit(RLIMIT_CPU, &rlc);
45
  if(st == 0) {
46
    rlc.rlim_cur = rlc.rlim_max;
47
    setrlimit(RLIMIT_CPU, &rlc);
48
  }
49
#endif /* ! __WIN32__ */
50
}
51
52
6122
CommandExecutor::CommandExecutor(const Options& options)
53
6123
    : d_solver(new api::Solver(&options)),
54
6121
      d_symman(new SymbolManager(d_solver.get())),
55
      d_driverOptions(&options),
56
18364
      d_result()
57
{
58
6121
}
59
18363
CommandExecutor::~CommandExecutor()
60
{
61
  // ensure that symbol manager is destroyed before solver
62
6121
  d_symman.reset(nullptr);
63
6121
  d_solver.reset(nullptr);
64
12242
}
65
66
6101
void CommandExecutor::printStatistics(std::ostream& out) const
67
{
68
6101
  if (d_solver->getOptions().base.statistics)
69
  {
70
    out << d_solver->getStatistics();
71
  }
72
6101
}
73
74
void CommandExecutor::printStatisticsSafe(int fd) const
75
{
76
  if (d_solver->getOptions().base.statistics)
77
  {
78
    d_solver->printStatisticsSafe(fd);
79
  }
80
}
81
82
306537
bool CommandExecutor::doCommand(Command* cmd)
83
{
84
306537
  if (d_solver->getOptions().base.parseOnly)
85
  {
86
    return true;
87
  }
88
89
306537
  CommandSequence *seq = dynamic_cast<CommandSequence*>(cmd);
90
306537
  if(seq != nullptr) {
91
    // assume no error
92
5080
    bool status = true;
93
94
12967
    for (CommandSequence::iterator subcmd = seq->begin();
95
12967
         status && subcmd != seq->end();
96
         ++subcmd)
97
    {
98
7887
      status = doCommand(*subcmd);
99
    }
100
101
5080
    return status;
102
  } else {
103
301457
    if (d_solver->getOptions().base.verbosity > 2)
104
    {
105
11
      *d_solver->getOptions().base.out << "Invoking: " << *cmd << std::endl;
106
    }
107
108
301457
    return doCommandSingleton(cmd);
109
  }
110
}
111
112
void CommandExecutor::reset()
113
{
114
  printStatistics(*d_solver->getOptions().base.err);
115
  /* We have to keep options passed via CL on reset. These options are stored
116
   * in CommandExecutor::d_driverOptions (populated and created in the driver),
117
   * and CommandExecutor::d_driverOptions only contains *these* options since
118
   * the SmtEngine copies them into its own options object before configuring
119
   * additional options based on the given CL options.
120
   * We can thus safely reuse CommandExecutor::d_driverOptions here.
121
   */
122
  d_solver.reset(new api::Solver(d_driverOptions));
123
}
124
125
301485
bool CommandExecutor::doCommandSingleton(Command* cmd)
126
{
127
301485
  bool status = true;
128
301485
  if (d_solver->getOptions().base.verbosity >= -1)
129
  {
130
301485
    status = solverInvoke(
131
301485
        d_solver.get(), d_symman.get(), cmd, d_solver->getOptions().base.out);
132
  }
133
  else
134
  {
135
    status = solverInvoke(d_solver.get(), d_symman.get(), cmd, nullptr);
136
  }
137
138
602970
  api::Result res;
139
301485
  const CheckSatCommand* cs = dynamic_cast<const CheckSatCommand*>(cmd);
140
301485
  if(cs != nullptr) {
141
9190
    d_result = res = cs->getResult();
142
  }
143
  const CheckSatAssumingCommand* csa =
144
301485
      dynamic_cast<const CheckSatAssumingCommand*>(cmd);
145
301485
  if (csa != nullptr)
146
  {
147
990
    d_result = res = csa->getResult();
148
  }
149
301485
  const QueryCommand* q = dynamic_cast<const QueryCommand*>(cmd);
150
301485
  if(q != nullptr) {
151
609
    d_result = res = q->getResult();
152
  }
153
154
301485
  bool isResultUnsat = res.isUnsat() || res.isEntailed();
155
156
  // dump the model/proof/unsat core if option is set
157
301485
  if (status) {
158
602894
    std::vector<std::unique_ptr<Command> > getterCommands;
159
602894
    if (d_solver->getOptions().driver.dumpModels
160
301457
        && (res.isSat()
161
34
            || (res.isSatUnknown()
162
                && res.getUnknownExplanation() == api::Result::INCOMPLETE)))
163
    {
164
10
      getterCommands.emplace_back(new GetModelCommand());
165
    }
166
301447
    if (d_solver->getOptions().driver.dumpProofs && isResultUnsat)
167
    {
168
      getterCommands.emplace_back(new GetProofCommand());
169
    }
170
171
602894
    if (d_solver->getOptions().driver.dumpInstantiations
172
301447
        && GetInstantiationsCommand::isEnabled(d_solver.get(), res))
173
    {
174
15
      getterCommands.emplace_back(new GetInstantiationsCommand());
175
    }
176
177
602894
    if ((d_solver->getOptions().driver.dumpUnsatCores
178
301438
         || d_solver->getOptions().driver.dumpUnsatCoresFull)
179
301471
        && isResultUnsat)
180
    {
181
3
      getterCommands.emplace_back(new GetUnsatCoreCommand());
182
    }
183
184
301447
    if (!getterCommands.empty()) {
185
      // set no time limit during dumping if applicable
186
28
      if (d_solver->getOptions().driver.forceNoLimitCpuWhileDump)
187
      {
188
        setNoLimitCPU();
189
      }
190
56
      for (const auto& getterCommand : getterCommands) {
191
28
        status = doCommandSingleton(getterCommand.get());
192
28
        if (!status)
193
        {
194
          break;
195
        }
196
      }
197
    }
198
  }
199
602970
  return status;
200
}
201
202
301485
bool solverInvoke(api::Solver* solver,
203
                  SymbolManager* sm,
204
                  Command* cmd,
205
                  std::ostream* out)
206
{
207
301485
  if (out == NULL)
208
  {
209
    cmd->invoke(solver, sm);
210
  }
211
  else
212
  {
213
301485
    cmd->invoke(solver, sm, *out);
214
  }
215
  // ignore the error if the command-verbosity is 0 for this command
216
  std::string commandName =
217
602970
      std::string("command-verbosity:") + cmd->getCommandName();
218
301485
  if (solver->getOption(commandName) == "0")
219
  {
220
    return true;
221
  }
222
301485
  return !cmd->fail();
223
}
224
225
6101
void CommandExecutor::flushOutputStreams() {
226
6101
  printStatistics(*d_solver->getOptions().base.err);
227
228
  // make sure out and err streams are flushed too
229
230
6101
  if (d_solver->getOptions().base.out != nullptr)
231
  {
232
6101
    *d_solver->getOptions().base.out << std::flush;
233
  }
234
6101
  if (d_solver->getOptions().base.err != nullptr)
235
  {
236
6101
    *d_solver->getOptions().base.err << std::flush;
237
  }
238
6101
}
239
240
}  // namespace main
241
29259
}  // namespace cvc5