1 /********************* */
2 /*! \file driver_unified.cpp
3 ** \verbatim
4 ** Top contributors (to current version):
5 ** Morgan Deters, Tim King, Liana Hadarean
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2019 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.\endverbatim
11 **
12 ** \brief Driver for CVC4 executable (cvc4) unified for both
13 ** sequential and portfolio versions
14 **/
15
16 #include <stdio.h>
17 #include <unistd.h>
18
19 #include <cstdlib>
20 #include <cstring>
21 #include <fstream>
22 #include <iostream>
23 #include <memory>
24 #include <new>
25
26 #include "cvc4autoconfig.h"
27
28 #include "api/cvc4cpp.h"
29 #include "base/configuration.h"
30 #include "base/output.h"
31 #include "expr/expr_iomanip.h"
32 #include "expr/expr_manager.h"
33 #include "main/command_executor.h"
34 #include "main/interactive_shell.h"
35 #include "main/main.h"
36 #include "options/options.h"
37 #include "options/set_language.h"
38 #include "parser/parser.h"
39 #include "parser/parser_builder.h"
40 #include "parser/parser_exception.h"
41 #include "smt/command.h"
42 #include "util/result.h"
43 #include "util/statistics_registry.h"
44
45 // The PORTFOLIO_BUILD is defined when compiling pcvc4 (the parallel version of
46 // CVC4) and undefined otherwise. The macro can only be used in
47 // driver_unified.cpp because we do not recompile all files for pcvc4.
48 #ifdef PORTFOLIO_BUILD
49 # include "main/command_executor_portfolio.h"
50 #endif
51
52 using namespace std;
53 using namespace CVC4;
54 using namespace CVC4::parser;
55 using namespace CVC4::main;
56
57 namespace CVC4 {
58 namespace main {
59 /** Global options variable */
60 thread_local Options* pOptions;
61
62 /** Full argv[0] */
63 const char *progPath;
64
65 /** Just the basename component of argv[0] */
66 const std::string *progName;
67
68 /** A pointer to the CommandExecutor (the signal handlers need it) */
69 CVC4::main::CommandExecutor* pExecutor = NULL;
70
71 /** A pointer to the totalTime driver stat (the signal handlers need it) */
72 CVC4::TimerStat* pTotalTime = NULL;
73
74 }/* CVC4::main namespace */
75 }/* CVC4 namespace */
76
77
printUsage(Options & opts,bool full)78 void printUsage(Options& opts, bool full) {
79 stringstream ss;
80 ss << "usage: " << opts.getBinaryName() << " [options] [input-file]"
81 << endl << endl
82 << "Without an input file, or with `-', CVC4 reads from standard input."
83 << endl << endl
84 << "CVC4 options:" << endl;
85 if(full) {
86 Options::printUsage( ss.str(), *(opts.getOut()) );
87 } else {
88 Options::printShortUsage( ss.str(), *(opts.getOut()) );
89 }
90 }
91
runCvc4(int argc,char * argv[],Options & opts)92 int runCvc4(int argc, char* argv[], Options& opts) {
93
94 // Timer statistic
95 pTotalTime = new TimerStat("totalTime");
96 pTotalTime->start();
97
98 // For the signal handlers' benefit
99 pOptions = &opts;
100
101 // Initialize the signal handlers
102 cvc4_init();
103
104 progPath = argv[0];
105
106 // Parse the options
107 vector<string> filenames = Options::parseOptions(&opts, argc, argv);
108
109 # ifndef PORTFOLIO_BUILD
110 if( opts.wasSetByUserThreads() ||
111 opts.wasSetByUserThreadStackSize() ||
112 (! opts.getThreadArgv().empty()) ) {
113 throw OptionException("Thread options cannot be used with sequential CVC4. Please build and use the portfolio binary `pcvc4'.");
114 }
115 # endif
116
117 string progNameStr = opts.getBinaryName();
118 progName = &progNameStr;
119
120 if( opts.getHelp() ) {
121 printUsage(opts, true);
122 exit(1);
123 } else if( opts.getLanguageHelp() ) {
124 Options::printLanguageHelp(*(opts.getOut()));
125 exit(1);
126 } else if( opts.getVersion() ) {
127 *(opts.getOut()) << Configuration::about().c_str() << flush;
128 exit(0);
129 }
130
131 segvSpin = opts.getSegvSpin();
132
133 // If in competition mode, set output stream option to flush immediately
134 #ifdef CVC4_COMPETITION_MODE
135 *(opts.getOut()) << unitbuf;
136 #endif /* CVC4_COMPETITION_MODE */
137
138 // We only accept one input file
139 if(filenames.size() > 1) {
140 throw Exception("Too many input files specified.");
141 }
142
143 // If no file supplied we will read from standard input
144 const bool inputFromStdin = filenames.empty() || filenames[0] == "-";
145
146 // if we're reading from stdin on a TTY, default to interactive mode
147 if(!opts.wasSetByUserInteractive()) {
148 opts.setInteractive(inputFromStdin && isatty(fileno(stdin)));
149 }
150
151 // Auto-detect input language by filename extension
152 std::string filenameStr("<stdin>");
153 if (!inputFromStdin) {
154 // Use swap to avoid copying the string
155 // TODO: use std::move() when switching to c++11
156 filenameStr.swap(filenames[0]);
157 }
158 const char* filename = filenameStr.c_str();
159
160 if(opts.getInputLanguage() == language::input::LANG_AUTO) {
161 if( inputFromStdin ) {
162 // We can't do any fancy detection on stdin
163 opts.setInputLanguage(language::input::LANG_CVC4);
164 } else {
165 unsigned len = filenameStr.size();
166 if(len >= 5 && !strcmp(".smt2", filename + len - 5)) {
167 opts.setInputLanguage(language::input::LANG_SMTLIB_V2_6);
168 } else if(len >= 4 && !strcmp(".smt", filename + len - 4)) {
169 opts.setInputLanguage(language::input::LANG_SMTLIB_V1);
170 } else if(len >= 5 && !strcmp(".smt1", filename + len - 5)) {
171 opts.setInputLanguage(language::input::LANG_SMTLIB_V1);
172 } else if((len >= 2 && !strcmp(".p", filename + len - 2))
173 || (len >= 5 && !strcmp(".tptp", filename + len - 5))) {
174 opts.setInputLanguage(language::input::LANG_TPTP);
175 } else if(( len >= 4 && !strcmp(".cvc", filename + len - 4) )
176 || ( len >= 5 && !strcmp(".cvc4", filename + len - 5) )) {
177 opts.setInputLanguage(language::input::LANG_CVC4);
178 } else if((len >= 3 && !strcmp(".sy", filename + len - 3))
179 || (len >= 3 && !strcmp(".sl", filename + len - 3))) {
180 opts.setInputLanguage(language::input::LANG_SYGUS);
181 //since there is no sygus output language, set this to SMT lib 2
182 //opts.setOutputLanguage(language::output::LANG_SMTLIB_V2_0);
183 }
184 }
185 }
186
187 if(opts.getOutputLanguage() == language::output::LANG_AUTO) {
188 opts.setOutputLanguage(language::toOutputLanguage(opts.getInputLanguage()));
189 }
190
191 // Determine which messages to show based on smtcomp_mode and verbosity
192 if(Configuration::isMuzzledBuild()) {
193 DebugChannel.setStream(&CVC4::null_os);
194 TraceChannel.setStream(&CVC4::null_os);
195 NoticeChannel.setStream(&CVC4::null_os);
196 ChatChannel.setStream(&CVC4::null_os);
197 MessageChannel.setStream(&CVC4::null_os);
198 WarningChannel.setStream(&CVC4::null_os);
199 }
200
201 // important even for muzzled builds (to get result output right)
202 (*(opts.getOut())) << language::SetLanguage(opts.getOutputLanguage());
203
204 // Create the expression manager using appropriate options
205 std::unique_ptr<api::Solver> solver;
206 # ifndef PORTFOLIO_BUILD
207 solver.reset(new api::Solver(&opts));
208 pExecutor = new CommandExecutor(solver.get(), opts);
209 # else
210 OptionsList threadOpts;
211 parseThreadSpecificOptions(threadOpts, opts);
212
213 bool useParallelExecutor = true;
214 // incremental?
215 if(opts.wasSetByUserIncrementalSolving() &&
216 opts.getIncrementalSolving() &&
217 (! opts.getIncrementalParallel()) ) {
218 Notice() << "Notice: In --incremental mode, using the sequential solver"
219 << " unless forced by...\n"
220 << "Notice: ...the experimental --incremental-parallel option.\n";
221 useParallelExecutor = false;
222 }
223 // proofs?
224 if(opts.getCheckProofs()) {
225 if(opts.getFallbackSequential()) {
226 Warning() << "Warning: Falling back to sequential mode, as cannot run"
227 << " portfolio in check-proofs mode.\n";
228 useParallelExecutor = false;
229 }
230 else {
231 throw OptionException("Cannot run portfolio in check-proofs mode.");
232 }
233 }
234 // pick appropriate one
235 if (useParallelExecutor)
236 {
237 solver.reset(new api::Solver(&threadOpts[0]));
238 pExecutor = new CommandExecutorPortfolio(solver.get(), opts, threadOpts);
239 }
240 else
241 {
242 solver.reset(new api::Solver(&opts));
243 pExecutor = new CommandExecutor(solver.get(), opts);
244 }
245 # endif
246
247 std::unique_ptr<Parser> replayParser;
248 if (opts.getReplayInputFilename() != "")
249 {
250 std::string replayFilename = opts.getReplayInputFilename();
251 ParserBuilder replayParserBuilder(solver.get(), replayFilename, opts);
252
253 if( replayFilename == "-") {
254 if( inputFromStdin ) {
255 throw OptionException("Replay file and input file can't both be stdin.");
256 }
257 replayParserBuilder.withStreamInput(cin);
258 }
259 replayParser.reset(replayParserBuilder.build());
260 pExecutor->setReplayStream(new Parser::ExprStream(replayParser.get()));
261 }
262
263 int returnValue = 0;
264 {
265 // Timer statistic
266 RegisterStatistic statTotalTime(&pExecutor->getStatisticsRegistry(),
267 pTotalTime);
268
269 // Filename statistics
270 ReferenceStat<std::string> s_statFilename("filename", filenameStr);
271 RegisterStatistic statFilenameReg(&pExecutor->getStatisticsRegistry(),
272 &s_statFilename);
273 // set filename in smt engine
274 pExecutor->getSmtEngine()->setFilename(filenameStr);
275
276 // Parse and execute commands until we are done
277 Command* cmd;
278 bool status = true;
279 if(opts.getInteractive() && inputFromStdin) {
280 if(opts.getTearDownIncremental() > 0) {
281 throw OptionException(
282 "--tear-down-incremental doesn't work in interactive mode");
283 }
284 #ifndef PORTFOLIO_BUILD
285 if(!opts.wasSetByUserIncrementalSolving()) {
286 cmd = new SetOptionCommand("incremental", SExpr(true));
287 cmd->setMuted(true);
288 pExecutor->doCommand(cmd);
289 delete cmd;
290 }
291 #endif /* PORTFOLIO_BUILD */
292 InteractiveShell shell(solver.get());
293 if(opts.getInteractivePrompt()) {
294 Message() << Configuration::getPackageName()
295 << " " << Configuration::getVersionString();
296 if(Configuration::isGitBuild()) {
297 Message() << " [" << Configuration::getGitId() << "]";
298 }
299 Message() << (Configuration::isDebugBuild() ? " DEBUG" : "")
300 << " assertions:"
301 << (Configuration::isAssertionBuild() ? "on" : "off")
302 << endl << endl;
303 Message() << Configuration::copyright() << endl;
304 }
305 if(replayParser) {
306 // have the replay parser use the declarations input interactively
307 replayParser->useDeclarationsFrom(shell.getParser());
308 }
309
310 while(true) {
311 try {
312 cmd = shell.readCommand();
313 } catch(UnsafeInterruptException& e) {
314 (*opts.getOut()) << CommandInterrupted();
315 break;
316 }
317 if (cmd == NULL)
318 break;
319 status = pExecutor->doCommand(cmd) && status;
320 if (cmd->interrupted()) {
321 delete cmd;
322 break;
323 }
324 delete cmd;
325 }
326 } else if( opts.getTearDownIncremental() > 0) {
327 if(!opts.getIncrementalSolving() && opts.getTearDownIncremental() > 1) {
328 // For tear-down-incremental values greater than 1, need incremental
329 // on too.
330 cmd = new SetOptionCommand("incremental", SExpr(true));
331 cmd->setMuted(true);
332 pExecutor->doCommand(cmd);
333 delete cmd;
334 // if(opts.wasSetByUserIncrementalSolving()) {
335 // throw OptionException(
336 // "--tear-down-incremental incompatible with --incremental");
337 // }
338
339 // cmd = new SetOptionCommand("incremental", SExpr(false));
340 // cmd->setMuted(true);
341 // pExecutor->doCommand(cmd);
342 // delete cmd;
343 }
344
345 ParserBuilder parserBuilder(solver.get(), filename, opts);
346
347 if( inputFromStdin ) {
348 #if defined(CVC4_COMPETITION_MODE) && !defined(CVC4_SMTCOMP_APPLICATION_TRACK)
349 parserBuilder.withStreamInput(cin);
350 #else /* CVC4_COMPETITION_MODE && !CVC4_SMTCOMP_APPLICATION_TRACK */
351 parserBuilder.withLineBufferedStreamInput(cin);
352 #endif /* CVC4_COMPETITION_MODE && !CVC4_SMTCOMP_APPLICATION_TRACK */
353 }
354
355 vector< vector<Command*> > allCommands;
356 allCommands.push_back(vector<Command*>());
357 std::unique_ptr<Parser> parser(parserBuilder.build());
358 if(replayParser) {
359 // have the replay parser use the file's declarations
360 replayParser->useDeclarationsFrom(parser.get());
361 }
362 int needReset = 0;
363 // true if one of the commands was interrupted
364 bool interrupted = false;
365 while (status || opts.getContinuedExecution()) {
366 if (interrupted) {
367 (*opts.getOut()) << CommandInterrupted();
368 break;
369 }
370
371 try {
372 cmd = parser->nextCommand();
373 if (cmd == NULL) break;
374 } catch (UnsafeInterruptException& e) {
375 interrupted = true;
376 continue;
377 }
378
379 if(dynamic_cast<PushCommand*>(cmd) != NULL) {
380 if(needReset >= opts.getTearDownIncremental()) {
381 pExecutor->reset();
382 for(size_t i = 0; i < allCommands.size() && !interrupted; ++i) {
383 if (interrupted) break;
384 for(size_t j = 0; j < allCommands[i].size() && !interrupted; ++j)
385 {
386 Command* cmd = allCommands[i][j]->clone();
387 cmd->setMuted(true);
388 pExecutor->doCommand(cmd);
389 if(cmd->interrupted()) {
390 interrupted = true;
391 }
392 delete cmd;
393 }
394 }
395 needReset = 0;
396 }
397 allCommands.push_back(vector<Command*>());
398 Command* copy = cmd->clone();
399 allCommands.back().push_back(copy);
400 status = pExecutor->doCommand(cmd);
401 if(cmd->interrupted()) {
402 interrupted = true;
403 continue;
404 }
405 } else if(dynamic_cast<PopCommand*>(cmd) != NULL) {
406 allCommands.pop_back(); // fixme leaks cmds here
407 if (needReset >= opts.getTearDownIncremental()) {
408 pExecutor->reset();
409 for(size_t i = 0; i < allCommands.size() && !interrupted; ++i) {
410 for(size_t j = 0; j < allCommands[i].size() && !interrupted; ++j)
411 {
412 Command* cmd = allCommands[i][j]->clone();
413 cmd->setMuted(true);
414 pExecutor->doCommand(cmd);
415 if(cmd->interrupted()) {
416 interrupted = true;
417 }
418 delete cmd;
419 }
420 }
421 if (interrupted) continue;
422 (*opts.getOut()) << CommandSuccess();
423 needReset = 0;
424 } else {
425 status = pExecutor->doCommand(cmd);
426 if(cmd->interrupted()) {
427 interrupted = true;
428 continue;
429 }
430 }
431 } else if(dynamic_cast<CheckSatCommand*>(cmd) != NULL ||
432 dynamic_cast<QueryCommand*>(cmd) != NULL) {
433 if(needReset >= opts.getTearDownIncremental()) {
434 pExecutor->reset();
435 for(size_t i = 0; i < allCommands.size() && !interrupted; ++i) {
436 for(size_t j = 0; j < allCommands[i].size() && !interrupted; ++j)
437 {
438 Command* cmd = allCommands[i][j]->clone();
439 cmd->setMuted(true);
440 pExecutor->doCommand(cmd);
441 if(cmd->interrupted()) {
442 interrupted = true;
443 }
444 delete cmd;
445 }
446 }
447 needReset = 0;
448 } else {
449 ++needReset;
450 }
451 if (interrupted) {
452 continue;
453 }
454
455 status = pExecutor->doCommand(cmd);
456 if(cmd->interrupted()) {
457 interrupted = true;
458 continue;
459 }
460 } else if(dynamic_cast<ResetCommand*>(cmd) != NULL) {
461 pExecutor->doCommand(cmd);
462 allCommands.clear();
463 allCommands.push_back(vector<Command*>());
464 } else {
465 // We shouldn't copy certain commands, because they can cause
466 // an error on replay since there's no associated sat/unsat check
467 // preceding them.
468 if(dynamic_cast<GetUnsatCoreCommand*>(cmd) == NULL &&
469 dynamic_cast<GetProofCommand*>(cmd) == NULL &&
470 dynamic_cast<GetValueCommand*>(cmd) == NULL &&
471 dynamic_cast<GetModelCommand*>(cmd) == NULL &&
472 dynamic_cast<GetAssignmentCommand*>(cmd) == NULL &&
473 dynamic_cast<GetInstantiationsCommand*>(cmd) == NULL &&
474 dynamic_cast<GetAssertionsCommand*>(cmd) == NULL &&
475 dynamic_cast<GetInfoCommand*>(cmd) == NULL &&
476 dynamic_cast<GetOptionCommand*>(cmd) == NULL &&
477 dynamic_cast<EchoCommand*>(cmd) == NULL) {
478 Command* copy = cmd->clone();
479 allCommands.back().push_back(copy);
480 }
481 status = pExecutor->doCommand(cmd);
482 if(cmd->interrupted()) {
483 interrupted = true;
484 continue;
485 }
486
487 if(dynamic_cast<QuitCommand*>(cmd) != NULL) {
488 delete cmd;
489 break;
490 }
491 }
492 delete cmd;
493 }
494 } else {
495 if(!opts.wasSetByUserIncrementalSolving()) {
496 cmd = new SetOptionCommand("incremental", SExpr(false));
497 cmd->setMuted(true);
498 pExecutor->doCommand(cmd);
499 delete cmd;
500 }
501
502 ParserBuilder parserBuilder(solver.get(), filename, opts);
503
504 if( inputFromStdin ) {
505 #if defined(CVC4_COMPETITION_MODE) && !defined(CVC4_SMTCOMP_APPLICATION_TRACK)
506 parserBuilder.withStreamInput(cin);
507 #else /* CVC4_COMPETITION_MODE && !CVC4_SMTCOMP_APPLICATION_TRACK */
508 parserBuilder.withLineBufferedStreamInput(cin);
509 #endif /* CVC4_COMPETITION_MODE && !CVC4_SMTCOMP_APPLICATION_TRACK */
510 }
511
512 std::unique_ptr<Parser> parser(parserBuilder.build());
513 if(replayParser) {
514 // have the replay parser use the file's declarations
515 replayParser->useDeclarationsFrom(parser.get());
516 }
517 bool interrupted = false;
518 while(status || opts.getContinuedExecution()) {
519 if (interrupted) {
520 (*opts.getOut()) << CommandInterrupted();
521 pExecutor->reset();
522 break;
523 }
524 try {
525 cmd = parser->nextCommand();
526 if (cmd == NULL) break;
527 } catch (UnsafeInterruptException& e) {
528 interrupted = true;
529 continue;
530 }
531
532 status = pExecutor->doCommand(cmd);
533 if (cmd->interrupted() && status == 0) {
534 interrupted = true;
535 break;
536 }
537
538 if(dynamic_cast<QuitCommand*>(cmd) != NULL) {
539 delete cmd;
540 break;
541 }
542 delete cmd;
543 }
544 }
545
546 Result result;
547 if(status) {
548 result = pExecutor->getResult();
549 returnValue = 0;
550 } else {
551 // there was some kind of error
552 returnValue = 1;
553 }
554
555 #ifdef CVC4_COMPETITION_MODE
556 opts.flushOut();
557 // exit, don't return (don't want destructors to run)
558 // _exit() from unistd.h doesn't run global destructors
559 // or other on_exit/atexit stuff.
560 _exit(returnValue);
561 #endif /* CVC4_COMPETITION_MODE */
562
563 ReferenceStat< Result > s_statSatResult("sat/unsat", result);
564 RegisterStatistic statSatResultReg(&pExecutor->getStatisticsRegistry(),
565 &s_statSatResult);
566
567 pTotalTime->stop();
568
569 // Tim: I think that following comment is out of date?
570 // Set the global executor pointer to NULL first. If we get a
571 // signal while dumping statistics, we don't want to try again.
572 pExecutor->flushOutputStreams();
573
574 #ifdef CVC4_DEBUG
575 if(opts.getEarlyExit() && opts.wasSetByUserEarlyExit()) {
576 _exit(returnValue);
577 }
578 #else /* CVC4_DEBUG */
579 if(opts.getEarlyExit()) {
580 _exit(returnValue);
581 }
582 #endif /* CVC4_DEBUG */
583 }
584
585 // On exceptional exit, these are leaked, but that's okay... they
586 // need to be around in that case for main() to print statistics.
587 delete pTotalTime;
588 delete pExecutor;
589
590 pTotalTime = NULL;
591 pExecutor = NULL;
592
593 cvc4_shutdown();
594
595 return returnValue;
596 }
597