'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.
:param list statements: A list of VEX statements.
:returns:           A list of new live definitions.'

def _track(self, state, live_defs, statements):

    live_defs = live_defs.copy()
    action_list = list(state.history.recent_actions)
    temp_defs = {}
    temp_variables = {}
    temp_register_symbols = {}
    temps_to_edges = defaultdict(list)
    regs_to_edges = defaultdict(list)
    last_statement_id = None
    pv_read = None
    data_generated = None
    for a in action_list:
        if ((last_statement_id is None) or (last_statement_id != a.stmt_idx)):
            pv_read = []
            data_generated = None
            last_statement_id = a.stmt_idx
        if (a.sim_procedure is None):
            current_code_location = CodeLocation(a.bbl_addr, a.stmt_idx, ins_addr=a.ins_addr)
        else:
            current_code_location = CodeLocation(None, None, sim_procedure=a.sim_procedure)
        if (a.type == 'mem'):
            if (a.actual_addrs is None):
                try:
                    addr_list = {state.se.any_int(a.addr.ast)}
                except (SimSolverModeError, SimUnsatError, ZeroDivisionError):
                    addr_list = {1610612736}
            else:
                addr_list = set(a.actual_addrs)
            for addr in addr_list:
                variable = None
                if ((len(addr_list) == 1) and (len(a.addr.tmp_deps) == 1)):
                    addr_tmp = list(a.addr.tmp_deps)[0]
                    if (addr_tmp in temp_register_symbols):
                        (sort, offset) = temp_register_symbols[addr_tmp]
                        variable = SimStackVariable(offset, (a.size.ast / 8), base=sort, base_addr=(addr - offset))
                if (variable is None):
                    variable = SimMemoryVariable(addr, (a.size.ast / 8))
                pvs = []
                if (a.action == 'read'):
                    prevdefs = self._def_lookup(live_defs, variable)
                    for (prev_code_loc, labels) in prevdefs.iteritems():
                        self._stmt_graph_add_edge(prev_code_loc, current_code_location, **labels)
                        pvs.append(ProgramVariable(variable, prev_code_loc))
                    if (not pvs):
                        pvs.append(ProgramVariable(variable, current_code_location, initial=True))
                        self._kill(live_defs, variable, current_code_location)
                    for pv in pvs:
                        pv_read.append(pv)
                if (a.action == 'write'):
                    self._kill(live_defs, variable, current_code_location)
                    pvs.append(ProgramVariable(variable, current_code_location))
                for pv in pvs:
                    for reg_offset in a.addr.reg_deps:
                        self._stmt_graph_annotate_edges(regs_to_edges[reg_offset], subtype='mem_addr')
                        reg_variable = SimRegisterVariable(reg_offset, self._get_register_size(reg_offset))
                        prev_defs = self._def_lookup(live_defs, reg_variable)
                        for (loc, _) in prev_defs.iteritems():
                            v = ProgramVariable(reg_variable, loc)
                            self._data_graph_add_edge(v, pv, type='mem_addr')
                    for tmp in a.addr.tmp_deps:
                        self._stmt_graph_annotate_edges(temps_to_edges[tmp], subtype='mem_addr')
                        if (tmp in temp_variables):
                            self._data_graph_add_edge(temp_variables[tmp], pv, type='mem_addr')
                    for reg_offset in a.data.reg_deps:
                        self._stmt_graph_annotate_edges(regs_to_edges[reg_offset], subtype='mem_data')
                        reg_variable = SimRegisterVariable(reg_offset, self._get_register_size(reg_offset))
                        prev_defs = self._def_lookup(live_defs, reg_variable)
                        for (loc, _) in prev_defs.iteritems():
                            v = ProgramVariable(reg_variable, loc)
                            self._data_graph_add_edge(v, pv, type='mem_data')
                    for tmp in a.data.tmp_deps:
                        self._stmt_graph_annotate_edges(temps_to_edges[tmp], subtype='mem_data')
                        if (tmp in temp_variables):
                            self._data_graph_add_edge(temp_variables[tmp], pv, type='mem_data')
        elif (a.type == 'reg'):
            reg_offset = a.offset
            variable = SimRegisterVariable(reg_offset, (a.data.ast.size() / 8))
            if (a.action == 'read'):
                prevdefs = self._def_lookup(live_defs, variable)
                for (prev_code_loc, labels) in prevdefs.iteritems():
                    self._stmt_graph_add_edge(prev_code_loc, current_code_location, **labels)
                    edge_tuple = (prev_code_loc, current_code_location)
                    regs_to_edges[reg_offset].append(edge_tuple)
                    pv_read.append(ProgramVariable(variable, prev_code_loc))
                if (not prevdefs):
                    pv_read.append(ProgramVariable(variable, current_code_location, initial=True))
                    self._kill(live_defs, variable, current_code_location)
                if (reg_offset == self.project.arch.sp_offset):
                    data_generated = ('sp', 0)
                elif (reg_offset == self.project.arch.bp_offset):
                    data_generated = ('bp', 0)
            else:
                self._kill(live_defs, variable, current_code_location)
                if (reg_offset in regs_to_edges):
                    del regs_to_edges[reg_offset]
                pv = ProgramVariable(variable, current_code_location)
                self._data_graph_add_node(pv)
                if ((not a.reg_deps) and (not a.tmp_deps)):
                    const_variable = SimConstantVariable()
                    if (statements is not None):
                        stmt = statements[a.stmt_idx]
                        if isinstance(stmt.data, pyvex.IRExpr.Const):
                            const_variable = SimConstantVariable(value=stmt.data.con.value)
                    const_pv = ProgramVariable(const_variable, current_code_location)
                    self._data_graph_add_edge(const_pv, pv)
                for tmp in a.tmp_deps:
                    if (tmp in temp_variables):
                        self._data_graph_add_edge(temp_variables[tmp], pv)
        elif (a.type == 'tmp'):
            tmp = a.tmp
            pv = ProgramVariable(SimTemporaryVariable(tmp), current_code_location)
            if (a.action == 'read'):
                prev_code_loc = temp_defs[tmp]
                self._stmt_graph_add_edge(prev_code_loc, current_code_location, type='tmp', data=a.tmp)
                edge_tuple = (prev_code_loc, current_code_location)
                temps_to_edges[a.tmp].append(edge_tuple)
                if (tmp in temp_register_symbols):
                    data_generated = temp_register_symbols[tmp]
            else:
                temp_defs[tmp] = current_code_location
                temp_variables[tmp] = pv
                if (tmp in temps_to_edges):
                    del temps_to_edges[tmp]
                for tmp_dep in a.tmp_deps:
                    if (tmp_dep in temp_variables):
                        self._data_graph_add_edge(temp_variables[tmp_dep], pv)
                if data_generated:
                    temp_register_symbols[tmp] = data_generated
                for data in pv_read:
                    self._data_graph_add_edge(data, pv)
                if ((not a.tmp_deps) and (not pv_read)):
                    const_variable = SimConstantVariable()
                    if (statements is not None):
                        stmt = statements[a.stmt_idx]
                        if isinstance(stmt, pyvex.IRStmt.Dirty):
                            l.warning('Dirty    statements    are    not    supported    in    DDG    for    now.')
                        elif isinstance(stmt.data, pyvex.IRExpr.Const):
                            const_variable = SimConstantVariable(value=stmt.data.con.value)
                    const_pv = ProgramVariable(const_variable, current_code_location)
                    self._data_graph_add_edge(const_pv, pv)
        elif (a.type == 'exit'):
            for tmp in a.tmp_deps:
                prev_code_loc = temp_defs[tmp]
                self._stmt_graph_add_edge(prev_code_loc, current_code_location, type='exit', data='tmp')
                edge_tuple = (prev_code_loc, current_code_location)
                temps_to_edges[tmp].append(edge_tuple)
        elif (a.type == 'operation'):
            if (a.op.endswith('Sub32') or a.op.endswith('Sub64')):
                (expr_0, expr_1) = a.exprs
                if (expr_0.tmp_deps and ((not expr_1.tmp_deps) and (not expr_1.reg_deps))):
                    tmp = list(expr_0.tmp_deps)[0]
                    if (tmp in temp_register_symbols):
                        (sort, offset) = temp_register_symbols[tmp]
                        offset -= expr_1.ast.args[0]
                        data_generated = (sort, offset)
            elif (a.op.endswith('Add32') or a.op.endswith('Add64')):
                (expr_0, expr_1) = a.exprs
                if (expr_0.tmp_deps and ((not expr_1.tmp_deps) and (not expr_1.reg_deps))):
                    tmp = list(expr_0.tmp_deps)[0]
                    if (tmp in temp_register_symbols):
                        (sort, offset) = temp_register_symbols[tmp]
                        offset += expr_1.ast.args[0]
                        data_generated = (sort, offset)
    return live_defs
