docs: 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