':param cfg: An angr CFG object
:returns:    a dictionary of function addresses to tuples of attributes'

@staticmethod
def _compute_function_attributes(cfg):

    attributes = dict()
    all_funcs = set(cfg.kb.callgraph.nodes())
    for function_addr in cfg.kb.functions:
        if ((cfg.kb.functions.function(function_addr) is None) or cfg.kb.functions.function(function_addr).is_syscall):
            continue
        if (cfg.kb.functions.function(function_addr) is not None):
            normalized_funtion = NormalizedFunction(cfg.kb.functions.function(function_addr))
            number_of_basic_blocks = len(normalized_funtion.graph.nodes())
            number_of_edges = len(normalized_funtion.graph.edges())
        else:
            number_of_basic_blocks = 0
            number_of_edges = 0
        if (function_addr in all_funcs):
            number_of_subfunction_calls = len(cfg.kb.callgraph.successors(function_addr))
        else:
            number_of_subfunction_calls = 0
        attributes[function_addr] = (number_of_basic_blocks, number_of_edges, number_of_subfunction_calls)
    return attributes
