class DotGraph: def __init__(self, functions): self.node_dic = {} self.edge_dic = {} self.graph = Dot() self.html = None for f in functions: if f.name in [ 'slitherConstructorVariables', 'slitherConstructorConstantVariables' ]: continue self.construct_node(f) self.construct_graph(functions) self.html = svg_to_html(self.graph.create_svg().decode('utf-8')) def construct_node(self, function): n = Node(function.name) n.set_tooltip(construct_tooltip(function)) self.node_dic[function.name] = n self.graph.add_node(n) def construct_graph(self, functions): for f in functions: if (f.name in [ 'slitherConstructorVariables', 'slitherConstructorConstantVariables' ] or f.slither_function.is_constructor): continue for sr in f.read: for written_f in sr.written_by: if written_f.name not in [ 'slitherConstructorVariables', 'slitherConstructorConstantVariables' ]: n1 = self.get_node(f.name) n2 = self.get_node(written_f.name) if self.edge_dic.get((n1, n2)): e = self.edge_dic[(n1, n2)] old_label = e.get_label() e.set_label( f'{old_label.strip()}, {sr.name} ') else: self.construct_edge(n1, n2) e = self.edge_dic[(n1, n2)] e.set_label(f'{sr.name} ') def construct_edge(self, _n1: Node, _n2: Node): e = Edge(_n1, _n2, fontsize="8", fontcolor="#2E86C1", arrowsize="0.7") self.edge_dic[(_n1, _n2)] = e self.graph.add_edge(e) def get_node(self, _name): return self.node_dic.get(_name)
class DependencyGraph: def __init__(self, _contract: Contract): """ Takes a Contract object and constructs the DDG for it. *** To be completed Currently cannot detect indirect read. Indirect write can be detected. """ self.contract = _contract self.node_dic = {} self.edge_dic = {} self.graph = Dot() for f in _contract.functions + _contract.constructor_as_list: if f.name in ['slitherConstructorVariables', 'slitherConstructorConstantVariables'] or not f.is_public_or_external: continue self.construct_node(f) self.construct_graph(_contract) @property def html(self): return svg_to_html(self.graph.create_svg().decode('utf-8')) def construct_graph(self, _contract): """ Constructs the graph by connecting nodes with edges. """ for f in _contract.functions + _contract.constructor_as_list: if f.name in ['slitherConstructorVariables', 'slitherConstructorConstantVariables'] or not f.is_public_or_external: continue for dependency in f.depends_on: written_f, sr = dependency[0], dependency[1] n1 = self.get_node(f.name) n2 = self.get_node(written_f.name) if self.edge_dic.get((n1, n2)): e = self.edge_dic[(n1, n2)] old_label = e.get_label() e.set_label(f'{old_label.strip()}, {sr.name} ') else: self.construct_edge(n1, n2) e = self.edge_dic[(n1, n2)] e.set_label(f'{sr.name} ') def update_graph(self): remaining_edges_dic = defaultdict(list) for f in self.contract.functions + self.contract.constructor_as_list: if f.name in ['slitherConstructorVariables', 'slitherConstructorConstantVariables'] or not f.is_public_or_external: continue for dependency in f.depends_on: written_f, sr = dependency[0], dependency[1] n1 = self.get_node(f.name) n2 = self.get_node(written_f.name) remaining_edges_dic[(n1, n2)].append(sr) existing_edges = self.edge_dic.keys() remaining_edges = remaining_edges_dic.keys() for ex_edge in existing_edges: if ex_edge not in remaining_edges: remove_edge(self.edge_dic[ex_edge]) # no need to check if only one state variable dependency is remove # because if a dependency between two functions is removed # all of its dependency based on whatever variable will be removed. def construct_node(self, _function: Function): """ Takes a Function object and constructs a Dot Node object for it. Adds the created object to the dictionary. Finished. """ n = Node(_function.name) n.set_tooltip(construct_tooltip(_function)) self.node_dic[_function.name] = n self.graph.add_node(n) def get_node(self, _name): return self.node_dic.get(_name) def construct_edge(self, _n1: Node, _n2: Node): """ Takes two nodes n1 depends on n2 n1 points to n2 Constructs the edge object and adds it to the dictionary. Finished. """ e = Edge(_n1, _n2, fontsize="8", fontcolor="#2E86C1", arrowsize="0.7") self.edge_dic[(_n1, _n2)] = e # e.set('color', 'green') self.graph.add_edge(e)
class SysCDiagramPlugin(): def initPlugin(self, signalproxy): """Initialise the systemc block diagram plugin""" self.signalproxy = signalproxy self.do = signalproxy.distributedObjects # systemc stuff and and pointer type strings needed for casting in gdb # because systemc objects can be systemc modules, systemc ports, etc. self.ctx = None self.ctx_pointer = None self.ctx_func = "sc_get_curr_simcontext()" self.ctx_found = False self.ctx_type = "(sc_core::sc_simcontext*)" self.port_type = "(sc_core::sc_port_base*)" self.module_type = "(sc_core::sc_module*)" self.object_type = "(sc_core::sc_object*)" self.prim_channel_type = "(sc_core::sc_prim_channel*)" # dict with a string that represents the pointer as key and # a nested dict with parent, children, etc as key-values # dst_dict[ptr] = {"wrapper": vw, # "name": None, # "parent_ptr": None, # "children": {}} self.sysc_modules = {} self.sysc_objects = {} self.sysc_ports = {} self.sysc_prim_channels = {} # because of how we built the interface for the tracepoints on the # datagraph we first need to create an file obj. We choose StringIO # because it uses a string as buffer and does not acces the filesystem self._file_obj = StringIO() self.image = SVGImage("SystemC Block Diagram", self._file_obj) self.image_wrapper = SVGDataGraphVW(self.image, self.do) self.signalproxy.inferiorStoppedNormally.connect(self.update) # hook Datagraph variable wrapper into the datagraph controller # after self.action.commit is called the image will be displayed at the # datagraph self.action = self.do.actions.\ getAddSVGToDatagraphAction(self.image_wrapper, self.do. datagraphController.addVar) # pydot graph visualization library self.block_diagram = Dot(graph_type='digraph') self.block_diagram.set_graph_defaults(compound='true', splines='ortho', rankdir='LR') self.block_diagram.set_node_defaults(shape='box') # Needed for creating the right variables and variable wrappers from # the systemc pointers self.vwFactory = VarWrapperFactory() self.variableList = VariableList(self.vwFactory, self.do) def deInitPlugin(self): pass def evaluateExp(self, exp): return self.signalproxy.gdbEvaluateExpression(exp) def showDiagram(self): self.action.commit() def update(self): if not self.ctx_found and self.ctx is None: self.__findSimContext() if self.ctx is None: return else: # don't try to analyze if elaboration is not done if not cpp2py(self.ctx["m_elaboration_done"].value): return # prepare for easy information collection object_vec = self.ctx["m_child_objects"] # find all relevant information self.__findSysCObjects(object_vec, self.object_type, self.sysc_objects) # if there are no objects to draw than skip the drawing part # this might happen if you set the breakpoint before any objects are # created. This is actually catched above, but there might also be a # design with no objects. if len(self.sysc_objects.keys()) == 0: return clusters = {} nodes = {} # build pydot hierachy and add all subgraphs and nodes to the main # graph self.__buildHierachy(self.sysc_objects, clusters, nodes) for sptr in clusters: self.block_diagram.add_subgraph(clusters[sptr]) for sptr in nodes: self.block_diagram.add_node(nodes[sptr]) self._file_obj.write(self.block_diagram.create_svg()) self.signalproxy.inferiorStoppedNormally.disconnect(self.update) self.showDiagram() def __buildHierachy(self, obj_dict, clusters, nodes): """ Build Cluster and Node hierachy for pydot """ for ptr in obj_dict: obj = obj_dict[ptr] if ptr in (self.sysc_ports.keys()+self.sysc_prim_channels.keys()): continue if len(obj["children"].keys()) == 0: node = Node(obj["name"]) nodes[ptr] = node else: clust = Cluster(obj["name"].replace(".", "_"), color='red', label=obj["name"]) c_clusters = {} c_nodes = {} self.__buildHierachy(obj["children"], c_clusters, c_nodes) for sptr in c_clusters: clust.add_subgraph(c_clusters[sptr]) for sptr in c_nodes: clust.add_node(c_nodes[sptr]) clusters[ptr] = clust def __findSysCObjects(self, obj_vec, obj_type, dst_dict): """ Find sc_object from module, port and prim channel registry """ for i in obj_vec.childs: ptr = i.value var = "(*{}{})".format(obj_type, ptr) vw = self.variableList.addVarByName(var) dst_dict[ptr] = {"wrapper": vw, "name": None, "parent_ptr": None, "children": {}} for member in vw.childs: if member.exp == "m_name": dst_dict[ptr]["name"] = member.value.strip('"') elif member.exp == "m_child_objects": children = {} self.__findSysCObjects(vw["m_child_objects"], obj_type, children) dst_dict[ptr]["children"] = children elif member.exp == "m_parent": dst_dict[ptr]["parent_ptr"] = member.value def __findSimContext(self): """ Find systemc simulation context """ self.ctx_pointer = self.evaluateExp(self.ctx_func) if self.ctx is None: frame = 0 depth = self.signalproxy.gdbGetStackDepth() while (self.ctx_pointer is None) and frame <= depth: frame += 1 self.signalproxy.gdbSelectStackFrame(frame) self.ctx_pointer = self.evaluateExp(self.ctx_func) else: if self.ctx_pointer is None: self.ctx_found = False return else: self.ctx_found = True self.ctx = self.do.variablePool.getVar(self.ctx_func)["*"] else: self.ctx_found = True self.ctx = self.do.variablePool.getVar(self.ctx_func)["*"]