def __generate_call(self, ep, func, obj, identifier): # Add declaration of caller caller_func = Function("emg_{}_caller_{}".format(func, identifier), "void a(void)") ep.add_declaration("environment model", caller_func.name, caller_func.declare(True)[0]) expression = "" body = [] initializations = [] # Check retval and cast to void call # if obj.declaration.return_value and obj.declaration.return_value.identifier != 'void': # expression += "(void) " # Get arguments and allocate memory for them args = [] free_args = [] for index, arg in enumerate(obj.declaration.parameters): if not isinstance(arg, str): argvar = Variable("emg_arg_{}".format(index), arg) body.append(argvar.declare() + ";") args.append(argvar.name) if isinstance(arg, Pointer): elements = self.conf.get( "initialize strings as null terminated") if elements and arg == 'char **': if isinstance(elements, int) or elements.isnumeric(): elements = int(elements) else: elements = 'ldv_undef_int()' argvar_len = Variable(argvar.name + '_len', 'int') # Define explicitly number of arguments, since undef value is too difficult sometimes initializations.append("int {} = {};".format( argvar_len.name, elements)) initializations.append( "{} = (char **) ldv_xmalloc({} * sizeof(char *));". format(argvar.name, argvar_len.name)) # Initialize all elements but the last one initializations.append( "for (int i = 0; i < {} - 1; i++)".format( argvar_len.name)) # Some undefined data initializations.append( "\t{}[i] = (char *) external_allocated_data();". format(argvar.name)) # The last element is a string initializations.append("{}[{}] = (char * ) 0;".format( argvar.name, elements - 1)) free_args.append(argvar.name) elif self.conf.get("allocate external", True): value = "external_allocated_data();" initializations.append("{} = {}".format( argvar.name, value)) else: if self.conf.get("allocate with sizeof", True): apt = arg.points.to_string( '', typedef='complex_and_params') value = "ldv_xmalloc(sizeof({}));".\ format(apt if apt != 'void' else apt + '*') else: value = "ldv_xmalloc_unknown_size(0);" free_args.append(argvar.name) initializations.append("{} = {}".format( argvar.name, value)) # Generate call if func != "main": expression += "int ret = " expression += "{}({});".format(func, ", ".join(args)) # Generate function body body += initializations + [expression] if func != "main": body += ["ldv_assume(ret==0);"] # Free memory for arg in free_args: body.append("ldv_free({});".format(arg)) caller_func.body = body # Add definition of caller ep.add_definition(obj.definition_file, caller_func.name, caller_func.define() + ["\n"]) # Return call expression return "{}();".format(caller_func.name)
def _dispatch_blocks(self, action, automaton, function_parameters, automata_peers, replicative): pre = [] post = [] blocks = [] for a_peer in (a for a in automata_peers if automata_peers[a]['actions']): decl = self._get_cf_struct(automaton, function_parameters) cf_param = 'cf_arg_{}'.format(str(a_peer)) vf_param_var = Variable(cf_param, decl.take_pointer) pre.append(vf_param_var.declare() + ';') if replicative: for r_action in automata_peers[a_peer]['actions']: block = list() block.append('{} = {}(sizeof({}));'.format( vf_param_var.name, self._cmodel.mem_function_map["ALLOC"], str(decl))) for index in range(len(function_parameters)): block.append('{}->arg{} = arg{};'.format( vf_param_var.name, index, index)) if r_action.replicative: call = self._call_cf(a_peer, cf_param) if self._conf.get('direct control functions calls'): block.append(call) else: if a_peer.self_parallelism and self._conf.get("self parallel processes") and \ self._conf.get('pure pthread interface'): thread_vars = self.__thread_variable( a_peer, var_type='pair') for v in thread_vars: # Expect that for this particular case the first argument is unset block.extend([ 'ret = {}'.format( call.format("& " + v.name)), 'ldv_assume(ret == 0);' ]) else: block.extend([ 'ret = {}'.format(call), 'ldv_assume(ret == 0);' ]) blocks.append(block) break else: self._logger.warning( 'Cannot generate dispatch based on labels for receive {} in process {} with category {}' .format(r_action.name, a_peer.process.name, a_peer.process.category)) # todo: Pretty ugly, but works elif action.name.find('dereg') != -1: block = list() call = self._join_cf(automata_peers[a_peer]['automaton']) if not self._conf.get('direct control functions calls'): if automata_peers[a_peer]['automaton'].self_parallelism and self._conf.get("self parallel processes")\ and self._conf.get('pure pthread interface'): thread_vars = self.__thread_variable( automata_peers[a_peer]['automaton'], var_type='pair') for v in thread_vars: # Expect that for this particular case the first argument is unset block.extend([ 'ret = {}'.format(call.format(v.name)), 'ldv_assume(ret == 0);' ]) else: block.extend( ['ret = {}'.format(call), 'ldv_assume(ret == 0);']) blocks.append(block) return pre, blocks, post
def _import_code_analysis(logger, conf, clade, dependencies, collection): # Import typedefs if there are provided logger.info("Extract complete types definitions") typedef = clade.get_typedefs( set(dependencies.keys()).union(collection.cfiles)) if typedef: import_typedefs(typedef, dependencies) variables = clade.get_variables(set(collection.cfiles)) if variables: logger.info("Import global variables initializations") for path, vals in variables.items(): for variable in vals: variable_name = extract_name(variable['declaration']) if not variable_name: raise ValueError('Global variable without a name') var = Variable(variable_name, variable['declaration']) # Here we know, that if we met a variable in an another file then it is an another variable because # a program should contain a single global variable initialization collection.set_source_variable(var, path) var.declaration_files.add(path) var.initialization_file = path if 'value' in variable: var.value = variable['value'] # Variables which are used in variables initializations logger.info("Import source functions") vfunctions = clade.get_used_in_vars_functions() # Get functions defined in dependencies and in the main functions and have calls cg = clade.get_callgraph(set(dependencies.keys())) # Function scope definitions # todo: maybe this should be fixed in Clade # As we will not get definitions for library functions if there are in compiled parts we should add all scopes # that are given for all function called from outside of the code we analyze for scope in (s for s in collection.cfiles if s in cg): for func in (f for f in cg[scope] if cg[scope][f].get('calls')): for dep in cg[scope][func].get('calls'): dependencies.setdefault(dep, sortedcontainers.SortedSet()) dependencies[dep].add(scope) fs = clade.get_functions_by_file( set(dependencies.keys()).union(collection.cfiles)) # Add called functions for scope in cg: for func in cg[scope]: desc = cg[scope][func] if scope in collection.cfiles: # Definition of the function is in the code of interest try: collection.add_function(func, scope, fs, dependencies, collection.cfiles) except ValueError: pass # Add called functions for def_scope, cf_desc in desc.get('calls', dict()).items(): if def_scope not in collection.cfiles: for called_func in ( f for f in cf_desc if def_scope in fs and f in fs[def_scope]): collection.add_function(called_func, def_scope, fs, dependencies, collection.cfiles) elif ('called_in' in desc and set(desc['called_in'].keys()).intersection( collection.cfiles)) or func in vfunctions: if scope in fs and func in fs[scope]: # Function is called in the target code but defined in dependencies collection.add_function(func, scope, fs, dependencies, collection.cfiles) elif scope != 'unknown': logger.warning( "There is no information on declarations of function {!r} from {!r} scope" .format(func, scope)) # Add functions missed in the call graph for scope in (s for s in fs if s in collection.cfiles): for func in fs[scope]: func_intf = collection.get_source_function(func, scope) if not func_intf: try: collection.add_function(func, scope, fs, dependencies, collection.cfiles) except ValueError: pass for func in collection.source_functions: for obj in collection.get_source_functions(func): scopes = set(obj.declaration_files).union(set(obj.header_files)) if not obj.definition_file: # It is likely be this way scopes.add('unknown') for scope in (s for s in scopes if cg.get(s, dict()).get(func)): for cscope, desc in ((s, d) for s, d in cg[scope][func].get( 'called_in', {}).items() if s in collection.cfiles): for caller in desc: for line in desc[caller]: params = desc[caller][line].get('args') caller_intf = collection.get_source_function( caller, cscope) obj.add_call(caller, cscope) if params: # Here can be functions which are not defined or visible for _, passed_func in list(params): passed_obj = collection.get_source_function( passed_func, cscope) if not passed_obj: passed_scope = collection.search_function( passed_func, cscope, fs) if passed_scope: collection.add_function( passed_func, passed_scope, fs, dependencies, collection.cfiles) else: logger.warning( "Cannot find function {!r} from scope {!r}" .format(passed_func, cscope)) # Ignore this call since model will not be correct without signature params = None break caller_intf.call_in_function(obj, params) if obj.definition_file and obj.definition_file in scopes and obj.definition_file in cg and \ func in cg[obj.definition_file]: for called_def_scope in cg[obj.definition_file][func].get( 'calls', dict()): for called_func in cg[obj.definition_file][func]['calls'][ called_def_scope]: called_obj = collection.get_source_function( called_func, paths={obj.definition_file}) if called_obj: called_obj.add_call(func, obj.definition_file) logger.debug("The following functions were imported: {}".format(', '.join( collection.source_functions))) macros_file = conf.get('macros white list', 'linux/emg/macros white list.json') if macros_file: macros_file = find_file_or_dir(logger, conf['main working directory'], macros_file) with open(macros_file, 'r', encoding='utf-8') as fp: white_list = sorted(ujson.load(fp)) if white_list: macros = clade.get_macros_expansions(sorted(collection.cfiles), white_list) for path, macros in macros.items(): for macro, desc in macros.items(): obj = collection.get_macro(macro) if not obj: obj = Macro(macro) for call in desc.get('args', []): obj.add_parameters(path, call) collection.set_macro(obj)
def _receive(self, action, automaton): code, v_code, conditions, comments = super(LabelTranslator, self)._receive( action, automaton) automata_peers = {} if len(action.peers) > 0: # Do call only if model which can be called will not hang extract_relevant_automata( self._logger, self._event_fsa + self._model_fsa + [self._entry_fsa], automata_peers, action.peers, Dispatch) # Add additional condition if action.replicative: param_declarations = [] param_expressions = [] if len(action.parameters) > 0: for index, param in enumerate(action.parameters): receiver_access = automaton.process.resolve_access( param) var = automaton.determine_variable( receiver_access.label) param_declarations.append(var.declaration) param_expressions.append(var.name) if action.condition and len(action.condition) > 0: # Arguments comparison is not supported in label-based model for statement in action.condition: # Replace first $ARG expressions s = statement for index, _ in enumerate(param_expressions): replacement = 'data->arg{}'.format(index) s = s.replace("$ARG{}".format(index + 1), replacement) cn = self._cmodel.text_processor(automaton, s) conditions.extend(cn) # This should be before precondition because it may check values unpacked in this section if len(param_declarations) > 0: decl = self._get_cf_struct( automaton, [val for val in param_declarations]) var = Variable('data', decl.take_pointer) v_code += [ '/* Received labels */', '{} = ({}*) arg0;'.format( var.declare(), decl.to_string('', typedef='complex')), '' ] code += ['/* Assign recieved labels */', 'if (data) {'] + \ ['\t{} = data->arg{};'.format(v, i) for i, v in enumerate(param_expressions)] + \ ['\t{}({});'.format(self._cmodel.free_function_map["FREE"], 'data'), '}'] else: code.append('{}({});'.format( self._cmodel.free_function_map["FREE"], 'arg0')) else: code.append( '/* Skip a non-replicative signal receiving %s */' % action.name) # Ignore conditions conditions = [] else: # Generate comment code.append( "/* Signal receive {!r} does not expect any signal from existing processes */" .format(action.name)) return code, v_code, conditions, comments