Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find size contributed by each external library on iOS

Tags:

I'm trying to reduce my app store binary size and we have lots of external libs that might be contributing to the size of the final ipa. Is there any way to find out how much each external static lib takes up in the final binary (Other than going about removing each one ?) ?

like image 545
Samhan Salahuddin Avatar asked Aug 14 '15 06:08

Samhan Salahuddin


1 Answers

All of this information is contained in the link map, if you have the patience for sifting through it (for large apps, it can be quite large). The link map has a listing of all the libraries, their object files, and all symbols that were packaged into your app, all in human-readable text. Normally, projects aren't configured to generate them by default, so you'll have to make a quick project file change.

From within Xcode:

  1. Under 'Build Settings' for your target, search for "map"
  2. In the results below, under the 'Linking' section, set 'Write Link Map File' to "Yes"
  3. Make sure to make note of the full path and file name listed under 'Path to Link Map File'

The next time you build your app you'll get a link map dumped to that file path. Note that the path is relative to your app's location in the DerivedData folder (usually ~/Library/Developer/Xcode/DerivedData/<your-app-name>-<random-string-of-letters-and-numbers>/Build/Intermediates/..., but YMMV). Since it's just a text file, you can read it with any text editor.

The contents of the link map are divided into 3 sections, of which 2 will be relevant to what you're looking for:

  1. Object Files: this section contains a listing of all of the object files included in your final app, including your own code and that of any third-party libraries you've included. Importantly, each object file also lists the library where it came from;
  2. Sections: this section, not relevant to your question, contains a list of the processor segments and their sections;
  3. Symbols: this section contains the raw data that you're interested in: a list of all symbols/methods with their absolute location (i.e. address in the processor's memory map), size, and most important of all, a cross-reference to their containing object module (under the 'File' column).

From this raw data, you have everything you need to do the required size calculation. From #1, you see that, for every library, there are N possible constituent object modules; from #2, you see that, for every object module, there are M possible symbols, each occupying size S. For any given library, then, your rough order of size will be something like O(N * M * S). That's only to give you an indication of the components that would go into your actual calculations, it's not any sort of a useful formula. To perform the calculation itself, I'm sorry to say that I'm not aware of any existing tools that will do the requisite processing for you, but given that the link map is just a text file, with a little script magic and ingenuity you can construct a script to do the heavy lifting.

For example, I have a little sample project that links to the following library: https://github.com/ColinEberhardt/LinqToObjectiveC (the sample project itself is from a nice tutorial on ReactiveCocoa, here: http://www.raywenderlich.com/62699/reactivecocoa-tutorial-pt1), and I want to know how much space it occupies. I've generated a link map, TwitterInstant-LinkMap-normal-x86_64.txt (it runs in the simulator). In order to find all object modules included by the library, I do this:

$ grep -i "libLinqToObjectiveC.a" TwitterInstant-LinkMap-normal-x86_64.txt 

which gives me this:

[  8] /Users/XXX/Library/Developer/Xcode/DerivedData/TwitterInstant-ecppmzhbawtxkwctokwryodvgkur/Build/Products/Debug-iphonesimulator/libLinqToObjectiveC.a(LinqToObjectiveC-dummy.o) [  9] /Users/XXX/Library/Developer/Xcode/DerivedData/TwitterInstant-ecppmzhbawtxkwctokwryodvgkur/Build/Products/Debug-iphonesimulator/libLinqToObjectiveC.a(NSArray+LinqExtensions.o) [ 10] /Users/XXX/Library/Developer/Xcode/DerivedData/TwitterInstant-ecppmzhbawtxkwctokwryodvgkur/Build/Products/Debug-iphonesimulator/libLinqToObjectiveC.a(NSDictionary+LinqExtensions.o) 

The first column contains the cross-references to the symbol table that I need, so I can search for those:

$ cat TwitterInstant-LinkMap-normal-x86_64.txt | grep -e "\[  8\]" 

which gives me:

0x100087161 0x0000001B  [  8] literal string: PodsDummy_LinqToObjectiveC 0x1000920B8 0x00000008  [  8] anon 0x100093658 0x00000048  [  8] l_OBJC_METACLASS_RO_$_PodsDummy_LinqToObjectiveC 0x1000936A0 0x00000048  [  8] l_OBJC_CLASS_RO_$_PodsDummy_LinqToObjectiveC 0x10009F0A8 0x00000028  [  8] _OBJC_METACLASS_$_PodsDummy_LinqToObjectiveC 0x10009F0D0 0x00000028  [  8] _OBJC_CLASS_$_PodsDummy_LinqToObjectiveC 

The second column contains the size of the symbol in question (in hexadecimal), so if I add them all up, I get 0x103, or 259 bytes.

Even better, I can do a bit of stream hacking to whittle it down to the essential elements and do the addition for me:

$ cat TwitterInstant-LinkMap-normal-x86_64.txt | grep -e "\[  8\]" | grep -e "0x" | awk '{print $2}' | xargs printf "%d\n" | paste -sd+ - | bc 

which gives me the number straight up:

259 

Doing the same for "\[ 9\]" (13016 bytes) and "\[ 10\]" (5503 bytes), and adding them to the previous 259 bytes, gives me 18778 bytes.

You can certainly improve upon the stream hacking I've done here to make it a bit more robust (in this implementation, you have to make sure you get the exact number of spaces right and quote the brackets), but you at least get the idea.

like image 174
fullofsquirrels Avatar answered Sep 27 '22 21:09

fullofsquirrels