123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525(** This is the main program! *)openPreludeopenGobConfigopenDefaultsopenPrintfopenGoblintutilletwriteconffile=ref""(** Print version and bail. *)letprint_versionch=letopenVersioninletopenConfiginletfchb=ifbthenfprintfch"enabled"elsefprintfch"disabled"inprintf"Goblint version: 1.1.1 (%s)\n"goblint;printf"Cil version: %s\n"Cil.cilVersion;printf"Configuration: tracing %a\n"ftracing;exit0(** Print helpful messages. *)letprint_helpch=fprintfch"Usage: goblint [options] source-files\nOptions\n";fprintfch" -v Prints more status information. \n";fprintfch" -o <file> Prints the output to file. \n";fprintfch" -I <dir> Add include directory. \n";fprintfch" -IK <dir> Add kernel include directory. \n\n";fprintfch" --help Prints this text \n";fprintfch" --version Print out current version information. \n\n";fprintfch" --conf <file> Merge the configuration from the <file>. \n";fprintfch" --writeconf <file> Write the effective configuration to <file> \n";fprintfch" --set <jpath> <jvalue> Set a configuration variable <jpath> to the specified <jvalue>.\n";fprintfch" --sets <jpath> <string> Set a configuration variable <jpath> to the string.\n";fprintfch" --enable <jpath> Set a configuration variable <jpath> to true. \n";fprintfch" --disable <jpath> Set a configuration variable <jpath> to false. \n\n";fprintfch" --print_options Print out commonly used configuration variables.\n";fprintfch" --print_all_options Print out all configuration variables. \n";fprintfch"\n";fprintfch"A <jvalue> is a string from the JSON language where single-quotes (')";fprintfch" are used instead of double-quotes (\").\n\n";fprintfch"A <jpath> is a path in a json structure. E.g. 'field.another_field[42]';\n";fprintfch"in addition to the normal syntax you can use 'field[+]' append to an array.\n\n";exit0(* The temp directory for preprocessing the input files *)letcreate_temp_dir()=ifSys.file_exists(get_string"tempDir")thenGoblintutil.tempDirName:=get_string"tempDir"else(* Using the stdlib to create a free tmp file name. *)lettmpDirRel=Filename.temp_file~temp_dir:"""goblint_temp_"""in(* ... and then delete it to create a directory instead. *)Sys.removetmpDirRel;lettmpDirName=create_dirtmpDirRelinGoblintutil.tempDirName:=tmpDirNameletremove_temp_dir()=ifnot(get_bool"keepcpp")thenignore(Goblintutil.rm_rf!Goblintutil.tempDirName)(** [Arg] option specification *)letoption_spec_list=letadd_stringl=letfstr=l:=str::!linArg.Stringfinletadd_intl=letfstr=l:=str::!linArg.Intfinletset_tracesys=letmsg="Goblint has been compiled without tracing, run ./scripts/trace_on.sh to recompile."inifConfig.tracingthenTracing.addsystemsyselse(prerr_endlinemsg;raiseExit)inletoilfile=set_string"ana.osek.oil"file;set_auto"ana.activated""['base','threadid','threadflag','escape','OSEK','OSEK2','stack_trace_set','fmode','flag','mallocWrapper']";set_auto"mainfun""[]"inletconfigure_html()=if(get_string"outfile"="")thenset_string"outfile""result";ifget_string"exp.g2html_path"=""thenset_string"exp.g2html_path"exe_dir;set_bool"dbg.print_dead_code"true;set_bool"exp.cfgdot"true;set_bool"g2html"true;set_string"result""fast_xml"inletconfigure_sarif()=if(get_string"outfile"="")thenset_string"outfile""goblint.sarif";set_bool"dbg.print_dead_code"true;set_string"result""sarif"inlettmp_arg=ref""in["-o",Arg.String(set_string"outfile"),"";"-v",Arg.Unit(fun()->set_bool"dbg.verbose"true;set_bool"printstats"true),"";"-I",Arg.String(set_string"includes[+]"),"";"-IK",Arg.String(set_string"kernel_includes[+]"),"";"--set",Arg.Tuple[Arg.Set_stringtmp_arg;Arg.String(funx->set_auto!tmp_argx)],"";"--sets",Arg.Tuple[Arg.Set_stringtmp_arg;Arg.String(funx->prerr_endline"--sets is deprecated, use --set instead.";set_string!tmp_argx)],"";"--enable",Arg.String(funx->set_boolxtrue),"";"--disable",Arg.String(funx->set_boolxfalse),"";"--conf",Arg.Stringmerge_file,"";"--writeconf",Arg.String(funfn->writeconffile:=fn),"";"--version",Arg.Unitprint_version,"";"--print_options",Arg.Unit(fun_->printCategorystdoutStd;exit0),"";"--print_all_options",Arg.Unit(fun_->printAllCategoriesstdout;exit0),"";"--trace",Arg.Stringset_trace,"";"--tracevars",add_stringTracing.tracevars,"";"--tracelocs",add_intTracing.tracelocs,"";"--help",Arg.Unit(fun_->print_helpstdout),"";"--html",Arg.Unit(fun_->configure_html()),"";"--sarif",Arg.Unit(fun_->configure_sarif()),"";"--compare_runs",Arg.Tuple[Arg.Set_stringtmp_arg;Arg.String(funx->set_auto"compare_runs"(sprintf"['%s','%s']"!tmp_argx))],"";"--oil",Arg.Stringoil,""(* ; "--tramp" , Arg.String (set_string "ana.osek.tramp"), "" *);"--osekdefaults",Arg.Unit(fun()->set_bool"ana.osek.defaults"false),"";"--osektaskprefix",Arg.String(set_string"ana.osek.taskprefix"),"";"--osekisrprefix",Arg.String(set_string"ana.osek.isrprefix"),"";"--osektasksuffix",Arg.String(set_string"ana.osek.tasksuffix"),"";"--osekisrsuffix",Arg.String(set_string"ana.osek.isrsuffix"),"";"--osekcheck",Arg.Unit(fun()->set_bool"ana.osek.check"true),"";"--oseknames",Arg.Set_stringOilUtil.osek_renames,"";"--osekids",Arg.Set_stringOilUtil.osek_ids,""](** List of C files to consider. *)letcFileNames=ref[](** Parse arguments and fill [cFileNames] and [jsonFiles]. Print help if needed. *)letparse_arguments()=letjsonRegex=Str.regexp".+\\.json$"inletrecordFilefname=ifStr.string_matchjsonRegexfname0thenGoblintutil.jsonFiles:=fname::!Goblintutil.jsonFileselsecFileNames:=fname::!cFileNamesinArg.parseoption_spec_listrecordFile"Look up options using 'goblint --help'.";if!writeconffile<>""then(GobConfig.write_file!writeconffile;raiseExit)(** Initialize some globals in other modules. *)lethandle_flags()=lethas_oil=get_string"ana.osek.oil"<>""inifhas_oilthenOsek.Spec.parse_oil();ifget_bool"dbg.verbose"then(Printexc.record_backtracetrue;Errormsg.debugFlag:=true;Errormsg.verboseFlag:=true);matchget_string"dbg.dump"with|""->()|path->Messages.formatter:=Format.formatter_of_out_channel(Legacy.open_out(Legacy.Filename.concatpath"warnings.out"));set_string"outfile"""(** Use gcc to preprocess a file. Returns the path to the preprocessed file. *)letpreprocess_one_filecppflagsfname=(* The actual filename of the preprocessed sourcefile *)letnname=Filename.concat!Goblintutil.tempDirName(Filename.basenamefname)inifSys.file_exists(get_string"tempDir")thennnameelse(* Preprocess using cpp. *)(* ?? what is __BLOCKS__? is it ok to just undef? this? http://en.wikipedia.org/wiki/Blocks_(C_language_extension) *)letcommand=Config.cpp^" --undef __BLOCKS__ "^cppflags^" \""^fname^"\" -o \""^nname^"\""inifget_bool"dbg.verbose"thenprint_endlinecommand;(* if something goes wrong, we need to clean up and exit *)letrm_and_exit()=remove_temp_dir();raiseExitintrymatchUnix.systemcommandwith|Unix.WEXITED0->nname|_->eprintf"Goblint: Preprocessing failed.";rm_and_exit()withUnix.Unix_error(e,f,a)->eprintf"%s at syscall %s with argument \"%s\".\n"(Unix.error_messagee)fa;rm_and_exit()(** Preprocess all files. Return list of preprocessed files and the temp directory name. *)letpreprocess_files()=(* Preprocessor flags *)letcppflags=ref(get_string"cppflags")in(* the base include directory *)letcustom_include_dirs=get_string_list"custom_includes"@Filename.concatexe_dir"includes"::Goblint_sites.includesinifget_bool"dbg.verbose"then(print_endline"Custom include dirs:";List.iteri(funicustom_include_dir->Printf.printf" %d. %s (exists=%B)\n"(i+1)custom_include_dir(Sys.file_existscustom_include_dir))custom_include_dirs);letcustom_include_dirs=List.filterSys.file_existscustom_include_dirsinifcustom_include_dirs=[]thenprint_endline"Warning, cannot find goblint's custom include files.";letfind_custom_includesubpath=List.find_map(funcustom_include_dir->letpath=Filename.concatcustom_include_dirsubpathinifSys.file_existspaththenSomepathelseNone)custom_include_dirsin(* include flags*)letinclude_dirs=ref[]inletinclude_files=ref[]in(* fill include flags *)letone_include_ffx=include_dirs:=fx::!include_dirsinifget_string"ana.osek.oil"<>""theninclude_files:=Filename.concat!Goblintutil.tempDirNameOilUtil.header::!include_files;(* if get_string "ana.osek.tramp" <> "" then include_files := get_string "ana.osek.tramp" :: !include_files; *)get_string_list"includes"|>List.iter(one_include_fidentity);include_dirs:=custom_include_dirs@!include_dirs;(* reverse the files again *)cFileNames:=List.rev!cFileNames;(* If the first file given is a Makefile, we use it to combine files *)if!cFileNames<>[]then(letfirstFile=List.first!cFileNamesinifFilename.basenamefirstFile="Makefile"then(letmakefile=firstFileinletpath=Filename.dirnamemakefilein(* make sure the Makefile exists or try to generate it *)ifnot(Sys.file_existsmakefile)then(print_endline("Given "^makefile^" does not exist!");letconfigure=Filename.concatpath"configure"inifSys.file_existsconfigurethen(print_endline("Trying to run "^configure^" to generate Makefile");letexit_code,output=MakefileUtil.exec_command~path"./configure"inprint_endline(configure^MakefileUtil.string_of_process_statusexit_code^". Output: "^output);ifnot(Sys.file_existsmakefile)thenfailwith("Running "^configure^" did not generate a Makefile - abort!"))elsefailwith("Could neither find given "^makefile^" nor "^configure^" - abort!"));let_=MakefileUtil.run_cillypathinletfile=MakefileUtil.(find_file_by_suffixpathcomb_suffix)incFileNames:=file::(List.drop1!cFileNames);););(* possibly add our lib.c to the files *)ifget_bool"custom_libc"thencFileNames:=find_custom_include"lib.c"::!cFileNames;ifget_bool"ana.sv-comp.functions"thencFileNames:=find_custom_include"sv-comp.c"::!cFileNames;(* If we analyze a kernel module, some special includes are needed. *)ifget_bool"kernel"then(letkernel_roots=[get_string"kernel-root";Filename.concatexe_dir"linux-headers";(* linux-headers not installed with goblint package *)]inletkernel_root=List.findSys.file_existskernel_rootsinletkernel_dir=kernel_root^"/include"inletarch_dir=kernel_root^"/arch/x86/include"in(* TODO add arm64: https://github.com/goblint/analyzer/issues/312 *)get_string_list"kernel_includes"|>List.iter(Filename.concatkernel_root|>one_include_f);letpreconf=find_custom_include"linux/goblint_preconf.h"inletautoconf=Filename.concatkernel_dir"linux/kconfig.h"incppflags:="-D__KERNEL__ -U__i386__ -D__x86_64__ "^!cppflags;include_files:=preconf::autoconf::!include_files;(* These are not just random permutations of directories, but based on USERINCLUDE from the
* Linux kernel Makefile (in the root directory of the kernel distribution). *)include_dirs:=!include_dirs@[kernel_dir;kernel_dir^"/uapi";kernel_dir^"include/generated/uapi";(* TODO: no / and duplicate include with kernel_dir is bug? *)arch_dir;arch_dir^"/generated";arch_dir^"/uapi";arch_dir^"/generated/uapi";]);letincludes=String.join" "(List.map(funinclude_dir->"-I "^include_dir)!include_dirs)^" "^String.join" "(List.map(funinclude_file->"-include "^include_file)!include_files)inletcppflags=!cppflags^" "^includesin(* preprocess all the files *)ifget_bool"dbg.verbose"thenprint_endline"Preprocessing files.";List.rev_map(preprocess_one_filecppflags)!cFileNames(** Possibly merge all postprocessed files *)letmerge_preprocessedcpp_file_names=(* get the AST *)ifget_bool"dbg.verbose"thenprint_endline"Parsing files.";letfiles_AST=List.rev_mapCilfacade.getASTcpp_file_namesinremove_temp_dir();letcilout=ifget_string"dbg.cilout"=""thenLegacy.stderrelseLegacy.open_out(get_string"dbg.cilout")in(* direct the output to file if requested *)ifnot(get_bool"g2html"||get_string"outfile"="")thenGoblintutil.out:=Legacy.open_out(get_string"outfile");Errormsg.logChannel:=Messages.get_out"cil"cilout;(* we use CIL to merge all inputs to ONE file *)letmerged_AST=matchfiles_ASTwith|[one]->Cilfacade.callConstructorsone|[]->prerr_endline"No arguments for Goblint?";prerr_endline"Try `goblint --help' for more information.";raiseExit|xs->Cilfacade.getMergedASTxs|>Cilfacade.callConstructorsinCilfacade.rmTempsmerged_AST;(* create the Control Flow Graph from CIL's AST *)Cilfacade.createCFGmerged_AST;Cilfacade.current_file:=merged_AST;merged_ASTletdo_stats()=ifget_bool"printstats"then(print_newline();ignore(Pretty.printf"vars = %d evals = %d \n"!Goblintutil.vars!Goblintutil.evals);print_newline();Stats.print(Messages.get_out"timing"Legacy.stderr)"Timings:\n";flush_all())(** Perform the analysis over the merged AST. *)letdo_analyzechange_infomerged_AST=letmoduleL=Printable.Liszt(CilType.Fundec)inifget_bool"justcil"then(* if we only want to print the output created by CIL: *)Cilfacade.printmerged_ASTelse((* we first find the functions to analyze: *)ifget_bool"dbg.verbose"thenprint_endline"And now... the Goblin!";let(stf,exf,otfasfuns)=Cilfacade.getFunsmerged_ASTinifstf@exf@otf=[]thenfailwith"No suitable function to start from.";ifget_bool"dbg.verbose"thenignore(Pretty.printf"Startfuns: %a\nExitfuns: %a\nOtherfuns: %a\n"L.prettystfL.prettyexfL.prettyotf);(* and here we run the analysis! *)letdo_all_phasesastfuns=letdo_one_phaseastp=phase:=p;ifget_bool"dbg.verbose"then(letaa=String.concat", "@@get_string_list"ana.activated"inletat=String.concat", "@@get_string_list"trans.activated"inprint_endline@@"Activated analyses for phase "^string_of_intp^": "^aa;print_endline@@"Activated transformations for phase "^string_of_intp^": "^at);tryControl.analyzechange_infoastfunswithe->letbacktrace=Printexc.get_raw_backtrace()in(* capture backtrace immediately, otherwise the following loses it (internal exception usage without raise_notrace?) *)letloc=!Tracing.current_locinMessages.error~loc"About to crash!";(* TODO: move severity coloring to Messages *)(* trigger Generic.SolverStats...print_stats *)Goblintutil.(self_signal(signal_of_string(get_string"dbg.solver-signal")));do_stats();print_newline();Printexc.raise_with_backtraceebacktrace(* re-raise with captured inner backtrace *)(* Cilfacade.current_file := ast'; *)in(* old style is ana.activated = [phase_1, ...] with phase_i = [ana_1, ...]
new style (Goblintutil.phase_config = true) is phases[i].ana.activated = [ana_1, ...]
phases[i].ana.x overwrites setting ana.x *)letnum_phases=letnp,na,nt=Tuple3.mapn(List.length%get_list)("phases","ana.activated","trans.activated")inphase_config:=np>0;(* TODO what about wrong usage like { phases = [...], ana.activated = [...] }? should child-lists add to parent-lists? *)ifget_bool"dbg.verbose"thenprint_endline@@"Using "^if!phase_configthen"new"else"old"^" format for phases!";ifnp=0&&na=0&&nt=0thenfailwith"No phases and no activated analyses or transformations!";maxnp1inignore@@Enum.iter(do_one_phaseast)(0--(num_phases-1))in(* Analyze with the new experimental framework. *)Stats.time"analysis"(do_all_phasesmerged_AST)funs)letdo_html_output()=letjar=Filename.concat(get_string"exp.g2html_path")"g2html.jar"inifget_bool"g2html"then(ifSys.file_existsjarthen(letcommand="java -jar "^jar^" --result-dir "^(get_string"outfile")^" "^!Messages.xml_file_nameintrymatchUnix.systemcommandwith|Unix.WEXITED0->()|_->eprintf"HTML generation failed! Command: %s\n"commandwithUnix.Unix_error(e,f,a)->eprintf"%s at syscall %s with argument \"%s\".\n"(Unix.error_messagee)fa)elseeprintf"Warning: jar file %s not found.\n"jar)letdo_gobview()=letcreate_symlinktargetlink=ifnot(Sys.file_existslink)thenUnix.symlinktargetlinkinletgobview=GobConfig.get_bool"gobview"inletgoblint_root=Filename.concat(Unix.getcwd())(Filename.dirnameSys.argv.(0))inletdist_dir=Filename.concatgoblint_root"_build/default/gobview/dist"inletjs_file=Filename.concatdist_dir"main.js"inifgobviewthen(ifSys.file_existsjs_filethen(letsave_run=GobConfig.get_string"save_run"inletrun_dir=ifsave_run<>""thensave_runelse"run"inletdist_files=Sys.files_ofdist_dir|>Enum.filter(funn->n<>"dune")|>List.of_enuminList.iter(funn->create_symlink(Filename.concatdist_dirn)(Filename.concatrun_dirn))dist_files)elseeprintf"Warning: Cannot locate Gobview.\n")leteprint_colorm=eprintf"%s\n"(MessageUtil.colorize~fd:Unix.stderrm)letcheck_arguments()=(* let fail m = let m = "Option failure: " ^ m in eprint_color ("{red}"^m); failwith m in *)(* unused now, but might be useful for future checks here *)letwarnm=eprint_color("{yellow}Option warning: "^m)inifget_bool"allfuns"&¬(get_bool"exp.earlyglobs")then(set_bool"exp.earlyglobs"true;warn"allfuns enables exp.earlyglobs.\n");ifnot@@List.mem"escape"@@get_string_list"ana.activated"thenwarn"Without thread escape analysis, every local variable whose address is taken is considered escaped, i.e., global!";ifget_string"ana.osek.oil"<>""&¬(get_string"exp.privatization"="protection-vesal"||get_string"exp.privatization"="protection-old")then(set_string"exp.privatization""protection-vesal";warn"oil requires protection-old/protection-vesal privatization");ifget_bool"ana.base.context.int"&¬(get_bool"ana.base.context.non-ptr")then(set_bool"ana.base.context.int"false;warn"ana.base.context.int implicitly disabled by ana.base.context.non-ptr");(* order matters: non-ptr=false, int=true -> int=false cascades to interval=false with warning *)ifget_bool"ana.base.context.interval"&¬(get_bool"ana.base.context.int")then(set_bool"ana.base.context.interval"false;warn"ana.base.context.interval implicitly disabled by ana.base.context.int");ifget_bool"incremental.only-rename"then(set_bool"incremental.load"true;warn"incremental.only-rename implicitly activates incremental.rename-load. Previous AST is loaded for diff and rename, but analyis results are not reused.")lethandle_extraspecials()=letfuns=get_string_list"exp.extraspecials"inLibraryFunctions.add_lib_funsfuns(* Detects changes and renames vids and sids. *)letdiff_and_renamecurrent_file=(* Create change info, either from old results, or from scratch if there are no previous results. *)letchange_info:Analyses.increment_data=ifGobConfig.get_bool"incremental.load"&¬(Serialize.results_exist())thenbeginletwarnm=eprint_color("{yellow}Warning: "^m)inwarn"incremental.load is activated but no data exists that can be loaded."end;let(changes,old_file,version_map,max_ids)=ifSerialize.results_exist()&&GobConfig.get_bool"incremental.load"thenbeginletold_file=Serialize.load_dataSerialize.CilFileinlet(version_map,changes,max_ids)=VersionLookup.load_and_update_mapold_filecurrent_fileinletmax_ids=UpdateCil.update_idsold_filemax_idscurrent_fileversion_mapchangesin(changes,Someold_file,version_map,max_ids)endelsebeginlet(version_map,max_ids)=VersionLookup.create_mapcurrent_filein(CompareCIL.empty_change_info(),None,version_map,max_ids)endinletsolver_data=ifSerialize.results_exist()&&GobConfig.get_bool"incremental.load"&¬(GobConfig.get_bool"incremental.only-rename")thenSome(Serialize.load_dataSerialize.SolverData)elseNoneinifGobConfig.get_bool"incremental.save"thenbeginSerialize.store_datacurrent_fileSerialize.CilFile;Serialize.store_data(version_map,max_ids)Serialize.VersionDataend;letold_data=matchold_file,solver_datawith|Somecil_file,Somesolver_data->Some({cil_file;solver_data}:Analyses.analyzed_data)|_,_->Nonein{Analyses.changes=changes;old_data;new_file=current_file}inchange_infolet()=(* signal for printing backtrace; other signals in Generic.SolverStats and Timeout *)letopenSysin(* whether interactive interrupt (ctrl-C) terminates the program or raises the Break exception which we use below to print a backtrace. https://ocaml.org/api/Sys.html#VALcatch_break *)catch_breaktrue;set_signal(Goblintutil.signal_of_string(get_string"dbg.backtrace-signal"))(Signal_handle(fun_->Printexc.get_callstack999|>Printexc.print_raw_backtraceStdlib.stderr;print_endline"\n...\n"))(* e.g. `pkill -SIGUSR2 goblint`, or `kill`, `htop` *)(** the main function *)letmain()=tryStats.resetStats.SoftwareTimer;parse_arguments();check_arguments();AfterConfig.run();Sys.set_signal(Goblintutil.signal_of_string(get_string"dbg.solver-signal"))Signal_ignore;(* Ignore solver-signal before solving (e.g. MyCFG), otherwise exceptions self-signal the default, which crashes instead of printing backtrace. *)(* Cil.lowerConstants assumes wrap-around behavior for signed intger types, which conflicts with checking
for overflows, as this will replace potential overflows with constants after wrap-around *)(ifGobConfig.get_bool"ana.sv-comp.enabled"&&Svcomp.Specification.of_option()=NoOverflowthenset_bool"exp.lower-constants"false);Cilfacade.init();handle_extraspecials();create_temp_dir();handle_flags();ifget_bool"dbg.verbose"then(print_endline(localtime());print_endlinecommand;);letfile=preprocess_files()|>merge_preprocessedinletchangeInfo=ifGobConfig.get_bool"incremental.load"||GobConfig.get_bool"incremental.save"thendiff_and_renamefileelseAnalyses.empty_increment_datafileinfile|>do_analyzechangeInfo;do_stats();do_html_output();do_gobview();if!verified=Somefalsethenexit3;(* verifier failed! *)with|Exit->exit1|Sys.Break->(* raised on Ctrl-C if `Sys.catch_break true` *)(* Printexc.print_backtrace BatInnerIO.stderr *)eprintf"%s\n"(MessageUtil.colorize~fd:Unix.stderr("{RED}Analysis was aborted by SIGINT (Ctrl-C)!"));exit131(* same exit code as without `Sys.catch_break true`, otherwise 0 *)|Timeout->eprintf"%s\n"(MessageUtil.colorize~fd:Unix.stderr("{RED}Analysis was aborted because it reached the set timeout of "^get_string"dbg.timeout"^" or was signalled SIGPROF!"));exit124(* The actual entry point is in the auto-generated goblint.ml module, and is defined as: *)(* let _ = at_exit main *)(* We do this since the evaluation order of top-level bindings is not defined, but we want `main` to run after all the other side-effects (e.g. registering analyses/solvers) have happened. *)