def test_get_exit_surface_metrics(self):
        # Arrange
        expected = {
            Call('', './src/greetings.c', Env.C, Gran.FILE):
            {
                'points': None, 'proximity': None, 'surface_coupling': None
            },
            Call('', './src/helloworld.c', Env.C, Gran.FILE):
            {
                'points': None, 'proximity': None, 'surface_coupling': None
            }
        }

        for i in expected:
            # Act
            actual = self.target.get_exit_surface_metrics(i)

            # Assert
            self.assertIsInstance(actual, dict)
            self.assertTrue('points' in actual)
            self.assertTrue('proximity' in actual)
            self.assertTrue('surface_coupling' in actual)
            if expected[i]['points'] is None:
                self.assertEqual(expected[i]['points'], actual['points'])
            else:
                self.assertCountEqual(expected[i]['points'], actual['points'])
            self.assertEqual(expected[i]['proximity'], actual['proximity'])
            self.assertEqual(
                expected[i]['surface_coupling'], actual['surface_coupling']
            )
예제 #2
0
    def test_load_call_graph_nodes(self):
        # Arrange
        expected = [
            Call('GreeterSayHiTo', './src/helloworld.c', Env.C),
            Call('greet_a', './src/helloworld.c', Env.C),
            Call('greet', './src/greetings.c', Env.C),
            Call('recursive_b', './src/greetings.c', Env.C),
            Call('new_Greeter', './src/helloworld.c', Env.C),
            Call('recursive_a', './src/greetings.c', Env.C),
            Call('addInt', './src/helloworld.c', Env.C),
            Call('greet_b', './src/helloworld.c', Env.C),
            Call('main', './src/helloworld.c', Env.C),
            Call('GreeterSayHi', './src/helloworld.c', Env.C)
        ]

        # Act
        graph = self.target.load_call_graph()
        actual = graph.nodes()
        match = (all([i in actual for i in expected])
                 and all([i in expected for i in actual]))

        # Assert
        self.assertCountEqual(expected, actual)
        for (_, attrs) in graph.nodes(data=True):
            self.assertTrue('tested' in attrs)
            self.assertTrue('defense' not in attrs)
            self.assertTrue('dangerous' not in attrs)
            self.assertTrue('vulnerable' not in attrs)
예제 #3
0
    def test_nodes(self):
        # Arrange
        expected = [
            Call('GreeterSayHiTo', './src/helloworld.c', Environments.C),
            Call('greet_a', './src/helloworld.c', Environments.C),
            Call('greet', './src/greetings.c', Environments.C),
            Call('recursive_b', './src/greetings.c', Environments.C),
            Call('new_Greeter', './src/helloworld.c', Environments.C),
            Call('recursive_a', './src/greetings.c', Environments.C),
            Call('addInt', './src/helloworld.c', Environments.C),
            Call('greet_b', './src/helloworld.c', Environments.C),
            Call('main', './src/helloworld.c', Environments.C),
            Call('GreeterSayHi', './src/helloworld.c', Environments.C)
        ]

        # Act
        actual = self.target.nodes

        # Assert
        self.assertCountEqual(expected, [i for (i, _) in actual])
        for (_, attrs) in actual:
            self.assertTrue('tested' in attrs)
            self.assertTrue('defense' not in attrs)
            self.assertTrue('dangerous' not in attrs)
            self.assertTrue('vulnerable' not in attrs)
    def load_call_graph(self):
        """
            Description.

            Comments.

            Returns:
                A call graph.
        """
        call_graph = nx.DiGraph()

        if self.app_packages:
            condition_to_add = lambda line: line.startswith("M:") and self._contains_call_in_package(line)
        else:
            condition_to_add = lambda line: line.startswith("M:")

        with open(self.source) as raw_call_graph:
            # line is like this:
            # M:com.example.kevin.helloandroid.Greeter:sayHelloInSpanish (M)java.lang.StringBuilder:toString.
            for line in raw_call_graph:
                if condition_to_add(line):
                    caller, callee = line.split(" ")
                    call_graph.add_edge(Call.from_javacg(caller), Call.from_javacg(callee))

        return call_graph
예제 #5
0
    def test_assign_page_rank(self):
        # Arrange
        expected = {
            Call('greet_a', './src/helloworld.c', Environments.C):
            0.12789113913543346,
            Call('main', './src/helloworld.c', Environments.C):
            0.2671500027198321,
            Call('greet', './src/greetings.c', Environments.C):
            0.08747233694516567,
            Call('addInt', './src/helloworld.c', Environments.C):
            0.052845759753949534,
            Call('greet_b', './src/helloworld.c', Environments.C):
            0.12789113913543343,
            Call('GreeterSayHiTo', './src/helloworld.c', Environments.C):
            0.052845759753949534,
            Call('recursive_b', './src/greetings.c', Environments.C):
            0.0891061715241686,
            Call('recursive_a', './src/greetings.c', Environments.C):
            0.08910617152416858,
            Call('new_Greeter', './src/helloworld.c', Environments.C):
            0.052845759753949534,
            Call('GreeterSayHi', './src/helloworld.c', Environments.C):
            0.052845759753949534
        }

        # Act
        self.target.assign_page_rank()
        actual = nx.get_node_attributes(self.target.call_graph, 'page_rank')

        # Assert
        self.assertEqual(len(expected), len(actual))
        for i in expected:
            self.assertAlmostEqual(expected[i], actual[i])
예제 #6
0
    def test_load_call_graph_edges(self):
        # Act
        expected = [(Call('main', 'multigprof.c',
                          Env.C), Call('factorial', 'multigprof.c', Env.C)),
                    (Call('factorial', 'multigprof.c',
                          Env.C), Call('main', 'multigprof.c', Env.C)),
                    (Call('factorial', 'multigprof.c',
                          Env.C), Call('factorial', 'multigprof.c', Env.C)),
                    (Call('main', 'multigprof.c',
                          Env.C), Call('fibonacci', 'multigprof.c', Env.C)),
                    (Call('fibonacci', 'multigprof.c',
                          Env.C), Call('main', 'multigprof.c', Env.C))]

        # Act
        test_graph = self.test_loader.load_call_graph()
        edges = test_graph.edges()

        all_edges_found = all([c in edges for c in expected])

        # Assert
        self.assertEqual(len(expected), len(edges))
        self.assertTrue(all_edges_found)
        for (u, v, attrs) in test_graph.edges(data=True):
            self.assertTrue('gprof' in attrs)
            self.assertTrue('cflow' not in attrs)
            self.assertTrue('call' in attrs or 'return' in attrs)
    def load_call_graph(self, granularity=Granularity.FUNC):
        """
            Description.

            Comments.

            Returns:
                A call graph.
        """
        call_graph = nx.DiGraph()

        if self.app_packages:

            def condition_to_add(line):
                return (line.startswith("M:")
                        and self._contains_call_in_package(line))
        else:

            def condition_to_add(line):
                return line.startswith("M:")

        with open(self.source) as raw_call_graph:
            # line is like this:
            # M:com.example.kevin.helloandroid.Greeter:sayHelloInSpanish (M)jav
            # a.lang.StringBuilder:toString.
            for line in raw_call_graph:
                if condition_to_add(line):
                    caller, callee = line.split(" ")
                    call_graph.add_edge(Call.from_javacg(caller, granularity),
                                        Call.from_javacg(callee, granularity))

        return call_graph
예제 #8
0
    def test_equal(self):
        # Arrange
        cflow_line = 'getchar()'
        test_call_1 = Call.from_cflow(cflow_line)
        test_call_2 = Call.from_cflow(cflow_line)

        # Assert
        self.assertEqual(test_call_1, test_call_2)
    def test_equal(self):
        # Arrange
        cflow_line = 'getchar()'
        test_call_1 = Call.from_cflow(cflow_line)
        test_call_2 = Call.from_cflow(cflow_line)

        # Assert
        self.assertEqual(test_call_1, test_call_2)
    def test_load_call_graph_nodes(self):
        # Arrange
        expected = [
            Call('opt_progress', './ffmpeg_opt.c', Environments.C),
            Call('get_preset_file_2', './ffmpeg_opt.c', Environments.C),
            Call('dump_attachment', './ffmpeg_opt.c', Environments.C),
            Call('open_output_file', './ffmpeg_opt.c', Environments.C),
            Call('init_input', './libavformat/utils.c', Environments.C),
            Call('avio_open2', './libavformat/aviobuf.c', Environments.C),
            Call('ffurl_open', './libavformat/avio.c', Environments.C),
            Call('biquad_s16', './libavfilter/af_biquads.c', Environments.C),
            Call('av_log', './libavutil/log.c', Environments.C)
        ]

        # Act
        graph = self.target.load_call_graph()
        actual = graph.nodes()

        # Assert
        self.assertCountEqual(expected, actual)
        for (_, attrs) in graph.nodes(data=True):
            self.assertTrue('tested' in attrs)
            self.assertTrue('defense' not in attrs)
            self.assertTrue('dangerous' not in attrs)
            self.assertTrue('vulnerable' not in attrs)
예제 #11
0
    def test_not_equal(self):
        # Arrange
        cflow_line_1 = 'getchar()'
        cflow_line_2 = (
            'xstrdup() <char *xstrdup (const char *str) at ./cyrus/lib/xmalloc'
            '.c:89> (R):')
        test_call_1 = Call.from_cflow(cflow_line_1)
        test_call_2 = Call.from_cflow(cflow_line_2)

        # Assert
        self.assertNotEqual(test_call_1, test_call_2)
    def test_get_nodes(self):
        # Arrange
        expected = [
            Call('', './src/greetings.c', Env.C, Gran.FILE),
            Call('', './src/helloworld.c', Env.C, Gran.FILE),
        ]

        # Act
        actual = self.target.get_nodes(attribute='exit')

        # Assert
        self.assertCountEqual(expected, actual)
    def test_node_attribute_tested(self):
        # Arrange
        expected = [
            Call('', './src/greetings.c', Env.C, Gran.FILE),
            Call('', './src/helloworld.c', Env.C, Gran.FILE)
        ]

        # Act
        actual = [i for (i, attrs) in self.target.nodes if 'tested' in attrs]

        # Assert
        self.assertCountEqual(expected, actual)
    def test_exit_points(self):
        # Arrange
        expected = [
            Call('', './src/greetings.c', Env.C, Gran.FILE),
            Call('', './src/helloworld.c', Env.C, Gran.FILE)
        ]

        # Act
        actual = self.target.exit_points

        # Assert
        self.assertCountEqual(expected, actual)
    def test_not_equal(self):
        # Arrange
        cflow_line_1 = 'getchar()'
        cflow_line_2 = (
            'xstrdup() <char *xstrdup (const char *str) at ./cyrus/lib/xmalloc'
            '.c:89> (R):'
        )
        test_call_1 = Call.from_cflow(cflow_line_1)
        test_call_2 = Call.from_cflow(cflow_line_2)

        # Assert
        self.assertNotEqual(test_call_1, test_call_2)
    def test_get_ancestors(self):
        # Arrange
        expected = [
            Call('', './src/helloworld.c', Env.C, Gran.FILE),
        ]
        call = Call('', './src/greetings.c', Env.C, Gran.FILE)

        # Act
        actual = self.target.get_ancestors(call)

        # Assert
        self.assertCountEqual(expected, actual)
    def test_get_shortest_path_length_with_entry(self):
        # Arrange
        expected = {Call('', './src/helloworld.c', Env.C, Gran.FILE): 1}
        call = Call('', './src/greetings.c', Env.C, Gran.FILE)

        # Act
        actual = self.target.get_shortest_path_length(call, 'entry')

        # Assert
        self.assertCountEqual(expected, actual)
        self.assertAlmostEqual(stat.mean(expected.values()),
                               stat.mean(expected.values()),
                               places=4)
    def test_get_shortest_path_length_with_entry(self):
        # Arrange
        expected = {Call('greet_b', './src/helloworld.c', Environments.C): 2}
        call = Call('functionPtr', './src/helloworld.c', Environments.C)

        # Act
        actual = self.target.get_shortest_path_length(call, 'entry')

        # Assert
        self.assertCountEqual(expected, actual)
        self.assertAlmostEqual(stat.mean(expected.values()),
                               stat.mean(expected.values()),
                               places=4)
    def test_load_call_graph_edges_file_granularity(self):
        # Arrange
        expected = [
            (
                Call('', './src/helloworld.c', Env.C, Gran.FILE),
                Call('', './src/helloworld.c', Env.C, Gran.FILE)
            ),
            (
                Call('', './src/greetings.c', Env.C, Gran.FILE),
                Call('', './src/helloworld.c', Env.C, Gran.FILE)
            ),
            (
                Call('', './src/helloworld.c', Env.C, Gran.FILE),
                Call('', './src/greetings.c', Env.C, Gran.FILE)
            ),
            (
                Call('', './src/greetings.c', Env.C, Gran.FILE),
                Call('', './src/greetings.c', Env.C, Gran.FILE)
            )
        ]

        # Act
        test_graph = self.test_loader.load_call_graph(granularity=Gran.FILE)
        edges = test_graph.edges()

        all_edges_found = all([c in edges for c in expected])

        # Assert
        self.assertEqual(len(expected), len(edges))
        self.assertTrue(all_edges_found)
        for (u, v, attrs) in test_graph.edges(data=True):
            self.assertTrue('cflow' in attrs)
            self.assertTrue('gprof' not in attrs)
            self.assertTrue('call' in attrs or 'return' in attrs)
    def test_get_degree(self):
        # Arrange
        expected = {
            Call('', './src/helloworld.c', Env.C, Gran.FILE): (2, 2),
            Call('', './src/greetings.c', Env.C, Gran.FILE): (2, 2)
        }

        # Act
        actual = self.target.get_degree()
        match = all([actual[i] == expected[i] for i in actual])

        # Assert
        self.assertEqual(len(expected), len(actual))
        self.assertTrue(match)
    def test_get_page_rank(self):
        # Arrange
        expected = {
            Call('', './src/helloworld.c', Env.C, Gran.FILE): 0.5,
            Call('', './src/greetings.c', Env.C, Gran.FILE): 0.5
        }

        # Act
        actual = self.target.get_page_rank()

        # Assert
        self.assertEqual(len(expected), len(actual))
        for i in expected:
            self.assertAlmostEqual(expected[i], actual[i])
    def test_assign_weights(self):
        # Arrange
        expected = {
            (
                Call('', './src/helloworld.c', Env.C, Gran.FILE),
                Call('', './src/helloworld.c', Env.C, Gran.FILE)
            ): 75,
            (
                Call('', './src/helloworld.c', Env.C, Gran.FILE),
                Call('', './src/greetings.c', Env.C, Gran.FILE)
            ): 75,
            (
                Call('', './src/greetings.c', Env.C, Gran.FILE),
                Call('', './src/helloworld.c', Env.C, Gran.FILE)
            ): 25,
            (
                Call('', './src/greetings.c', Env.C, Gran.FILE),
                Call('', './src/greetings.c', Env.C, Gran.FILE)
            ): 75
        }

        # Act
        self.target.assign_weights()
        actual = nx.get_edge_attributes(self.target.call_graph, 'weight')

        # Assert
        self.assertCountEqual(expected, actual)
        for i in expected:
            self.assertEqual(expected[i], actual[i], msg=i)
    def test_edges(self):
        # Arrange
        expected = [
            (
                Call('', './src/helloworld.c', Env.C, Gran.FILE),
                Call('', './src/greetings.c', Env.C, Gran.FILE)
            ),
            (
                Call('', './src/greetings.c', Env.C, Gran.FILE),
                Call('', './src/helloworld.c', Env.C, Gran.FILE)
            ),
            (
                Call('', './src/greetings.c', Env.C, Gran.FILE),
                Call('', './src/greetings.c', Env.C, Gran.FILE)
            ),
            (
                Call('', './src/helloworld.c', Env.C, Gran.FILE),
                Call('', './src/helloworld.c', Env.C, Gran.FILE)
            )
        ]

        # Act
        actual = self.target.edges

        # Assert
        self.assertCountEqual(expected, [(i, j) for (i, j, _) in actual])
        for (_, _, attrs) in actual:
            self.assertTrue('gprof' in attrs)
            self.assertTrue('cflow' not in attrs)
            self.assertTrue('call' in attrs or 'return' in attrs)
    def test_assign_page_rank(self):
        # Arrange
        expected = {
            Call('', './src/helloworld.c', Env.C, Gran.FILE): 0.525,
            Call('', './src/greetings.c', Env.C, Gran.FILE): 0.475
        }

        # Act
        self.target.assign_page_rank()
        actual = nx.get_node_attributes(self.target.call_graph, 'page_rank')

        # Assert
        self.assertEqual(len(expected), len(actual))
        for i in expected:
            self.assertAlmostEqual(expected[i], actual[i])
예제 #25
0
    def test_is_output(self):
        # Arrange
        cflow_line = 'printf()'
        test_call = Call.from_cflow(cflow_line)

        # Assert
        self.assertTrue(test_call.is_output())
    def test_function_signature_only_name(self):
        # Arrange
        cflow_line = 'printf()'
        test_call = Call.from_cflow(cflow_line)

        # Assert
        self.assertEqual('', test_call.function_signature)
    def test_identity_function_name(self):
        # Arrange
        cflow_line = 'printf()'
        test_call = Call.from_cflow(cflow_line)

        # Assert
        self.assertEqual('printf', test_call.identity)
    def test_is_input(self):
        # Arrange
        cflow_line = 'getchar()'
        test_call = Call.from_cflow(cflow_line)

        # Assert
        self.assertTrue(test_call.is_input())
    def test_is_not_input(self):
        # Arrange
        cflow_line = 'printf()'
        test_call = Call.from_cflow(cflow_line)

        # Assert
        self.assertFalse(test_call.is_input())
예제 #30
0
    def test_is_not_output(self):
        # Arrange
        cflow_line = 'getchar()'
        test_call = Call.from_cflow(cflow_line)

        # Assert
        self.assertFalse(test_call.is_output())
예제 #31
0
    def test_identity_function_name_file_granularity(self):
        # Arrange
        cflow_line = 'printf()'
        test_call = Call.from_cflow(cflow_line, granularity=Granularity.FILE)

        # Assert
        self.assertEqual('', test_call.identity)
    def test_get_entry_point_reachability_non_entry(self):
        # Arrange
        call = Call('', './src/greetings.c', Env.C, Gran.FILE)

        # Assert
        self.assertRaises(Exception, self.target.get_entry_point_reachability,
                          call)
    def test_is_not_output(self):
        # Arrange
        cflow_line = 'getchar()'
        test_call = Call.from_cflow(cflow_line)

        # Assert
        self.assertFalse(test_call.is_output())
    def test_is_output(self):
        # Arrange
        cflow_line = 'printf()'
        test_call = Call.from_cflow(cflow_line)

        # Assert
        self.assertTrue(test_call.is_output())
예제 #35
0
    def test_is_not_input(self):
        # Arrange
        cflow_line = 'printf()'
        test_call = Call.from_cflow(cflow_line)

        # Assert
        self.assertFalse(test_call.is_input())
    def test_identity_function_name_file_granularity(self):
        # Arrange
        cflow_line = 'printf()'
        test_call = Call.from_cflow(cflow_line, granularity=Granularity.FILE)

        # Assert
        self.assertEqual('', test_call.identity)
예제 #37
0
    def test_identity_function_name(self):
        # Arrange
        cflow_line = 'printf()'
        test_call = Call.from_cflow(cflow_line)

        # Assert
        self.assertEqual('printf', test_call.identity)
예제 #38
0
    def test_is_input(self):
        # Arrange
        cflow_line = 'getchar()'
        test_call = Call.from_cflow(cflow_line)

        # Assert
        self.assertTrue(test_call.is_input())
예제 #39
0
    def test_function_signature_only_name(self):
        # Arrange
        cflow_line = 'printf()'
        test_call = Call.from_cflow(cflow_line)

        # Assert
        self.assertEqual('', test_call.function_signature)
예제 #40
0
    def test_in_stdlib(self):
        # Arrange
        cflow_line = 'printf()'
        test_call = Call.from_cflow(cflow_line)

        # Assert
        self.assertTrue(test_call.in_stdlib())
    def load_call_graph(self):
        """
            Generates the Call Graph as a networkx.DiGraph object.

            Invokes the call grap generation software (cflow) and creates a networkx.DiGraph instance that represents
            the analyzed source code's Call Graph.

            Args:
                is_reverse: Boolean specifying whether the graph generation software (cflow) should use the reverse
                    algorithm.

            Returns:
                None
        """
        call_graph = nx.DiGraph()
        is_first_line = True
        parent = Stack()

        if os.path.isfile(self.source):
            raw_call_graph = open(self.source)
            readline = lambda: raw_call_graph.readline()

        elif os.path.isdir(self.source):
            raw_call_graph = self._exec_cflow(self.is_reverse)
            readline = lambda: raw_call_graph.stdout.readline().decode(encoding='UTF-8')

        while True:
            line = readline()

            if line == '':
                break

            current = Call.from_cflow(line)

            if not is_first_line:
                if current.level > previous.level:
                    parent.push(previous)
                elif current.level < previous.level:
                    for t in range(previous.level - current.level):
                        parent.pop()

                if parent.top:
                    call_graph.add_node(current, {'tested': False})
                    call_graph.add_node(parent.top, {'tested': False})

                    # Edge weight can be any arbitrary number greater than 
                    #   that used as the weight for the edges in the gprof 
                    #   call graph.

                    if not self.is_reverse:
                        call_graph.add_edge(parent.top, current, 
                            {'cflow': 'cflow'}, weight=1)
                    else:
                        call_graph.add_edge(current, parent.top, 
                            {'cflow': 'cflow'}, weight=1)

            previous = current
            is_first_line = False

        return call_graph
    def test_in_stdlib(self):
        # Arrange
        cflow_line = 'printf()'
        test_call = Call.from_cflow(cflow_line)

        # Assert
        self.assertTrue(test_call.in_stdlib())
    def test_is_not_input_no_leaf(self):
        # Arrange
        cflow_line = (
            'xstrdup() <char *xstrdup (const char *str) at ./cyrus/lib/xmalloc'
            '.c:89> (R):'
        )
        test_call = Call.from_cflow(cflow_line)

        # Assert
        self.assertFalse(test_call.is_input())
    def test_function_signature_full(self):
        # Arrange
        cflow_line = (
            'xstrdup() <char *xstrdup (const char *str) at ./cyrus/lib/xmalloc'
            '.c:89> (R):'
        )
        test_call = Call.from_cflow(cflow_line)

        # Assert
        self.assertEqual('./cyrus/lib/xmalloc.c', test_call.function_signature)
    def test_identity_full_file_granularity(self):
        # Arrange
        cflow_line = (
            'xstrdup() <char *xstrdup (const char *str) at ./cyrus/lib/xmalloc'
            '.c:89> (R):'
        )
        test_call = Call.from_cflow(cflow_line, granularity=Granularity.FILE)

        # Assert
        self.assertEqual('./cyrus/lib/xmalloc.c', test_call.identity)
    def test_identity_full(self):
        # Arrange
        cflow_line = (
            'xstrdup() <char *xstrdup (const char *str) at ./cyrus/lib/xmalloc'
            '.c:89> (R):'
        )
        test_call = Call.from_cflow(cflow_line)

        # Assert
        self.assertEqual('xstrdup ./cyrus/lib/xmalloc.c', test_call.identity)
    def load_call_graph(self):
        """
            Generates the Call Graph as a networkx.DiGraph object.

            Invokes the call grap generation software (cflow) and creates a networkx.DiGraph instance that represents
            the analyzed source code's Call Graph.

            Args:
                is_reverse: Boolean specifying whether the graph generation software (cflow) should use the reverse
                    algorithm.

            Returns:
                None
        """
        call_graph = nx.DiGraph()

        header_passed = False

        entry = None

        is_caller = True
        callers = list()
        callees = list()

        # line_count = 0

        with open(self.source) as raw_call_graph:
            while True:
                line = raw_call_graph.readline()
                # line_count += 1
                # print(line_count)
                # print(line)

                if not header_passed:
                    if line == GprofLoader.header:
                        header_passed = True
                    continue

                else:  # if header_passed:
                    if self.is_entry_line(line):
                        try:
                            entry = Call.from_gprof(line)
                        except ValueError as e:
                            raise e

                        call_graph.add_node(entry, {'tested': False})
                        is_caller = False

                    elif line == GprofLoader.separator:
                        if len(callers) > 0 or len(callees) > 0:
                            call_graph.node[entry]['tested'] = True

                        for caller in callers:
                            call_graph.add_node(caller, {'tested': True})

                            # Edge weight can be any arbitrary number lesser 
                            #   than that used as the weight for the edges 
                            #   in the cflow call graph.
                            call_graph.add_edge(caller, entry, 
                                {'gprof': 'gprof'}, weight=0.5)
                        callers.clear()

                        for callee in callees:
                            call_graph.add_node(callee, {'tested': True})

                            # Edge weight can be any arbitrary number lesser 
                            #   than that used as the weight for the edges 
                            #   in the cflow call graph.
                            call_graph.add_edge(entry, callee, 
                                {'gprof': 'gprof'}, weight=0.5)
                        callees.clear()

                        is_caller = True

                    elif line == GprofLoader.eof:
                        break

                    else:
                        try:
                            if is_caller:
                                callers.append(Call.from_gprof(line))
                            else:
                                callees.append(Call.from_gprof(line))
                        except ValueError as e:
                            self._error_messages.append("Error: " + str(e) + " Input line: " + line)

        return call_graph
    def load_call_graph(self, granularity=Granularity.FUNC):
        """Load a call graph generated by cflow.

        If necessary, the static call graph generation utility (cflow) is
        invoked to generate the call graph before attempting to load it.

        Parameters
        ----------
        granularity : str
            The granularity at which the call graph must be loaded. See
            attacksurfacemeter.granularity.Granularity for available choices.

        Returns
        -------
        call_graph : networkx.DiGraph
            An object representing the call graph.
        """
        call_graph = nx.DiGraph()
        parent = Stack()

        raw_call_graph = None
        if os.path.isfile(self.source):
            raw_call_graph = open(self.source)
        elif os.path.isdir(self.source):
            raw_call_graph = self._exec_cflow()

        try:
            previous = Call.from_cflow(raw_call_graph.readline(), granularity)
            for line in raw_call_graph:
                current = Call.from_cflow(line, granularity)

                if current.level > previous.level:
                    parent.push(previous)
                elif current.level < previous.level:
                    for t in range(previous.level - current.level):
                        parent.pop()

                if parent.top:
                    caller = callee = None
                    entry = exit = dangerous = defense = False
                    if self.is_reverse:
                        caller = current
                        callee = parent.top
                    else:
                        caller = parent.top
                        callee = current

                    (caller_attrs, callee_attrs) = utilities.get_node_attrs(
                        'cflow', caller, callee, self.defenses,
                        self.vulnerabilities
                    )

                    call_graph.add_node(caller, caller_attrs)

                    if callee_attrs is not None:
                        call_graph.add_node(callee, callee_attrs)

                        # Adding the edge caller --  callee
                        attrs = {'cflow': None, 'call': None}
                        call_graph.add_edge(caller, callee, attrs)

                        # Adding the edge callee -- caller with the assumption
                        #   that every call must return
                        attrs = {'cflow': None, 'return': None}
                        call_graph.add_edge(callee, caller, attrs)

                previous = current
        finally:
            if raw_call_graph:
                raw_call_graph.close()

        return call_graph