Skip to content

Commit b2d2cc9

Browse files
committed
CFILE: add c-file run command
1 parent 7fe1b6f commit b2d2cc9

File tree

2 files changed

+187
-0
lines changed

2 files changed

+187
-0
lines changed

chc/cmdline/c_file/cfileutil.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,155 @@ def pofilter(po: "CFunctionPO") -> bool:
348348
exit(0)
349349

350350

351+
def cfile_run_file(args: argparse.Namespace) -> NoReturn:
352+
"""Parses, analyzes, and outputs a report for a single file."""
353+
354+
# arguments
355+
pcfilename: str = os.path.abspath(args.filename)
356+
opttgtpath: Optional[str] = args.tgtpath
357+
cshowcode: bool = args.showcode
358+
copen: bool = args.open
359+
jsonoutput: bool = args.json
360+
outputfile: Optional[str] = args.output
361+
loglevel: str = args.loglevel
362+
logfilename: Optional[str] = args.logfilename
363+
logfilemode: str = args.logfilemode
364+
365+
try:
366+
UF.check_parser()
367+
except UF.CHError as e:
368+
print(str(e.wrap()))
369+
exit(1)
370+
371+
if not os.path.isfile(os.path.abspath(pcfilename)):
372+
print_error("C source code file " + pcfilename + " not found")
373+
exit(1)
374+
375+
if not pcfilename.endswith(".c"):
376+
print_error("C source code file should have extension .c")
377+
exit(1)
378+
379+
projectpath = os.path.dirname(os.path.abspath(pcfilename))
380+
targetpath = projectpath if opttgtpath is None else opttgtpath
381+
targetpath = os.path.abspath(targetpath)
382+
cfilename_c = os.path.basename(pcfilename)
383+
cfilename = cfilename_c[:-2]
384+
projectname = cfilename
385+
386+
if not os.path.isdir(targetpath):
387+
print_error("Target directory: " + targetpath + " does not exist")
388+
exit(1)
389+
390+
set_logging(
391+
loglevel,
392+
targetpath,
393+
logfilename=logfilename,
394+
mode=logfilemode,
395+
msg="Command cfile run was invoked")
396+
397+
chklogger.logger.info("Target path: %s", targetpath)
398+
399+
parsemanager = ParseManager(projectpath, projectname, targetpath)
400+
parsemanager.remove_semantics()
401+
parsemanager.initialize_paths()
402+
403+
try:
404+
cfilename_i = parsemanager.preprocess_file_with_gcc(cfilename_c)
405+
result = parsemanager.parse_ifile(cfilename_i)
406+
if result != 0:
407+
print("*" * 80)
408+
print("Error in parsing " + cfilename_c)
409+
if Config().platform == "macOS":
410+
print(" (Problem may be related to standard header files on macOS)")
411+
print("*" * 80)
412+
exit(1)
413+
except OSError as e:
414+
print_error("Error when parsing file: " + str(e))
415+
exit(1)
416+
417+
parsemanager.save_semantics()
418+
419+
chklogger.logger.info("cfile parse completed")
420+
421+
parsearchive = UF.get_parse_archive(targetpath, projectname)
422+
423+
if not os.path.isfile(parsearchive):
424+
print_error("Please run parser first on c file")
425+
exit(1)
426+
427+
cchpath = UF.get_cchpath(targetpath, projectname)
428+
429+
if os.path.isdir(cchpath):
430+
chklogger.logger.info("Old analysis results: %s are removed", cchpath)
431+
shutil.rmtree(cchpath)
432+
433+
if os.path.isfile(parsearchive):
434+
chklogger.logger.info("Directory is changed to %s", targetpath)
435+
os.chdir(targetpath)
436+
tarname = os.path.basename(parsearchive)
437+
cmd = ["tar", "xfz", os.path.basename(tarname)]
438+
chklogger.logger.info("Semantics is extracted from %s", tarname)
439+
result = subprocess.call(cmd, cwd=targetpath, stderr=subprocess.STDOUT)
440+
if result != 0:
441+
print_error("Error in extracting " + tarname)
442+
exit(1)
443+
chklogger.logger.info(
444+
"Semantics was successfully extracted from %s", tarname)
445+
446+
contractpath = os.path.join(targetpath, "chc_contracts")
447+
448+
capp = CApplication(
449+
projectpath, projectname, targetpath, contractpath, singlefile=True)
450+
451+
capp.initialize_single_file(cfilename)
452+
cfile = capp.get_cfile()
453+
454+
am = AnalysisManager(capp)
455+
456+
am.create_file_primary_proofobligations(cfilename)
457+
am.reset_tables(cfile)
458+
capp.collect_post_assumes()
459+
460+
am.generate_and_check_file(cfilename, None, "llrvisp")
461+
am.reset_tables(cfile)
462+
capp.collect_post_assumes()
463+
464+
for k in range(5):
465+
capp.update_spos()
466+
am.generate_and_check_file(cfilename, None, "llrvisp")
467+
am.reset_tables(cfile)
468+
469+
chklogger.logger.info("cfile analyze completed")
470+
471+
capp = CApplication(
472+
projectpath, projectname, targetpath, contractpath, singlefile=True)
473+
capp.initialize_single_file(cfilename)
474+
cfile = capp.get_cfile()
475+
476+
if jsonoutput:
477+
jsonresult = JU.file_proofobligations_to_json_result(cfile)
478+
if jsonresult.is_ok:
479+
jsonokresult = JU.jsonok("fileproofobligations", jsonresult.content)
480+
if outputfile:
481+
with open(outputfile + ".json", "w") as fp:
482+
json.dump(jsonokresult, fp, indent=2)
483+
else:
484+
print(json.dumps(jsonokresult, indent=2))
485+
exit(0)
486+
487+
def pofilter(po: "CFunctionPO") -> bool:
488+
if copen:
489+
return not po.is_closed
490+
else:
491+
return True
492+
493+
if cshowcode:
494+
print(RP.file_code_tostring(cfile, pofilter=pofilter))
495+
496+
print(RP.file_proofobligation_stats_tostring(cfile))
497+
exit(0)
498+
499+
351500
def cfile_investigate_file(args: argparse.Namespace) -> NoReturn:
352501
"""Shows a list of open, delegated, and violated proof obligations.
353502

chc/cmdline/chkc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,44 @@ def parse() -> argparse.Namespace:
726726

727727
cfilereport.set_defaults(func=C.cfile_report_file)
728728

729+
# --- run
730+
cfilerun = cfileparsers.add_parser("run")
731+
cfilerun.add_argument(
732+
"filename",
733+
help="name of file to be analyzed (<cpath/>)<cfilename>)")
734+
cfilerun.add_argument(
735+
"--tgtpath",
736+
help="directory to store the analysis results")
737+
cfilerun.add_argument(
738+
"--showcode",
739+
action="store_true",
740+
help="show proof obligations on code for entire file")
741+
cfilerun.add_argument(
742+
"--open",
743+
action="store_true",
744+
help="only show open proof obligations")
745+
cfilerun.add_argument(
746+
"--json",
747+
action="store_true",
748+
help="output results in json format")
749+
cfilerun.add_argument(
750+
"--output", "-o",
751+
help="name of outputfile (without extension)")
752+
cfilerun.add_argument(
753+
"--loglevel", "-log",
754+
choices=UL.LogLevel.options(),
755+
default="NONE",
756+
help="activate logging with given level (default to stderr)")
757+
cfilerun.add_argument(
758+
"--logfilename",
759+
help="name of file to write log messages")
760+
cfilerun.add_argument(
761+
"--logfilemode",
762+
choices=["a", "w"],
763+
default="a",
764+
help="file mode for log file: append (a, default), or write (w)")
765+
cfilerun.set_defaults(func=C.cfile_run_file)
766+
729767
# --- investigate
730768
cfileinvestigate = cfileparsers.add_parser("investigate")
731769
cfileinvestigate.add_argument(

0 commit comments

Comments
 (0)