2

Somewhat related to this, but it still doesn't quite answer my question

I have a C project, and i would like to enumerate all unreferenced functions (including non-static, so setting compiler option for werror=unused-function only partially works...) in order to identify and clean up the codebase.

One way I thought is to enumerate all the functions in the project, and then make a script to go through each and see if it is called via cscope... but I'm not sure how to get a list-form of all the functions in the first place. That link above has a solution that is failing for me.

Any other ideas are welcome.

liekamg
  • 23
  • 5
  • *That link above has a solution that is failing for me* - failing how? – Eugene Sh. Nov 14 '17 at 19:13
  • 1
    *Compiler* options cannot achieve this for functions with external linkage because C has separate compilation for each translation unit. Just because a (non-`static`) function is not called in a given translation unit where it is declared does not mean that there isn't some other TU in which it is called. – John Bollinger Nov 14 '17 at 19:14
  • Hi Eugene, I am getting the error below: cscope -R -L -2 ".*" | awk -F ' ' '{print $2 "#" $1}' | sort | uniq cscope: cannot find file .. cscope: no source files found – liekamg Nov 14 '17 at 19:15
  • 1
    Even *linker* objects cannot reliably do this for dynamic objects, because you don't necessarily know until run time whether a given dynamic symbol is used. – John Bollinger Nov 14 '17 at 19:16
  • @JohnBollinger, that is exactly my point :) therefore compiler options is not the answer here. – liekamg Nov 14 '17 at 19:17
  • The `cscope` method you linked to appears to assume that the project already uses `cscope`, so that it has an existing `cscope` database. Is that the case for you? – John Bollinger Nov 14 '17 at 19:24
  • This sounds like a subset of the features an instrumenting code coverage analysis covers. Is that an option? I could name one example of such a tool (no affiliation), but I guess it is not needed. – Yunnosch Nov 14 '17 at 19:32
  • @JohnBollinger, yes. I can actually load cscope with the recursive R option in that same directory and query symbols just fine. It's just that command as a whole in the solution link above which is failing. Are there other ways of enumerating all functions within a project? – liekamg Nov 14 '17 at 19:33
  • 1
    Well, I don't know what to tell you. The command works for me. The error message you present makes me suspect a quoting problem on the command line, but I don't see such a problem in the command you say you ran. – John Bollinger Nov 14 '17 at 20:04

5 Answers5

1

Knowing if a function will be called or not is an undecidable problem (analogous to the stop issue for Turing machines). As you can call a function through a pointer (as it is done by callbacks) you cannot actually determine if a function will be called until you actually take it out from the code and run the code.

Linker only links functions that are referenced in the code, and doesn't know if they belong to dead code that will not be called.

If you want to know all the referenced functions in your code, just grep the output of nm(1) command on all your .o files, to get all the U undefined references (this is the list of functions that must be externally linked to your code). This will list all the external references to functions that must be resolved by the linker. If your function is not there, then it is not used by that module. You can match this list with the list of external functions (the ones marked as T in nm(1) output) of the .o files that you want to check (or shared objects .so) and you'll see (as the linker does) which ones are published to the linker but not referenced in your code. Think twice, as this only represents a direct reference, you have to manage also for indirect references (your module asks for a function in another module, that finally asks for the function you are trying to check).

In case your functions are static (only file visibility) just surround the function definition by a #if 0 directive, and you'll get if the function is being referenced somewhere.

I repeat, you cannot easily know if a function will be called in your code, you can know if it is referenced somewhere.

I don't know what are you trying to identify with this question, but you can run into the XY problem instance (what you ask is not what you try to solve)

By the way, defined functions in .h header files are commonly declared inline by developers to optimice function call/return execution. For this reason, they will be inlined where they are used and so, no reference appears to them on linking, so you have to search for them in the code (with the added problem of being macro expanded, so you need to run the preprocessor first to find the references to those functions)

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31
  • It is not NP-complete, it is *undecidable*. – n. m. could be an AI Nov 15 '17 at 10:03
  • Of course one should try to find *unused* functuon rather than "uncalled". – n. m. could be an AI Nov 15 '17 at 10:11
  • @n.m., Well, that depends of what you understand for _unusing_ a function.... and you are right... it's undecidable, and not NP-complete. My apologies for the mistake. – Luis Colorado Nov 15 '17 at 10:33
  • To put it simply, if you can remove a function definition and still successfully build the program, that function is unused. – n. m. could be an AI Nov 15 '17 at 10:59
  • @n.m., that is preciselly what I mean as _unreferenced_. It's only then, when the linker makes no complaint about unreferenced function, when the function is not referenced anywhere.... – Luis Colorado Nov 15 '17 at 11:40
  • Ah sorry didn't notice that. – n. m. could be an AI Nov 15 '17 at 11:41
  • @LuisColorado, I was not expecting such a thorough answer! My wording was bad I apologize for that, some concepts you threw in there though really caught my interest. Didn't know about those, so my vagueness was a blessing in disguise I guess ;). Basically my question was indeed to look for unreferenced functions. I do have quite a bunch of inline functions actually defined in h files for this codebase. So what you are telling me is for those cases, we cannot rely on nm(1) to expose those? Can the preprocessor be used in conjunction with such a tool (or a different one)? Thanks. – liekamg Nov 15 '17 at 20:44
  • @liekamg, when you inline a function, the compiler copies the code of the function in the place you are using it, so there's no reference to the function, as there's no function call _per se_. This is done normally with small functions, and they are limited (cannot inline a recursive call, for obvious reasons) If the compiler cannot inline a function, it will make a normal call. This is normally a hint to the compiler, but care must be taken with this. – Luis Colorado Nov 16 '17 at 08:50
  • @liekamg ... but those inlined functions don't have to worry you about. When the compiler sees a reference to an inlined function, it copies the actual code _in line_ to make the call as effective as possible (so the function is referenced _by expansion_) and when it is not referenced anywhere, the compiler doesn't use(expand) it, so you don't have to worry about inline functions. – Luis Colorado Nov 16 '17 at 08:57
1

You can try CppDepend, it will give you all the not used functions of your code base.

0

I would use a static code analysis tool such as lint. It is a good tool for discovering potential problems in your code as well as for keeping it tidy, since it can be configured to point out unused functions/variables/etc.

Others have suggested Cppcheck which is a free alternative, but I have not used it so I cannot personally vouch for it.

Andrew Cottrell
  • 3,312
  • 3
  • 26
  • 41
0

There are g++ compiler options to remove unused code or not, and you can use the difference in symbol tables to point to unused non-virtual methods. Virtual methods are "used" by virtue of being linked into the vtable even if they are never called, so the only way to find those is via coverage tools or perhaps lint. Dynamically linked library methods are a different issue, as you should only have a limited number of published entrypoints that you need to check through. And anyway you have to decide if some other client would want those entrypoints.

Gem Taylor
  • 5,381
  • 1
  • 9
  • 27
0

I do this in ncurses, to see which library symbols are used by a (custom) script list-used.sh which makes a list of the symbols exported from its libraries and which of those are used (or not) from programs linked against the libraries.

The output of the script is a report which makes up most of the test/README file.

In other answers, there were tools suggested which would be either (a) unavailable or (b) require extensive rework of the build structure (to put a few hundred files into a single command-line as required by those tools).

Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105