0

Suppose there is a C++ source code base of millions of lines composed of several hundred *.cpp and *.h files.

There is also a driver program main.cpp that uses several header files from the above source code.

Suppose I want to scan the entire code base and

(1) pull only those files that are used by main.cpp,
and
(2) keep only those classes and functions that are used by main.cpp

I have finished step step#1. How can I do the #2?

How can I do that?

9
  • 1
    What is your goal here? Because C/C++ is completely messed up when it comes to static analyzability, so there is no good way short of instrumenting the compiler. But there might be a couple of ways that might be good enough for your specific purposes.
    – amon
    Commented Oct 10, 2023 at 18:49
  • 1
    @candied_orange Yes, that was one of my thoughts – compile the compilation unit, then use objdump or readelf to extract the linker tables from the object code to see which other functions it depends on. This is 100% reliable, but tedious and only gives us objects with linkage (e.g. functions, extern variables). It cannot tell us anything about classes, templates, typedefs, macros, and so on. If OP is just trying to navigate the source code, a language server like clangd might be more appropriate (or a conventional C++ IDE).
    – amon
    Commented Oct 10, 2023 at 19:00
  • 4
    Ah, some other strategies that might be useful: (1) Put the code into Git, or create a new branch. Then remove stuff that looks irrelevant and check if it still compiles. If so, commit and repeat. If not, revert to last commit and try remove something else. With a bit of intuition, this should quickly get from the "millions" into the "thousands" of lines. (2) Run the code under a profiler (e.g. valgrind-callgrind or gcc --coverage) and use a tool to draw the call graph. Unlike linker stuff this won't be perfect, but it might highlight the most important relationships.
    – amon
    Commented Oct 10, 2023 at 19:44
  • 1
    A good "characterization test" that makes it do the most important thing it should be able to do would be handy. On the off chance you break its behavior without breaking the build. Commented Oct 10, 2023 at 19:49
  • 5
    I have to question why understanding an algorithm requires deleting other code not used in the algorithm. You should be able to navigate directly to the algorithm's actual dependencies (and their dependencies) with a correctly set-up IDE, so just do that instead of reading whole files and worrying about which parts are relevant.
    – Useless
    Commented Oct 11, 2023 at 10:48

2 Answers 2

4

This is not easy and will be significant work. You first want to reach a stage where you can pick any set of files and they either compile or they don’t, but if they compile, they will give the same result. Why wouldn’t they? For example because header files #define things that other header files or source files use.

Say your source file says

#ifndef PI
    #define PI 3.15
#endif

If you stop including a header file with a precise definition of PI and nothing else, your program is suddenly very inaccurate.

So you look for things defined in header files and where they are used. The source file here would be changed, the PI madness removed and the header file included instead.

Look for things with context. I hope nobody does this, but someone could open a namespace and include a header file. So if you change include order things compile differently. Find and remove all rubbish like that.

Now make all headerfiles standalone. That means a C++ source file stating just “#include myheader.h” must compile. So you comment out all include statements. Then try compiling the first header file. If it doesn’t compile fix it by making it include everything it needs. If a.h needs something from b.h then you include b.h. If b.h doesn’t compile on its own then you fix it first and so on until all your header files compile and act the same no matter how they are included.

Then you do the same with your source files. And then your main can build with the source files you want after adding everything needed to fix linker errors.

Now if you find you have a huge source file that includes some small utility that you need, and a huge bunch of stuff you don’t care about, you split it up. And so on.

1
  • The long road to sanity begins with a single step... ;-) Commented Oct 11, 2023 at 18:07
1

The linker removes unreferenced code. The map file generated by the linker will show what functions are still referenced. You can then begin removing portions of code in main.cpp, compile, link, and verify if the algorithm of interest is still referenced in the map file.

2
  • where can I find this map file? I am using MSYS2, CLion and Visual Studio 2017.
    – user366312
    Commented Oct 11, 2023 at 14:07
  • In Visual Studio, go to the project properties -> Configuration Properties -> Linker -> Debugging -> Generate Map File to output a map file. There is an option to give the map file a name different from the output file. The generated map file will be located somewhere in the configuration directory (Release, Debug, etc.) with a .map extension. Commented Oct 12, 2023 at 14:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.