aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/profiling/ios_bin/binary_diff.py
blob: 6d5ae65e468114286837f9225fee989a157b742f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env python2.7
#
# Copyright 2018 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import glob
import multiprocessing
import os
import shutil
import subprocess
import sys
from parse_link_map import parse_link_map

sys.path.append(
    os.path.join(
        os.path.dirname(sys.argv[0]), '..', '..', 'run_tests', 'python_utils'))
import comment_on_pr

size_labels = ('Core', 'ObjC', 'BoringSSL', 'Protobuf', 'Total')

argp = argparse.ArgumentParser(
    description='Binary size diff of gRPC Objective-C sample')

argp.add_argument(
    '-d',
    '--diff_base',
    type=str,
    help='Commit or branch to compare the current one to')

args = argp.parse_args()


def dir_size(dir):
    total = 0
    for dirpath, dirnames, filenames in os.walk(dir):
        for f in filenames:
            fp = os.path.join(dirpath, f)
            total += os.stat(fp).st_size
    return total


def get_size(where, frameworks):
    build_dir = 'src/objective-c/examples/Sample/Build-%s/' % where
    if not frameworks:
        link_map_filename = 'Build/Intermediates.noindex/Sample.build/Release-iphoneos/Sample.build/Sample-LinkMap-normal-arm64.txt'
        return parse_link_map(build_dir + link_map_filename)
    else:
        framework_dir = 'Build/Products/Release-iphoneos/Sample.app/Frameworks/'
        boringssl_size = dir_size(
            build_dir + framework_dir + 'openssl.framework')
        core_size = dir_size(build_dir + framework_dir + 'grpc.framework')
        objc_size = dir_size(build_dir + framework_dir + 'GRPCClient.framework') + \
                    dir_size(build_dir + framework_dir + 'RxLibrary.framework') + \
                    dir_size(build_dir + framework_dir + 'ProtoRPC.framework')
        protobuf_size = dir_size(
            build_dir + framework_dir + 'Protobuf.framework')
        app_size = dir_size(
            build_dir + 'Build/Products/Release-iphoneos/Sample.app')
        return core_size, objc_size, boringssl_size, protobuf_size, app_size


def build(where, frameworks):
    shutil.rmtree(
        'src/objective-c/examples/Sample/Build-%s' % where, ignore_errors=True)
    subprocess.check_call(
        'CONFIG=opt EXAMPLE_PATH=src/objective-c/examples/Sample SCHEME=Sample FRAMEWORKS=%s ./build_one_example.sh'
        % ('YES' if frameworks else 'NO'),
        shell=True,
        cwd='src/objective-c/tests')
    os.rename('src/objective-c/examples/Sample/Build',
              'src/objective-c/examples/Sample/Build-%s' % where)


text = ''
for frameworks in [False, True]:
    build('new', frameworks)
    new_size = get_size('new', frameworks)
    old_size = None

    if args.diff_base:
        old = 'old'
        where_am_i = subprocess.check_output(
            ['git', 'rev-parse', '--abbrev-ref', 'HEAD']).strip()
        subprocess.check_call(['git', 'checkout', '--', '.'])
        subprocess.check_call(['git', 'checkout', args.diff_base])
        subprocess.check_call(['git', 'submodule', 'update'])
        try:
            build('old', frameworks)
            old_size = get_size('old', frameworks)
        finally:
            subprocess.check_call(['git', 'checkout', '--', '.'])
            subprocess.check_call(['git', 'checkout', where_am_i])
            subprocess.check_call(['git', 'submodule', 'update'])

    text += ('****************FRAMEWORKS*****************\n'
             if frameworks else '******************STATIC*******************\n')
    row_format = "{:>10}{:>15}{:>15}" + '\n'
    text += row_format.format('New size', '', 'Old size')
    for i in range(0, len(size_labels)):
        if old_size == None:
            diff_sign = ' '
        elif new_size[i] == old_size[i]:
            diff_sign = ' (=)'
        elif new_size[i] > old_size[i]:
            diff_sign = ' (>)'
        else:
            diff_sign = ' (<)'
        text += ('\n' if i == len(size_labels) - 1 else '') + row_format.format(
            '{:,}'.format(new_size[i]), size_labels[i] + diff_sign,
            '{:,}'.format(old_size[i]) if old_size != None else '')
    text += '\n'

print text

comment_on_pr.comment_on_pr('```\n%s\n```' % text)