views:

1600

answers:

6

I have a large legacy C++ project compiled under Visual Studio 2008. I know there is a reasonably amount of 'dead' code that is not accessed anywhere -- methods that are not called, whole classes that are not used.

I'm looking for a tool that will identify this by static analysis.

This question: Dead code detection in legacy C/C++ project suggests using code coverage tools. This isn't an option as the test coverage just isn't high enough.

It also mentions a -Wunreachable-code. option to gcc. I'd like something similar for Visual Studio. We already use the linker's /OPT:REF option to remove redundant code, but this doesn't report the dead code at a useful level (when used with /VERBOSE there are over 100,000 lines, including a lot from libraries).

Are there any better options that work well with a Visual Studio project?

+5  A: 

You'll want something along the lines of QA-C++ (http://www.programmingresearch.com/QACPP_MAIN.html), also see http://en.wikipedia.org/wiki/List_of_tools_for_static_code_analysis for similar products.

You're looking for a static code analysis tool that detects unreachable code; many coding guidelines (such as MISRA-C++, if I'm not mistaken) require that no unreachable code exists. An analysis tool geared specifically to enforce such a guideline would be your best bet.

And you'll like be able to find other uses for the tool as well.

+2  A: 

I dont know Visual C, and had also recommended the -Wunreachable-code specific coverage tools. As solution for your situation I would try the following:

  1. Make with ctags (or similar programm) a list of all your symbols in your source
  2. Enable in your compiler the dead code elimination (I would assume it defaults to on)
  3. Enable your whole-program/link time optimizations (so he knows that not used functions in your moduls are not required by other externals and get discarded)
  4. Take the symbols from your binary and compare them with the symbols from 1.

Another approach could be some call graph generating tool (e.g. doxygen).

flolo
A: 

One approach that works for me - with Delphi - is to enable debugging, and run your program under the debugger.

When a Delphi program is run under the debugger, the IDE shows in the margin which lines of code can be set as breakpoints. Code which is truly dead - i.e., has been stripped out by the linker/compiler is obvious as breakpoints can't be set there.

Some additional notes, as commenters seem to misunderstand this:

a: You don't need to try setting a breakpoint on each line. Just open up the source file in the IDE, and quickly scroll through it. Dead code is easily spotted.

b: This is NOT a 'code coverage' check. You don't need to run the application to see if it reaches the lines.

c: I'm not familiar enough VS2008 so can't say if this suggestion will work.

Roddy
Cool idea! VS will not allow breakpoints this way in a release build ... but I don't think it highlights the affected lines visually. You could look at the mixed assembly/source view to see which lines don't have assembly though.
Rob Walker
VS would not allow breakpoints in the code that is not "dead", but disabled using macros. Thus one may end up breaking some macros by removing the apparently dead code. This is dangerous!
Ignas Limanauskas
@Ignas : And how, exactly, is ANY approach going to deal with that problem?
Roddy
Runtime dead code detection is different - there will be live code that is only accessed for certain inputs (eg error handlers) which you aren't going to find by just running the code a few times.
Martin Beckett
Also this approach is not very scalable. For a large project, am I supposed to try to set a breakpoint *on each line* and then figure out which ones don't work?
Cheeso
@mgb. You misunderstood. It's about setting breakpoints, not hitting them. If the linker has determined the code is 'dead', it strips it out so you cannot set a breakpoint. Such lines are easily identified within the IDE.
Roddy
@Cheeso: With Delphi, you can see at a glance which lines are breakpointable. No need to actually set any breakpoints. And it scales linearly, which isn't too bad. In my experience stripping out dead code is done on a per-module level while refactoring. Unless your module is huge or is 95% dead, this really doesn't take long.
Roddy
A: 

Write a script that randomly deletes a function (from the source code) and recompiles everything from scratch. If it still compiles - that function was dead code.

Krzysztof Kowalczyk
This won't work. What if there is a chain of 3 related functions, a code "cul-de-sac". Removal of any one of the functions will cause the compile to break, even though the set of them is, all together, dead code.
Cheeso
+3  A: 

I know that Gimpel's Lint products (PC-Lint and Flexelint) will identify unreachable code and unused / unreferenced modules.

They both fall in the category of static analysis tools.

I have no affiliation w/ Gimpel, just a satisfied long-term customer.

Dan
A: 

Dead code analysis like this requires a global analysis of your entire project. You can't get this information by analyzing translation units individually (well, you can detect dead entities if they are entirely within a single translation unit, but I don't think that's what you are really looking for).

We've used the DMS Software Reengineering Toolkit to implement exactly this for Java code, by parsing all the compilation-units involved, building symbol tables for everything and chasing down all the references. A top level definition with no references and no claim of being an external API item is dead. This tool also automatically strips out the dead code, and at the end you can choose what you want: the report of dead entities, or the code stripped of those entities.

DMS also parses C++ (specifically including VS 2008) and builds all the necessary symbol tables. Tracking down the dead references would be straightforward from that point. DMS could also be used to strip them out. See http://www.semanticdesigns.com/Products/DMS/DMSToolkit.html

Ira Baxter