docs: def _scan_procedure(self, cfg_job, current_func_addr):
          """
          Checks the hooking procedure for this address searching for new static
          exit points to add to successors (generating entries for them)
          if this address has not been traced before. Updates previous CFG nodes
          with edges.
  
          :param CFGJob cfg_job:      The CFGJob instance.
          :param int current_func_addr: Address of the current function.
          :return: List of successors
          :rtype: list
          """
  
          addr = cfg_job.addr
  
          try:
              if self.project.is_hooked(addr):
                  procedure = self.project.hooked_by(addr)
                  name = procedure.display_name
              else:
                  procedure = self.project.simos.syscall_from_addr(addr)
                  name = procedure.display_name
  
              if addr not in self._nodes:
                  cfg_node = CFGNode(addr, 0, self.model,
                                     function_address=current_func_addr,
                                     simprocedure_name=name,
                                     no_ret=procedure.NO_RET,
                                     block_id=addr,
                                     )
  
                  self._nodes[addr] = cfg_node
                  self._nodes_by_addr[addr].append(cfg_node)
  
              else:
                  cfg_node = self._nodes[addr]
  
          except (SimMemoryError, SimEngineError):
              return [ ]
  
          self._graph_add_edge(cfg_node, cfg_job.src_node, cfg_job.jumpkind, cfg_job.src_ins_addr,
                               cfg_job.src_stmt_idx
                               )
          self._function_add_node(cfg_node, current_func_addr)
  
          # Add edges going to this node in function graphs
          cfg_job.apply_function_edges(self, clear=True)
  
          # If we have traced it before, don't trace it anymore
          if addr in self._traced_addresses:
              return [ ]
          else:
              # Mark the address as traced
              self._traced_addresses.add(addr)
  
          entries = [ ]
  
          if procedure.ADDS_EXITS:
              # Get two blocks ahead
              grandparent_nodes = list(self.graph.predecessors(cfg_job.src_node))
              if not grandparent_nodes:
                  l.warning("%s is supposed to yield new exits, but it fails to do so.", name)
                  return [ ]
              blocks_ahead = [
                  self._lift(grandparent_nodes[0].addr).vex,
                  self._lift(cfg_job.src_node.addr).vex,
              ]
              procedure.project = self.project
              procedure.arch = self.project.arch
              new_exits = procedure.static_exits(blocks_ahead)
  
              for addr_, jumpkind in new_exits:
                  if isinstance(addr_, claripy.ast.BV) and not addr_.symbolic:
                      addr_ = addr_._model_concrete.value
                  if not isinstance(addr_, int):
                      continue
                  entries += self._create_jobs(addr_, jumpkind, current_func_addr, None, addr_, cfg_node, None,
                                               None
                                               )
  
          if not procedure.NO_RET:
              # it returns
              cfg_node.has_return = True
              self._function_exits[current_func_addr].add(addr)
              self._function_add_return_site(addr, current_func_addr)
          else:
              # the procedure does not return
              self._updated_nonreturning_functions.add(current_func_addr)
  
          return entries