def _load_call_graph(self, index, granularity, sync_queue):
        loader = GprofLoader(
            self.sources[index], self.is_reverse, self.defenses,
            self.vulnerabilities
        )
        call_graph = loader.load_call_graph(granularity)

        sync_queue.put((call_graph, loader.errors), block=True)
 def setUp(self):
     self.target = GprofLoader(
         os.path.join(
             os.path.dirname(os.path.realpath(__file__)),
             'ffmpeg/gprof.callgraph.txt'
         ),
         False
     )
    def test_load_call_graph_empty_file(self):
        # Act
        target = GprofLoader(
            os.path.join(os.path.dirname(os.path.realpath(__file__)),
                         'helloworld/empty.gprof.callgraph.txt'), False)

        # Using a separate process to ensure the loading of an emtpy call graph
        # completes within a reasonable time (see timeout below).
        queue = multiprocessing.Queue()
        process = multiprocessing.Process(name='p.gprof',
                                          target=self._wrapper,
                                          args=(target.load_call_graph, queue))
        process.start()
        timeout = 1
        process.join(timeout=timeout)
        is_alive = process.is_alive()
        if is_alive:
            process.terminate()
        self.assertFalse(
            is_alive,
            msg=('Process loading an empty gprof file has not terminated'
                 ' even after {0} second(s).'.format(timeout)))

        # Assert
        test_graph = queue.get()
        self.assertEqual(0, len(test_graph.nodes()))
    def load_call_graph(self):
        """
            Generates a Call Graph as a networkx.DiGraph object. The call 
            graph generated is an aggregate of multiple call graphs output by
            GNU gprof.
        """
        call_graph = None
        for source in self.sources:
            _gprof_loader = GprofLoader(source, reverse=self.reverse)
            _gprof_call_graph = _gprof_loader.load_call_graph()

            if call_graph:
                call_graph.add_edges_from(
                    _gprof_call_graph.edges()
                )
            else:
                call_graph = _gprof_call_graph

            self.error_messages.append(_gprof_loader.error_messages)

        return call_graph
 def setUp(self):
     self.target = CallGraph.from_merge(
         CallGraph.from_loader(
             CflowLoader(
                 os.path.join(
                     os.path.dirname(os.path.realpath(__file__)),
                     'helloworld/cflow.callgraph.r.txt'
                 ),
                 True
             )
         ),
         CallGraph.from_loader(
             GprofLoader(
                 os.path.join(
                     os.path.dirname(os.path.realpath(__file__)),
                     'helloworld/gprof.callgraph.txt'
                 )
             )
         )
     )
class GprofLoaderFFmpegFileTestCase(unittest.TestCase):
    def setUp(self):
        self.target = GprofLoader(
            os.path.join(
                os.path.dirname(os.path.realpath(__file__)),
                'ffmpeg/gprof.callgraph.txt'
            ),
            False
        )

    def test_load_call_graph_errors(self):
        # Act
        graph = self.target.load_call_graph()

        # Assert
        self.assertEqual(1, len(self.target.errors))

    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)

    def test_load_call_graph_entry_nodes(self):
        # Arrange
        expected = []

        # Act
        graph = self.target.load_call_graph()

        # Assert
        for (n, attrs) in graph.nodes(data=True):
            if n in expected:
                self.assertTrue('entry' in attrs)
            else:
                self.assertTrue('entry' not in attrs)

    def test_load_call_graph_exit_nodes(self):
        # Arrange
        expected = []

        # Act
        graph = self.target.load_call_graph()

        # Assert
        for (n, attrs) in graph.nodes(data=True):
            if n in expected:
                self.assertTrue('exit' in attrs)
            else:
                self.assertTrue('exit' not in attrs)

    def test_load_call_graph_edges(self):
        # Arrange
        expected = [
            (
                Call('opt_progress', './ffmpeg_opt.c', Environments.C),
                Call('avio_open2', './libavformat/aviobuf.c', Environments.C)
            ),
            (
                Call('avio_open2', './libavformat/aviobuf.c', Environments.C),
                Call('opt_progress', './ffmpeg_opt.c', Environments.C)
            ),
            (
                Call('avio_open2', './libavformat/aviobuf.c', Environments.C),
                Call('get_preset_file_2', './ffmpeg_opt.c', Environments.C)
            ),
            (
                Call('get_preset_file_2', './ffmpeg_opt.c', Environments.C),
                Call('avio_open2', './libavformat/aviobuf.c', Environments.C)
            ),
            (
                Call('dump_attachment', './ffmpeg_opt.c', Environments.C),
                Call('avio_open2', './libavformat/aviobuf.c', Environments.C)
            ),
            (
                Call('avio_open2', './libavformat/aviobuf.c', Environments.C),
                Call('dump_attachment', './ffmpeg_opt.c', Environments.C)
            ),
            (
                Call('open_output_file', './ffmpeg_opt.c', Environments.C),
                Call('avio_open2', './libavformat/aviobuf.c', Environments.C)
            ),
            (
                Call('avio_open2', './libavformat/aviobuf.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('avio_open2', './libavformat/aviobuf.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('ffurl_open', './libavformat/avio.c', Environments.C),
                Call('avio_open2', './libavformat/aviobuf.c', Environments.C)
            ),
            (
                Call(
                    'biquad_s16', './libavfilter/af_biquads.c', Environments.C
                ),
                Call('av_log', './libavutil/log.c', Environments.C)
            ),
            (
                Call('av_log', './libavutil/log.c', Environments.C),
                Call(
                    'biquad_s16', './libavfilter/af_biquads.c', Environments.C
                )
            ),
        ]

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

        # Assert
        self.assertCountEqual(expected, actual)
        for (_, _, attrs) in 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 test_load_call_graph_return_edges(self):
        # Act
        graph = self.target.load_call_graph()

        # Assert
        for (u, v) in nx.get_edge_attributes(graph, 'call'):
            self.assertTrue('return' in graph[v][u])
    def test_fix(self):
        # Arrange
        target = CallGraph.from_loader(
            CflowLoader(
                os.path.join(
                    os.path.dirname(os.path.realpath(__file__)),
                    'helloworld/cflow.callgraph.r.mod.txt'
                ),
                True
            )
        )
        _target = copy.deepcopy(target)
        reference = CallGraph.from_loader(
            GprofLoader(
                os.path.join(
                    os.path.dirname(os.path.realpath(__file__)),
                    'helloworld/gprof.callgraph.txt'
                )
            )
        )
        expected = {
            'before': Call('GreeterSayHi', '', Environments.C),
            'after': Call('GreeterSayHi', './src/helloworld.c', Environments.C)
        }

        # Act
        utilities.fix(target, using=reference)
        actual = {
            'before':  next(
                i
                for (i, _) in _target.nodes
                if i.function_name == 'GreeterSayHi'
            ),
            'after':  next(
                i
                for (i, _) in target.nodes
                if i.function_name == 'GreeterSayHi'
            )
        }

        # Assert
        self.assertEqual(expected['before'], actual['before'])
        self.assertEqual(expected['after'], actual['after'])
        # Asserting if node attributes got carried over
        self.assertCountEqual(
            [
                attrs
                for (i, attrs) in _target.nodes
                if i == expected['before']
            ],
            [
                attrs
                for (i, attrs) in target.nodes
                if i == expected['after']
            ]
        )
        # Asserting if edge attributes got carried over
        self.assertCountEqual(
            [
                attrs
                for (i, j, attrs) in _target.edges
                if i == expected['before'] or j == expected['before']
            ],
            [
                attrs
                for (i, j, attrs) in target.edges
                if i == expected['after'] or j == expected['after']
            ],
        )
        # Asserting if OTHER nodes and their attributes got carried over
        self.assertCountEqual(
            [
                (i, attrs)
                for (i, attrs) in _target.nodes
                if i != expected['before']
            ],
            [
                (i, attrs)
                for (i, attrs) in target.nodes
                if i != expected['after']
            ]
        )
        # Asserting if OTHER edges and their attributes got carried over
        self.assertCountEqual(
            [
                (i, j, attrs)
                for (i, j, attrs) in _target.edges
                if i != expected['before'] and j != expected['before']
            ],
            [
                (i, j, attrs)
                for (i, j, attrs) in target.edges
                if i != expected['after'] and j != expected['after']
            ],
        )
class GprofLoaderTestCase(unittest.TestCase):
    def setUp(self):
        self.target = GprofLoader(
            os.path.join(
                os.path.dirname(os.path.realpath(__file__)),
                'helloworld/gprof.callgraph.txt'
            ),
            False
        )

    def test_load_call_graph_errors(self):
        # Act
        graph = self.target.load_call_graph()

        # Assert
        self.assertEqual(0, len(self.target.errors))

    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)

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

        # Act
        graph = self.target.load_call_graph(granularity=Gran.FILE)
        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)

    def test_load_call_graph_entry_nodes(self):
        # Arrange
        expected = []

        # Act
        graph = self.target.load_call_graph()

        # Assert
        for (n, attrs) in graph.nodes(data=True):
            if n in expected:
                self.assertTrue('entry' in attrs)
            else:
                self.assertTrue('entry' not in attrs)

    def test_load_call_graph_entry_nodes_file_granularity(self):
        # Arrange
        expected = []

        # Act
        graph = self.target.load_call_graph(granularity=Gran.FILE)

        # Assert
        for (n, attrs) in graph.nodes(data=True):
            if n in expected:
                self.assertTrue('entry' in attrs)
            else:
                self.assertTrue('entry' not in attrs)

    def test_load_call_graph_exit_nodes(self):
        # Arrange
        expected = []

        # Act
        graph = self.target.load_call_graph()

        # Assert
        for (n, attrs) in graph.nodes(data=True):
            if n in expected:
                self.assertTrue('exit' in attrs)
            else:
                self.assertTrue('exit' not in attrs)

    def test_load_call_graph_exit_nodes_file_granularity(self):
        # Arrange
        expected = []

        # Act
        graph = self.target.load_call_graph(granularity=Gran.FILE)

        # Assert
        for (n, attrs) in graph.nodes(data=True):
            if n in expected:
                self.assertTrue('exit' in attrs)
            else:
                self.assertTrue('exit' not in attrs)

    def test_load_call_graph_edges(self):
        # Arrange
        expected = [
            (
                Call('greet_b', './src/helloworld.c', Env.C),
                Call('recursive_b', './src/greetings.c', Env.C)
            ),
            (
                Call('recursive_b', './src/greetings.c', Env.C),
                Call('greet_b', './src/helloworld.c', Env.C)
            ),
            (
                Call('recursive_a', './src/greetings.c', Env.C),
                Call('recursive_b', './src/greetings.c', Env.C)
            ),
            (
                Call('greet_a', './src/helloworld.c', Env.C),
                Call('recursive_a', './src/greetings.c', Env.C)
            ),
            (
                Call('recursive_a', './src/greetings.c', Env.C),
                Call('greet_a', './src/helloworld.c', Env.C)
            ),
            (
                Call('recursive_b', './src/greetings.c', Env.C),
                Call('recursive_a', './src/greetings.c', Env.C)
            ),
            (
                Call('greet_a', './src/helloworld.c', Env.C),
                Call('greet', './src/greetings.c', Env.C)
            ),
            (
                Call('greet', './src/greetings.c', Env.C),
                Call('greet_a', './src/helloworld.c', Env.C)
            ),
            (
                Call('greet_b', './src/helloworld.c', Env.C),
                Call('greet', './src/greetings.c', Env.C)
            ),
            (
                Call('greet', './src/greetings.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)
            ),
            (
                Call('GreeterSayHi', './src/helloworld.c', Env.C),
                Call('main', './src/helloworld.c', Env.C)
            ),
            (
                Call('main', './src/helloworld.c', Env.C),
                Call('GreeterSayHiTo', './src/helloworld.c', Env.C)
            ),
            (
                Call('GreeterSayHiTo', './src/helloworld.c', Env.C),
                Call('main', './src/helloworld.c', Env.C)
            ),
            (
                Call('main', './src/helloworld.c', Env.C),
                Call('addInt', './src/helloworld.c', Env.C)
            ),
            (
                Call('addInt', './src/helloworld.c', Env.C),
                Call('main', './src/helloworld.c', Env.C)
            ),
            (
                Call('main', './src/helloworld.c', Env.C),
                Call('greet_a', './src/helloworld.c', Env.C)
            ),
            (
                Call('greet_a', './src/helloworld.c', Env.C),
                Call('main', './src/helloworld.c', Env.C)
            ),
            (
                Call('main', './src/helloworld.c', Env.C),
                Call('greet_b', './src/helloworld.c', Env.C)
            ),
            (
                Call('greet_b', './src/helloworld.c', Env.C),
                Call('main', './src/helloworld.c', Env.C)
            ),
            (
                Call('main', './src/helloworld.c', Env.C),
                Call('new_Greeter', './src/helloworld.c', Env.C)
            ),
            (
                Call('new_Greeter', './src/helloworld.c', Env.C),
                Call('main', './src/helloworld.c', Env.C)
            )
        ]

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

        # Assert
        self.assertCountEqual(expected, actual)
        for (_, _, attrs) in 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 test_load_call_graph_edges_file_granularity(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
        graph = self.target.load_call_graph(granularity=Gran.FILE)
        actual = graph.edges()

        # Assert
        self.assertCountEqual(expected, actual)
        for (_, _, attrs) in 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 test_load_call_graph_return_edges(self):
        # Act
        graph = self.target.load_call_graph()

        # Assert
        self.assertTrue(nx.is_strongly_connected(graph))
        for (u, v) in nx.get_edge_attributes(graph, 'call'):
            self.assertTrue('return' in graph[v][u])

    def test_load_call_graph_return_edges_file_granularity(self):
        # Act
        graph = self.target.load_call_graph(granularity=Gran.FILE)

        # Assert
        self.assertTrue(nx.is_strongly_connected(graph))
        for (u, v) in nx.get_edge_attributes(graph, 'call'):
            self.assertTrue('return' in graph[v][u])
예제 #9
0
def main():
    args = parse_args()

    call_graph = None
    if args.javacg:
        loader = JavaCGLoader(args.javacg, args.apppackages)
        call_graph = CallGraph.from_loader(loader)
    else:
        cflow_loader = None
        gprof_loader = None
        if args.cflow:
            if not os.path.exists(args.cflow):
                raise Exception('{} not found.'.format(args.cflow))
            else:
                cflow_loader = CflowLoader(args.cflow, reverse=args.reverse)

        if args.gprof:
            if not os.path.exists(args.gprof):
                raise Exception('{} not found.'.format(args.gprof))
            else:
                if os.path.isdir(args.gprof):
                    sources = [
                        os.path.join(args.gprof, filename)
                        for filename in os.listdir(args.gprof)
                        if os.path.isfile(os.path.join(args.gprof, filename))
                    ]
                    gprof_loader = MultigprofLoader(sources,
                                                    processes=args.processes)
                else:
                    gprof_loader = GprofLoader(args.gprof)

        if cflow_loader and gprof_loader:
            call_graph = CallGraph.from_merge(
                CallGraph.from_loader(cflow_loader,
                                      granularity=args.granularity),
                CallGraph.from_loader(gprof_loader,
                                      granularity=args.granularity))
        elif cflow_loader:
            call_graph = CallGraph.from_loader(cflow_loader,
                                               granularity=args.granularity)
        elif gprof_loader:
            call_graph = CallGraph.from_loader(gprof_loader,
                                               granularity=args.granularity)

    if args.output:
        (name, extension) = os.path.splitext(args.output)
        output_format = extension.replace('.', '')
        if output_format not in FORMATTERS:
            output_format = 'txt'
        formatter = FORMATTERS[output_format](call_graph)
        with open(args.output, 'w') as file_:
            if args.verbose:
                file_.write(formatter.write_output())
            else:
                file_.write(formatter.write_summary())
    else:
        formatter = FORMATTERS['txt'](call_graph)
        if args.verbose:
            sys.stdout.write(formatter.write_output())
        else:
            sys.stdout.write(formatter.write_summary())

    if args.showerrors and call_graph.load_errors:
        sys.stdout.write('Parse Errors\n')
        sys.stdout.write('============\n')
        for error in call_graph.load_errors:
            sys.stdout.write(error)
예제 #10
0
class GprofLoaderTestCase(unittest.TestCase):
    def setUp(self):
        self.target = GprofLoader(
            os.path.join(os.path.dirname(os.path.realpath(__file__)),
                         'helloworld/gprof.callgraph.txt'), False)

    def test_load_call_graph_errors(self):
        # Act
        graph = self.target.load_call_graph()

        # Assert
        self.assertEqual(0, len(self.target.errors))

    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)

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

        # Act
        graph = self.target.load_call_graph(granularity=Gran.FILE)
        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)

    def test_load_call_graph_entry_nodes(self):
        # Arrange
        expected = []

        # Act
        graph = self.target.load_call_graph()

        # Assert
        for (n, attrs) in graph.nodes(data=True):
            if n in expected:
                self.assertTrue('entry' in attrs)
            else:
                self.assertTrue('entry' not in attrs)

    def test_load_call_graph_entry_nodes_file_granularity(self):
        # Arrange
        expected = []

        # Act
        graph = self.target.load_call_graph(granularity=Gran.FILE)

        # Assert
        for (n, attrs) in graph.nodes(data=True):
            if n in expected:
                self.assertTrue('entry' in attrs)
            else:
                self.assertTrue('entry' not in attrs)

    def test_load_call_graph_exit_nodes(self):
        # Arrange
        expected = []

        # Act
        graph = self.target.load_call_graph()

        # Assert
        for (n, attrs) in graph.nodes(data=True):
            if n in expected:
                self.assertTrue('exit' in attrs)
            else:
                self.assertTrue('exit' not in attrs)

    def test_load_call_graph_exit_nodes_file_granularity(self):
        # Arrange
        expected = []

        # Act
        graph = self.target.load_call_graph(granularity=Gran.FILE)

        # Assert
        for (n, attrs) in graph.nodes(data=True):
            if n in expected:
                self.assertTrue('exit' in attrs)
            else:
                self.assertTrue('exit' not in attrs)

    def test_load_call_graph_edges(self):
        # Arrange
        expected = [
            (Call('greet_b', './src/helloworld.c',
                  Env.C), Call('recursive_b', './src/greetings.c', Env.C)),
            (Call('recursive_b', './src/greetings.c',
                  Env.C), Call('greet_b', './src/helloworld.c', Env.C)),
            (Call('recursive_a', './src/greetings.c',
                  Env.C), Call('recursive_b', './src/greetings.c', Env.C)),
            (Call('greet_a', './src/helloworld.c',
                  Env.C), Call('recursive_a', './src/greetings.c', Env.C)),
            (Call('recursive_a', './src/greetings.c',
                  Env.C), Call('greet_a', './src/helloworld.c', Env.C)),
            (Call('recursive_b', './src/greetings.c',
                  Env.C), Call('recursive_a', './src/greetings.c', Env.C)),
            (Call('greet_a', './src/helloworld.c',
                  Env.C), Call('greet', './src/greetings.c', Env.C)),
            (Call('greet', './src/greetings.c',
                  Env.C), Call('greet_a', './src/helloworld.c', Env.C)),
            (Call('greet_b', './src/helloworld.c',
                  Env.C), Call('greet', './src/greetings.c', Env.C)),
            (Call('greet', './src/greetings.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)),
            (Call('GreeterSayHi', './src/helloworld.c',
                  Env.C), Call('main', './src/helloworld.c', Env.C)),
            (Call('main', './src/helloworld.c',
                  Env.C), Call('GreeterSayHiTo', './src/helloworld.c', Env.C)),
            (Call('GreeterSayHiTo', './src/helloworld.c',
                  Env.C), Call('main', './src/helloworld.c', Env.C)),
            (Call('main', './src/helloworld.c',
                  Env.C), Call('addInt', './src/helloworld.c', Env.C)),
            (Call('addInt', './src/helloworld.c',
                  Env.C), Call('main', './src/helloworld.c', Env.C)),
            (Call('main', './src/helloworld.c',
                  Env.C), Call('greet_a', './src/helloworld.c', Env.C)),
            (Call('greet_a', './src/helloworld.c',
                  Env.C), Call('main', './src/helloworld.c', Env.C)),
            (Call('main', './src/helloworld.c',
                  Env.C), Call('greet_b', './src/helloworld.c', Env.C)),
            (Call('greet_b', './src/helloworld.c',
                  Env.C), Call('main', './src/helloworld.c', Env.C)),
            (Call('main', './src/helloworld.c',
                  Env.C), Call('new_Greeter', './src/helloworld.c', Env.C)),
            (Call('new_Greeter', './src/helloworld.c',
                  Env.C), Call('main', './src/helloworld.c', Env.C))
        ]

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

        # Assert
        self.assertCountEqual(expected, actual)
        for (_, _, attrs) in 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 test_load_call_graph_edges_file_granularity(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
        graph = self.target.load_call_graph(granularity=Gran.FILE)
        actual = graph.edges()

        # Assert
        self.assertCountEqual(expected, actual)
        for (_, _, attrs) in 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 test_load_call_graph_return_edges(self):
        # Act
        graph = self.target.load_call_graph()

        # Assert
        self.assertTrue(nx.is_strongly_connected(graph))
        for (u, v) in nx.get_edge_attributes(graph, 'call'):
            self.assertTrue('return' in graph[v][u])

    def test_load_call_graph_return_edges_file_granularity(self):
        # Act
        graph = self.target.load_call_graph(granularity=Gran.FILE)

        # Assert
        self.assertTrue(nx.is_strongly_connected(graph))
        for (u, v) in nx.get_edge_attributes(graph, 'call'):
            self.assertTrue('return' in graph[v][u])
def stat(**options):
    cflow_path = options.get('cflow_path', None)
    gprof_path = options.get('gprof_path', None)
    num_processes = options.get('num_processes')
    output = options.get('output', False)
    threshold = options.get('threshold', None)
    subject = options.get('subject', None)
    revision = options.get('revision', None)

    if subject not in settings.ENABLED_SUBJECTS:
        raise CommandError('Subject {0} is not enabled'.format(subject))

    cflow_loader = None
    gprof_loader = None

    cflow_call_graph = None
    gprof_call_graph = None

    begin = datetime.datetime.now()
    if cflow_path:
        fragmentize = False
        if not gprof_path:
            fragmentize = True

        print('Loading cflow call graph')
        cflow_loader = CflowLoader(cflow_path, reverse=True)
        cflow_call_graph = CallGraph.from_loader(cflow_loader, fragmentize)

    if gprof_path:
        fragmentize = False
        if not cflow_path:
            fragmentize = True

        print('Loading gprof call graph')
        if os.path.isdir(gprof_path):
            sources = [
                os.path.join(gprof_path, file_name)
                for file_name in os.listdir(gprof_path) if 'txt' in file_name
            ]
            os.environ['DEBUG'] = '1'
            gprof_loader = MultigprofLoader(sources, processes=num_processes)
            gprof_call_graph = CallGraph.from_loader(gprof_loader, fragmentize)
        else:
            gprof_loader = GprofLoader(gprof_path, reverse=False)
            gprof_call_graph = CallGraph.from_loader(gprof_loader, fragmentize)
    end = datetime.datetime.now()

    call_graph = None
    if cflow_call_graph and gprof_call_graph:
        if 'DEBUG' in os.environ:
            print()

        print('Merging cflow and gprof call graphs')
        call_graph = CallGraph.from_merge(cflow_call_graph,
                                          gprof_call_graph,
                                          fragmentize=True)
    elif cflow_call_graph:
        call_graph = cflow_call_graph
    elif gprof_call_graph:
        call_graph = gprof_call_graph

    if not call_graph:
        print('Nothing to stat. Exiting.')
        sys.exit(0)

    print('Load completed in {0:.2f} seconds'.format(
        (end - begin).total_seconds()))

    print('#' * 50)
    print('              Call Graph Statistics')
    print('#' * 50)
    print('    Nodes               {0}'.format(len(call_graph.nodes)))
    print('    Edges               {0}'.format(len(call_graph.edges)))
    print('    Entry Points        {0}'.format(len(call_graph.entry_points)))
    print('    Exit Points         {0}'.format(len(call_graph.exit_points)))
    print('    Dangerous           {0}'.format(
        len(nx.get_node_attributes(call_graph.call_graph, 'dangerous'))))
    print('    Monolithicity       {0:4f}'.format(call_graph.monolithicity))
    print('    Fragments           {0}'.format(call_graph.num_fragments))
    print('#' * 50)

    if output:
        generate(subject, revision, call_graph, threshold, num_processes)
 def setUp(self):
     self.target = CallGraph.from_loader(GprofLoader(
         os.path.join(os.path.dirname(os.path.realpath(__file__)),
                      'helloworld/gprof.callgraph.txt'), True),
                                         granularity=Granularity.FILE)