def _track(self, state, live_defs):
        """
        Given all live definitions prior to this program point, track the changes, and return a new list of live
        definitions. We scan through the action list of the new state to track the changes.

        :param state:       The input state at that program point.
        :param live_defs:   A list of all live definitions prior to reaching this program point.
        :returns:           A list of new live definitions.
        """

        # Make a copy of live_defs
        live_defs = live_defs.copy()

        action_list = list(state.history.recent_actions)

        # Since all temporary variables are local, we simply track them in a local dict
        temps = { }

        # All dependence edges are added to the graph either at the end of this method, or when they are going to be
        # overwritten by a new edge. This is because we sometimes have to modify a  previous edge (e.g. add new labels
        # to the edge)
        temps_to_edges = defaultdict(list)
        regs_to_edges = defaultdict(list)

        def _annotate_edges_in_dict(dict_, key, **new_labels):
            """

            :param dict_:       The dict, can be either `temps_to_edges` or `regs_to_edges`
            :param key:         The key used in finding elements in the dict
            :param new_labels:  New labels to be added to those edges
            """

            for edge_tuple in dict_[key]:
                # unpack it
                _, _, labels = edge_tuple
                for k, v in new_labels.items():
                    if k in labels:
                        labels[k] = labels[k] + (v, )
                    else:
                        # Construct a tuple
                        labels[k] = (v, )

        def _dump_edge_from_dict(dict_, key, del_key=True):
            """
            Pick an edge from the dict based on the key specified, add it to our graph, and remove the key from dict.

            :param dict_:   The dict, can be either `temps_to_edges` or `regs_to_edges`.
            :param key:     The key used in finding elements in the dict.
            """
            for edge_tuple in dict_[key]:
                # unpack it
                prev_code_loc, current_code_loc, labels = edge_tuple
                # Add the new edge
                self._add_edge(prev_code_loc, current_code_loc, **labels)

            # Clear it
            if del_key:
                del dict_[key]

        for a in action_list:

            if a.bbl_addr is None:
                current_code_loc = CodeLocation(None, None, sim_procedure=a.sim_procedure)
            else:
                current_code_loc = CodeLocation(a.bbl_addr, a.stmt_idx, ins_addr=a.ins_addr)

            if a.type == "mem":
                if a.actual_addrs is None:
                    # For now, mem reads don't necessarily have actual_addrs set properly
                    addr_list = set(state.memory.normalize_address(a.addr.ast, convert_to_valueset=True))
                else:
                    addr_list = set(a.actual_addrs)

                for addr in addr_list:
                    variable = SimMemoryVariable(addr, a.data.ast.size()) # TODO: Properly unpack the SAO

                    if a.action == "read":
                        # Create an edge between def site and use site

                        prevdefs = self._def_lookup(live_defs, variable)

                        for prev_code_loc, labels in prevdefs.items():
                            self._read_edge = True
                            self._add_edge(prev_code_loc, current_code_loc, **labels)

                    else: #if a.action == "write":
                        # Kill the existing live def
                        self._kill(live_defs, variable, current_code_loc)

                    # For each of its register dependency and data dependency, we revise the corresponding edge
                    for reg_off in a.addr.reg_deps:
                        _annotate_edges_in_dict(regs_to_edges, reg_off, subtype='mem_addr')
                    for tmp in a.addr.tmp_deps:
                        _annotate_edges_in_dict(temps_to_edges, tmp, subtype='mem_addr')

                    for reg_off in a.data.reg_deps:
                        _annotate_edges_in_dict(regs_to_edges, reg_off, subtype='mem_data')
                    for tmp in a.data.tmp_deps:
                        _annotate_edges_in_dict(temps_to_edges, tmp, subtype='mem_data')

            elif a.type == 'reg':
                # For now, we assume a.offset is not symbolic
                # TODO: Support symbolic register offsets

                #variable = SimRegisterVariable(a.offset, a.data.ast.size())
                variable = SimRegisterVariable(a.offset, self.project.arch.bits)

                if a.action == 'read':
                    # What do we want to do?
                    prevdefs = self._def_lookup(live_defs, variable)

                    if a.offset in regs_to_edges:
                        _dump_edge_from_dict(regs_to_edges, a.offset)

                    for prev_code_loc, labels in prevdefs.items():
                        edge_tuple = (prev_code_loc, current_code_loc, labels)
                        regs_to_edges[a.offset].append(edge_tuple)

                else:
                    # write
                    self._kill(live_defs, variable, current_code_loc)

            elif a.type == 'tmp':
                # tmp is definitely not symbolic
                if a.action == 'read':
                    prev_code_loc = temps[a.tmp]
                    edge_tuple = (prev_code_loc, current_code_loc, {'type':'tmp', 'data':a.tmp})

                    if a.tmp in temps_to_edges:
                        _dump_edge_from_dict(temps_to_edges, a.tmp)

                    temps_to_edges[a.tmp].append(edge_tuple)

                else:
                    # write
                    temps[a.tmp] = current_code_loc

            elif a.type == 'exit':
                # exits should only depend on tmps

                for tmp in a.tmp_deps:
                    prev_code_loc = temps[tmp]
                    edge_tuple = (prev_code_loc, current_code_loc, {'type': 'exit', 'data': tmp})

                    if tmp in temps_to_edges:
                        _dump_edge_from_dict(temps_to_edges, tmp)

                    temps_to_edges[tmp].append(edge_tuple)

        # In the end, dump all other edges in those two dicts
        for reg_offset in regs_to_edges:
            _dump_edge_from_dict(regs_to_edges, reg_offset, del_key=False)
        for tmp in temps_to_edges:
            _dump_edge_from_dict(temps_to_edges, tmp, del_key=False)

        return live_defs
def _track ( self , state , live_defs ) : live_defs = live_defs . copy ( ) action_list = list ( state . history . recent_actions ) temps = { } temps_to_edges = defaultdict ( list ) regs_to_edges = defaultdict ( list ) def _annotate_edges_in_dict ( dict_ , key , ** new_labels ) : for edge_tuple in dict_ [ key ] : _ , _ , labels = edge_tuple for k , v in new_labels . items ( ) : if k in labels : labels [ k ] = labels [ k ] + ( v , ) else : labels [ k ] = ( v , ) def _dump_edge_from_dict ( dict_ , key , del_key = True ) : for edge_tuple in dict_ [ key ] : prev_code_loc , current_code_loc , labels = edge_tuple self . _add_edge ( prev_code_loc , current_code_loc , ** labels ) if del_key : del dict_ [ key ] for a in action_list : if a . bbl_addr is None : current_code_loc = CodeLocation ( None , None , sim_procedure = a . sim_procedure ) else : current_code_loc = CodeLocation ( a . bbl_addr , a . stmt_idx , ins_addr = a . ins_addr ) if a . type == "mem" : if a . actual_addrs is None : addr_list = set ( state . memory . normalize_address ( a . addr . ast , convert_to_valueset = True ) ) else : addr_list = set ( a . actual_addrs ) for addr in addr_list : variable = SimMemoryVariable ( addr , a . data . ast . size ( ) ) if a . action == "read" : prevdefs = self . _def_lookup ( live_defs , variable ) for prev_code_loc , labels in prevdefs . items ( ) : self . _read_edge = True self . _add_edge ( prev_code_loc , current_code_loc , ** labels ) else : self . _kill ( live_defs , variable , current_code_loc ) for reg_off in a . addr . reg_deps : _annotate_edges_in_dict ( regs_to_edges , reg_off , subtype = 'mem_addr' ) for tmp in a . addr . tmp_deps : _annotate_edges_in_dict ( temps_to_edges , tmp , subtype = 'mem_addr' ) for reg_off in a . data . reg_deps : _annotate_edges_in_dict ( regs_to_edges , reg_off , subtype = 'mem_data' ) for tmp in a . data . tmp_deps : _annotate_edges_in_dict ( temps_to_edges , tmp , subtype = 'mem_data' ) elif a . type == 'reg' : variable = SimRegisterVariable ( a . offset , self . project . arch . bits ) if a . action == 'read' : prevdefs = self . _def_lookup ( live_defs , variable ) if a . offset in regs_to_edges : _dump_edge_from_dict ( regs_to_edges , a . offset ) for prev_code_loc , labels in prevdefs . items ( ) : edge_tuple = ( prev_code_loc , current_code_loc , labels ) regs_to_edges [ a . offset ] . append ( edge_tuple ) else : self . _kill ( live_defs , variable , current_code_loc ) elif a . type == 'tmp' : if a . action == 'read' : prev_code_loc = temps [ a . tmp ] edge_tuple = ( prev_code_loc , current_code_loc , { 'type' : 'tmp' , 'data' : a . tmp } ) if a . tmp in temps_to_edges : _dump_edge_from_dict ( temps_to_edges , a . tmp ) temps_to_edges [ a . tmp ] . append ( edge_tuple ) else : temps [ a . tmp ] = current_code_loc elif a . type == 'exit' : for tmp in a . tmp_deps : prev_code_loc = temps [ tmp ] edge_tuple = ( prev_code_loc , current_code_loc , { 'type' : 'exit' , 'data' : tmp } ) if tmp in temps_to_edges : _dump_edge_from_dict ( temps_to_edges , tmp ) temps_to_edges [ tmp ] . append ( edge_tuple ) for reg_offset in regs_to_edges : _dump_edge_from_dict ( regs_to_edges , reg_offset , del_key = False ) for tmp in temps_to_edges : _dump_edge_from_dict ( temps_to_edges , tmp , del_key = False ) return live_defs
Given all live definitions prior to this program point, track the changes, and return a new list of live
        definitions. We scan through the action list of the new state to track the changes.

        :param state:       The input state at that program point.
        :param live_defs:   A list of all live definitions prior to reaching this program point.
        :returns:           A list of new live definitions.