277 lines
9.6 KiB
Python
277 lines
9.6 KiB
Python
from __future__ import print_function
|
|
import argparse
|
|
import os,sys,io
|
|
from pycparser import parse_file, c_ast, CParser, c_generator
|
|
from pcpp import Preprocessor
|
|
import graphviz as gv
|
|
import html
|
|
from dataclasses import dataclass
|
|
|
|
from modelbuilder import *
|
|
from utils import *
|
|
from astvisitors import *
|
|
|
|
def parse_statevar(statevar):
|
|
r_struct = r'(\w+)(\.|->)(\w+)'
|
|
match = re.match(r_struct, statevar)
|
|
if match:
|
|
name,typ,field = match.groups()
|
|
return c_ast.StructRef(c_ast.ID(name), typ, c_ast.ID(field))
|
|
return c_ast.ID(statevar)
|
|
|
|
if __name__ == "__main__":
|
|
argparser = argparse.ArgumentParser('Create a Kripke Structure Model from C Code')
|
|
argparser.add_argument('filename',
|
|
nargs='?',
|
|
help='name of file to parse')
|
|
|
|
argparser.add_argument('-I', '--includedirs', nargs='+', default=[])
|
|
argparser.add_argument('-D', '--defines', nargs='+', default=[])
|
|
argparser.add_argument('-p', '--preprocess', help='output preprocessor file')
|
|
argparser.add_argument('-c', '--conditional', help='only count state assignment if this conditional applies on the path')
|
|
argparser.add_argument('--func', help='process function')
|
|
argparser.add_argument('--enum', help='state enum')
|
|
argparser.add_argument('--statevar', help='state variable')
|
|
argparser.add_argument('--initial', help='initial state')
|
|
argparser.add_argument('--ltlfile', help='file containing LTL formulae')
|
|
argparser.add_argument('-o', '--output', dest='output', help='output NuSMV file')
|
|
argparser.add_argument('--dot', help='output dot file')
|
|
args = argparser.parse_args()
|
|
|
|
source = ""
|
|
with open(args.filename, "r") as f:
|
|
#read whole file to a string
|
|
source = f.read()
|
|
|
|
p = Preprocessor()
|
|
p.add_path('/usr/lib/gcc/x86_64-linux-gnu/12/include/')
|
|
for inc in args.includedirs:
|
|
#print(f"Include-Dir: {inc}")
|
|
p.add_path(inc)
|
|
|
|
for define in args.defines:
|
|
name,value = define.split('=')
|
|
#print(f"Define: {name}={value}")
|
|
p.define(f"{name} {value}")
|
|
|
|
p.parse(source)
|
|
oh = io.StringIO()
|
|
p.write(oh)
|
|
prep_source = oh.getvalue()
|
|
|
|
if args.preprocess is not None:
|
|
with open(args.preprocess, "wt") as f:
|
|
n = f.write(prep_source)
|
|
|
|
cg = CGenerator()
|
|
|
|
parser = CParser()
|
|
ast = parser.parse(prep_source)
|
|
ass = parse_statevar(args.statevar)
|
|
tg = TypedefGatherer()
|
|
tg.visit(ast)
|
|
typedefs = tg.typedefs
|
|
#print("Typedefs", typedefs.keys())
|
|
|
|
tast = TypedIdTransformer(typedefs).transform(ast)
|
|
#tast.show()
|
|
ast = tast
|
|
|
|
initial_state = args.initial
|
|
assign_table = []
|
|
state_enums = []
|
|
func_table = {}
|
|
enum_table = {}
|
|
state_asmts = []
|
|
fsm_funcs = []
|
|
tran_table = []
|
|
properties = []
|
|
|
|
state_id = 'm_tState'
|
|
|
|
#TMR
|
|
#input_ids = ['m_bIn', 'm_ulActTime']
|
|
input_ids = ['cbEnable', 'cbCondition', 'm_ucEnableUnconditioned']
|
|
#conditional_ids = [*input_ids, 'm_ucTimerType']
|
|
|
|
fdv = FuncDefVisitor()
|
|
fdv.visit(ast)
|
|
func_table = fdv.func_table
|
|
proc_func = None
|
|
if not(args.func in func_table):
|
|
raise Exception(f"Function name '{args.func}' not found!")
|
|
else:
|
|
proc_func = func_table[args.func]
|
|
|
|
fast = AstFuncCallInlinerTransformer(func_table).transform(ast)
|
|
ast = fast
|
|
|
|
etv = EnumTypedefVisitor()
|
|
etv.visit(ast)
|
|
enum_table = etv.enums
|
|
states = []
|
|
|
|
def discover_cals(func):
|
|
funcs = []
|
|
child_funcs = []
|
|
fcv = FuncCallVisitor()
|
|
fcv.visit(func)
|
|
for fc in fcv.func_calls:
|
|
if fc in func_table:
|
|
funcs.append(func_table[fc])
|
|
child_funcs += discover_cals(func_table[fc])
|
|
return funcs + child_funcs
|
|
|
|
# discover FSM functions
|
|
fsm_funcs.append(proc_func)
|
|
fsm_funcs += discover_cals(proc_func)
|
|
#fsm_funcs += [ func_table[x] for x in fcv.func_calls ]
|
|
|
|
print("Function Table")
|
|
for f in fsm_funcs:
|
|
print(f" - {f.decl.name} {'<<< entry' if (f.decl.name == args.func) else ''}")
|
|
print("")
|
|
|
|
config_cond = None
|
|
cond_blacklist = []
|
|
cond_whitelist = []
|
|
if args.conditional is not None:
|
|
config_cond = args.conditional
|
|
print(f"Configuration Conditional {config_cond}")
|
|
config_type = None
|
|
config_enums = None
|
|
for name,enum_type in enum_table.items():
|
|
ev = EnumVisitor()
|
|
ev.visit(enum_type)
|
|
if config_cond in ev.enum_names:
|
|
config_type = name
|
|
config_enums = ev.enum_names
|
|
break
|
|
if config_type is not None:
|
|
print(f" - Type is {config_type}")
|
|
cond_whitelist.append(config_cond)
|
|
print( " - Enum Blacklist is")
|
|
for enum in config_enums:
|
|
if enum != config_cond:
|
|
print(f" * {enum}")
|
|
cond_blacklist.append(enum)
|
|
else:
|
|
print("No type found for conditional, ignoring")
|
|
print("")
|
|
|
|
print("Enum Table")
|
|
if args.enum in enum_table:
|
|
ev = EnumVisitor()
|
|
ev.visit(enum_table[args.enum])
|
|
for ename in ev.enum_names:
|
|
print(" - ",ename)
|
|
states.append(ename)
|
|
else:
|
|
print(f"Initial State Enum '{args.enum}' not found")
|
|
print("")
|
|
|
|
print("Extracting State Transitions")
|
|
sav = StateAssignmentVisitor(func_table, ass)
|
|
sav.visit(proc_func)
|
|
for assignment in sav.assignments:
|
|
state_to = assignment.state
|
|
cpa = ConditionalPathAnalyzer()
|
|
#print("Path", path_to_str(assignment.path))
|
|
cpa.analyze(reversed(assignment.path))
|
|
cond_chain = []
|
|
conds_state = []
|
|
state_from_expr = None
|
|
disregard_transition = False
|
|
for c in cpa.condition_chain:
|
|
# filter by conditional enum
|
|
# this is far from correct
|
|
cond_blacklist_visitor = ContainsOneOfIdVisitor(cond_blacklist)
|
|
cond_blacklist_visitor.visit(c)
|
|
if cond_blacklist_visitor.hit:
|
|
disregard_transition = True
|
|
break
|
|
conditional_visitor = ContainsOneOfIdVisitor(input_ids)
|
|
conditional_visitor.visit(c)
|
|
if conditional_visitor.hit:
|
|
cond_chain.append(c)
|
|
state_condition_visitor = ContainsOneOfIdVisitor([state_id])
|
|
state_condition_visitor.visit(c)
|
|
if state_condition_visitor.hit:
|
|
conds_state.append(c)
|
|
|
|
if not(disregard_transition):
|
|
if len(conds_state) != 1:
|
|
cond_exprs = [cg.visit(x) for x in cpa.condition_chain]
|
|
print("OOPS")
|
|
print(cond_exprs)
|
|
raise Exception("No or too many state conditions found")
|
|
|
|
# find out from which state the assignment transitions
|
|
current_state_visitor = ContainsOneOfIdVisitor(states)
|
|
current_state_visitor.visit(conds_state[0])
|
|
if not(current_state_visitor.hit):
|
|
raise Exception("State assignment does not assign state enum")
|
|
state_from = current_state_visitor.name
|
|
condition = None
|
|
if len(cond_chain) == 1:
|
|
condition = cond_chain[0]
|
|
elif len(cond_chain) > 1:
|
|
condition = cond_chain[0]
|
|
for expr in cond_chain[1:len(cond_chain)]:
|
|
condition = c_ast.BinaryOp('&&', expr, condition)
|
|
|
|
cond_exprs = [cg.visit(x) for x in cond_chain]
|
|
cond_expr = cg.visit(condition)
|
|
|
|
tf = NuSmvConditionTransformer()
|
|
cond_mod = tf.transform(condition)
|
|
mod_expr = cg.visit(cond_mod)
|
|
|
|
print(f" - {state_from} -> {state_to} on {mod_expr}")
|
|
tran_table.append(StateTransition(state_from, state_to, condition))
|
|
print("")
|
|
# Extract properties
|
|
av = AssignmentVisitor(func_table)
|
|
av.visit(proc_func)
|
|
assigments = []
|
|
print("Assignments")
|
|
for a in av.assignments:
|
|
if a.node.op == '=':
|
|
if isinstance(a.node.lvalue, c_ast.StructRef):
|
|
if not(check_structref_equivalence(a.node.lvalue, ass)):
|
|
assigments.append(a)
|
|
|
|
for a in assigments:
|
|
cpa = ConditionalPathAnalyzer()
|
|
#print("Path", path_to_str(assignment.path))
|
|
cpa.analyze(reversed(a.path))
|
|
cond_chain = []
|
|
conds_state = []
|
|
state_from_expr = None
|
|
disregard_transition = False
|
|
for c in cpa.condition_chain:
|
|
# filter by conditional enum
|
|
# this is far from correct
|
|
cond_blacklist_visitor = ContainsOneOfIdVisitor(cond_blacklist)
|
|
cond_blacklist_visitor.visit(c)
|
|
if cond_blacklist_visitor.hit:
|
|
disregard_transition = True
|
|
break
|
|
conditional_visitor = ContainsOneOfIdVisitor(input_ids)
|
|
conditional_visitor.visit(c)
|
|
if conditional_visitor.hit:
|
|
cond_chain.append(c)
|
|
state_condition_visitor = ContainsOneOfIdVisitor([state_id])
|
|
state_condition_visitor.visit(c)
|
|
if state_condition_visitor.hit:
|
|
conds_state.append(c)
|
|
|
|
if not(disregard_transition):
|
|
cond_exprs = [cg.visit(x) for x in cond_chain]
|
|
state_cond_exprs = [cg.visit(x) for x in conds_state]
|
|
print(f" - {cg.visit(a.node)} {cond_exprs} {state_cond_exprs}")
|
|
|
|
mod = ModelBuilder(states, initial_state, tran_table, properties)
|
|
nusmv = mod.generate()
|
|
print(nusmv)
|