1#!/usr/bin/python 2# Copyright 2018 gRPC authors. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16# This script analyzes link map file generated by Xcode. It calculates and 17# prints out the sizes of each dependent library and the total sizes of the 18# symbols. 19# The script takes one parameter, which is the path to the link map file. 20 21import re 22import sys 23 24 25def parse_link_map(filename): 26 table_tag = {} 27 state = "start" 28 29 table_stats_symbol = {} 30 table_stats_dead = {} 31 section_total_size = 0 32 symbol_total_size = 0 33 34 boringssl_size = 0 35 core_size = 0 36 objc_size = 0 37 protobuf_size = 0 38 39 lines = open(filename, encoding="utf-8", errors="ignore").readlines() 40 for line in lines: 41 line_stripped = line[:-1] 42 if "# Object files:" == line_stripped: 43 state = "object" 44 continue 45 elif "# Sections:" == line_stripped: 46 state = "section" 47 continue 48 elif "# Symbols:" == line_stripped: 49 state = "symbol" 50 continue 51 elif "# Dead Stripped Symbols:" == line_stripped: 52 state = "dead" 53 continue 54 55 if state == "object": 56 segs = re.search("(\[ *[0-9]*\]) (.*)", line_stripped) 57 table_tag[segs.group(1)] = segs.group(2) 58 59 if state == "section": 60 if len(line_stripped) == 0 or line_stripped[0] == "#": 61 continue 62 segs = re.search("^(.+?)\s+(.+?)\s+.*", line_stripped) 63 section_total_size += int(segs.group(2), 16) 64 65 if state == "symbol": 66 if len(line_stripped) == 0 or line_stripped[0] == "#": 67 continue 68 segs = re.search("^.+?\s+(.+?)\s+(\[.+?\]).*", line_stripped) 69 if not segs: 70 continue 71 target = table_tag[segs.group(2)] 72 target_stripped = re.search("^(.*?)(\(.+?\))?$", target).group(1) 73 size = int(segs.group(1), 16) 74 if not target_stripped in table_stats_symbol: 75 table_stats_symbol[target_stripped] = 0 76 table_stats_symbol[target_stripped] += size 77 if "BoringSSL" in target_stripped: 78 boringssl_size += size 79 elif "libgRPC-Core" in target_stripped: 80 core_size += size 81 elif ( 82 "libgRPC-RxLibrary" in target_stripped 83 or "libgRPC" in target_stripped 84 or "libgRPC-ProtoLibrary" in target_stripped 85 ): 86 objc_size += size 87 elif "libProtobuf" in target_stripped: 88 protobuf_size += size 89 90 for target in table_stats_symbol: 91 symbol_total_size += table_stats_symbol[target] 92 93 return ( 94 core_size, 95 objc_size, 96 boringssl_size, 97 protobuf_size, 98 symbol_total_size, 99 ) 100 101 102def main(): 103 filename = sys.argv[1] 104 ( 105 core_size, 106 objc_size, 107 boringssl_size, 108 protobuf_size, 109 total_size, 110 ) = parse_link_map(filename) 111 print("Core size:{:,}".format(core_size)) 112 print("ObjC size:{:,}".format(objc_size)) 113 print("BoringSSL size:{:,}".format(boringssl_size)) 114 print("Protobuf size:{:,}\n".format(protobuf_size)) 115 print("Total size:{:,}".format(total_size)) 116 117 118if __name__ == "__main__": 119 main() 120