Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I find circular dependencies?

Tags:

c++

Could someone suggest me a tool to find circular dependencies? I tried with a graph of the project but it has hundreds of header files so is very complicated to find them.

I edit the post with the meaning of circular dependency:

  • File A.h has a #include "B.h" guard.
  • File B.h has a #include "A.h" guard.

Thanks.

like image 899
Emilio Avatar asked Mar 27 '12 08:03

Emilio


2 Answers

I have found one way to get circular dependencies:

  1. Generate a DOT file which describes a #include dependency directed graph using cinclude2dot.pl Perl script.

    ./cinclude2dot.pl --src path_to_include_dir graph.dot

  2. Decompose directed graph into strongly connected components (circular dependencies):

    sccmap -v graph.dot

like image 64
Emilio Avatar answered Nov 11 '22 17:11

Emilio


You could query for possible or actual inclusion cycles, because the preprocessing directives actually are a language, to be debugged...

To know about actual cycles, you could use preprocessor cpp with options

-M  Instead of outputting the result of preprocessing, output a rule suitable for make describing the dependencies of the main source file...

or better

-MM Like -M but do not mention header files that are found in system header directories, nor header files that are included, directly or indirectly, from such a header.

and

-MF file
       When used with -M or -MM, specifies a file to write the dependencies to.  If no -MF switch is given the preprocessor sends the rules to the same place it would have sent preprocessed output.

You will get an error on nesting deep overflow when a cycle is found, and the output specified with -MF should be useful to spot the problem.

To know about possible cycles an approximate analysis, that recursively visit source files, should be easily feasible, using a map to track included files.

edit: here is sketched a program for such approximate analysis

#include <set>
#include <vector>
#include <string>
#include <fstream>
#include <cstdlib>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <stdexcept>

#include <boost/foreach.hpp>
#include <boost/filesystem.hpp>
#include <boost/program_options.hpp>

using namespace std;
using namespace boost;
using namespace boost::filesystem;
using namespace boost::program_options;

struct inclusions
{
    inclusions(int argc, char **argv)
    {
        options_description ops("detect_loops usage");
        ops.add_options()
             ("include,I", value< vector<string> >(), "search paths")
             ("file,F",    value< string >(),         "file to be analyzed");
        variables_map vm;
        store(parse_command_line(argc, argv, ops), vm);
        notify(vm);

        path start = locate(vm["file"].as<string>());
        simstack.push_back(start);

        // file directory is always search start
        include_paths.push_back(start.parent_path());

        if (vm.count("include"))
        {
            vector<string> s = vm["include"].as< vector<string> >();
            copy(s.begin(), s.end(), back_inserter(include_paths));
        }

        scan_includes();
    }

    typedef vector<path> t_paths;
    t_paths include_paths;

    t_paths simstack;

    typedef vector<t_paths> t_cycles;
    t_cycles cycles;

    set<path> analyzed;

    path locate(string file)
    {
        path p(file);
        if (exists(p))
            return p;
        BOOST_FOREACH(path i, include_paths)
        {
            path q = i / p;
            if (exists(q))
                return q;
        }
        throw domain_error(file + " not fund");
    }

    void scan_includes()
    {
        path c = simstack.back();
        if (analyzed.find(c) != analyzed.end())
            return;

        ifstream f(c.string());
        string l;
        while (getline(f, l))
        {
            char included[256 + 1];
            if (sscanf(l.c_str(), " # include \"%256[^\"]\"", included) == 1)
            {
                path p = locate(included);

                // check loops before recurse
                t_paths::iterator g = find(simstack.begin(), simstack.end(), p);
                if (g != simstack.end())
                {
                    t_paths loop(g, simstack.end());
                    loop.push_back(p);
                    cycles.push_back(loop);
                }
                else
                {
                    simstack.push_back(p);
                    scan_includes();
                    simstack.pop_back();
                }
            }
        }

        analyzed.insert(c);
    }
};

int main_detect_loops(int argc, char **argv)
{
    try
    {
        inclusions i(argc, argv);
        BOOST_FOREACH(inclusions::t_paths p, i.cycles)
        {
            copy(p.begin(), p.end(), ostream_iterator<path>(cout, ","));
            cout << endl;
        }
        return 0;
    }
    catch(const std::exception &e)
    {
        cerr << e.what() << endl;
        return 1;
    }
}
like image 2
CapelliC Avatar answered Nov 11 '22 16:11

CapelliC