- Removing Cocoa Dead Code Using Code Coverage
- permanent link
- July 28, 2008
-
Motivation
It is not uncommon to have dead code in programs, that is uncalled methods, or code that is never executed.
Dead code makes the code cumbersome and harder to read. With dead code, it is also harder to figure out each class responsabilities. Executables get bigger and need more memory.
So, dead code must be removed. The question is how.
Note 1: we do not consider here dead code stripping by the linker. It only works with C / C++, and although it does reduce the executable size by removing unused symbols and functions, it does not improve code clarity, which is the primary goal here.
Note 2: static analysis can reveal some unreacheable code by adding
-Wunreachable-codeto GCC warning flags. We're goind to see techniques that also detect reacheable code that is never called. However, this flag is always useful to add in your projects.Objective-C / Cocoa needs code coverage
Eclipse makes is easy to detect uncalled Java private methods by issuing a warning.

In Objective-C however, it is impossible to know which methods will be called, due to the dynamic runtime, Cocoa key-value coding or bundles loading.
So, dead code must be hunt at runtime. The program must be run in order to go through most if not all methods. This process is known as code coverage.
When we know the methods that are called, we know the ones that are not. It is important to note that an uncalled method might be called in another run scenario. It remains up to the programmer to know what to remove.
I can think of three techniques do code coverage XCode and Objective-C / Cocoa: breakpoints, dtrace and gcov.
Claudio also told me about a Cedric's technique: the GCC
-finstrument-functionscompilation flag. See below.I will be glad if you can comment on this or share your own techniques.
Breakpoints
It is a rudimentary but efficient technique.
- put breakpoints on each method
- run the program
- remove each breakpoint you stop on
- remaining breakpoint signal uncalled methods

Obviously, this technique can be a hassle if you have more than a few methods.
DTrace
The idea here is to compare the list of your class methods with the list of those methods which are called at runtime.
You can do this with two DTrace scripts. DTrace is a powerful tool to dynamicaly examine the behaviour of user programs or the OS itself. Its scripting language is called D. Since DTrace is dynamic, you do not need to recompile your application. You do not even need to have the source code :-)
The first script shows all the methods of a class. Replace
18678by the pid of your process, andMyClassby the name of your class.$ sudo dtrace -l -n "objc18678:MyClass::entry" ID PROVIDER MODULE FUNCTION NAME 18580 objc18678 MyClass -myCalledMethod entry 18581 objc18678 MyClass -myUncalledMethod entry 18582 objc18678 MyClass -awakeFromNib entryThe second script objc_msg_calls.d is a bit longer. It counts each method call and displays the result before termination. Here again, you must give the script your class name.
$ sudo dtrace -s objc_msg_calls.d -c /Untitled.app/Contents/MacOS/Untitled MyClass awakeFromNib 1 MyClass myCalledMethod 1The difference between the two lists reveals the uncalled methods.
Gcov
Gcov is the GCC code coverage tool. It works this way:
- compile you code with special options, it produces .gcno files beside your .o files
- run your executable, it produces .gcda files beside your .o files
- run gcov tool on the .gcda files to produce .gcov files
To enable Gcov on your project, follow these four simple steps (from Apple QA1514):
- duplicate your build configuration to say "Coverage"
- check "Generate Test Coverage Files"
- check "Instrument Program Flow"
- add "-lgcov" to "Other Linker Flags"

After you've compiled and run your program, you still need to generate gcov files. This can be done by running the coverage.sh script that you will add to your project. Adapt the
OBJ_DIRpath to your settings.$ ./coverage.shThe .gcov files are then generated in the
coverage/directory. Each line of the source code is prefixed by the number of passages. Dead code is marked with#####.1: 13:- (void)myCalledMethod { 1: 14: NSLog(@"-- myCalledMethod"); -: 15:} -: 16: #####: 17:- (void)myUncalledMethod { #####: 18: NSLog(@"-- myUncalledMethod"); -: 19:} -: 20: 1: 21:- (void)awakeFromNib { 1: 22: NSLog(@"-- awakeFromNib"); 1: 23: [self myCalledMethod];GCC flag
According to GCC man page, you can have these two functions called after entering and before exiting any other function by adding the compilation flag
-finstrument-functions:void __cyg_profile_func_enter (void *this_fn, void *call_site); void __cyg_profile_func_exit (void *this_fn, void *call_site);
The parameters are the addresses of the callee and the caller. They can be converted into symbols by calling
atoswithopenfrom within the function, as in profile_functions.c.So, add the compilation flag and this
profile_functions.cto your project, and the output will look like this:main (in Untitled) (main.m:12) -[MyClass awakeFromNib] (in Untitled) (MyClass.m:21) -[MyClass myCalledMethod] (in Untitled) (MyClass.m:13)Conclusion
Although automatic dead-code stripping doesn't make sense for Objective-C / Cocoa, dead code still reduces code readability and overall quality.
We showed four quick and easy techniques to detect uncalled methods at runtime. The programmer then has to read his code and decide if those methods should be removed or not.
Each technique has its pros and cons, which we could synthesize as follows:
- breakpoints: very simple, but doesn't scale
- dtrace: does not require the source code, but applications must be run as root
- gcov: needs relink, but reveals unreached code inside called methods
- gcc flag: needs recompilation and calls to atos, but quite simple and customizable