• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3# Copyright 2018 gRPC authors.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import argparse
18import glob
19import multiprocessing
20import os
21import shutil
22import subprocess
23import sys
24
25from parse_link_map import parse_link_map
26
27sys.path.append(
28    os.path.join(
29        os.path.dirname(sys.argv[0]), "..", "..", "run_tests", "python_utils"
30    )
31)
32import check_on_pr
33
34# Only show diff 1KB or greater
35_DIFF_THRESHOLD = 1000
36
37_SIZE_LABELS = ("Core", "ObjC", "BoringSSL", "Protobuf", "Total")
38
39argp = argparse.ArgumentParser(
40    description="Binary size diff of gRPC Objective-C sample"
41)
42
43argp.add_argument(
44    "-d",
45    "--diff_base",
46    type=str,
47    help="Commit or branch to compare the current one to",
48)
49
50args = argp.parse_args()
51
52
53def dir_size(dir):
54    total = 0
55    for dirpath, dirnames, filenames in os.walk(dir):
56        for f in filenames:
57            fp = os.path.join(dirpath, f)
58            total += os.stat(fp).st_size
59    return total
60
61
62def get_size(where):
63    build_dir = "src/objective-c/examples/Sample/Build/Build-%s/" % where
64    link_map_filename = "Build/Intermediates.noindex/Sample.build/Release-iphoneos/Sample.build/Sample-LinkMap-normal-arm64.txt"
65    # IMPORTANT: order needs to match labels in _SIZE_LABELS
66    return parse_link_map(build_dir + link_map_filename)
67
68
69def build(where):
70    subprocess.check_call(["make", "clean"])
71    shutil.rmtree(
72        "src/objective-c/examples/Sample/Build/Build-%s" % where,
73        ignore_errors=True,
74    )
75    subprocess.check_call(
76        (
77            "CONFIG=opt EXAMPLE_PATH=src/objective-c/examples/Sample"
78            " SCHEME=Sample ./build_one_example.sh"
79        ),
80        shell=True,
81        cwd="src/objective-c/tests",
82    )
83    os.rename(
84        "src/objective-c/examples/Sample/Build/Build",
85        "src/objective-c/examples/Sample/Build/Build-%s" % where,
86    )
87
88
89def _render_row(new, label, old):
90    """Render row in 3-column output format."""
91    try:
92        formatted_new = "{:,}".format(int(new))
93    except:
94        formatted_new = new
95    try:
96        formatted_old = "{:,}".format(int(old))
97    except:
98        formatted_old = old
99    return "{:>15}{:>15}{:>15}\n".format(formatted_new, label, formatted_old)
100
101
102def _diff_sign(new, old, diff_threshold=None):
103    """Generate diff sign based on values"""
104    diff_sign = " "
105    if (
106        diff_threshold is not None
107        and abs(new_size[i] - old_size[i]) >= diff_threshold
108    ):
109        diff_sign += "!"
110    if new > old:
111        diff_sign += "(>)"
112    elif new < old:
113        diff_sign += "(<)"
114    else:
115        diff_sign += "(=)"
116    return diff_sign
117
118
119text = "Objective-C binary sizes\n"
120
121build("new")
122new_size = get_size("new")
123old_size = None
124
125if args.diff_base:
126    old = "old"
127    where_am_i = (
128        subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])
129        .decode()
130        .strip()
131    )
132    subprocess.check_call(["git", "checkout", "--", "."])
133    subprocess.check_call(["git", "checkout", args.diff_base])
134    subprocess.check_call(["git", "submodule", "update", "--force"])
135    try:
136        build("old")
137        old_size = get_size("old")
138    finally:
139        subprocess.check_call(["git", "checkout", "--", "."])
140        subprocess.check_call(["git", "checkout", where_am_i])
141        subprocess.check_call(["git", "submodule", "update", "--force"])
142
143text += "**********************STATIC******************\n"
144text += _render_row("New size", "", "Old size")
145if old_size == None:
146    for i in range(0, len(_SIZE_LABELS)):
147        if i == len(_SIZE_LABELS) - 1:
148            # skip line before rendering "Total"
149            text += "\n"
150        text += _render_row(new_size[i], _SIZE_LABELS[i], "")
151else:
152    has_diff = False
153    # go through all labels but "Total"
154    for i in range(0, len(_SIZE_LABELS) - 1):
155        if abs(new_size[i] - old_size[i]) >= _DIFF_THRESHOLD:
156            has_diff = True
157        diff_sign = _diff_sign(
158            new_size[i], old_size[i], diff_threshold=_DIFF_THRESHOLD
159        )
160        text += _render_row(
161            new_size[i], _SIZE_LABELS[i] + diff_sign, old_size[i]
162        )
163
164    # render the "Total"
165    i = len(_SIZE_LABELS) - 1
166    diff_sign = _diff_sign(new_size[i], old_size[i])
167    # skip line before rendering "Total"
168    text += "\n"
169    text += _render_row(new_size[i], _SIZE_LABELS[i] + diff_sign, old_size[i])
170    if not has_diff:
171        text += "\n No significant differences in binary sizes\n"
172text += "\n"
173
174print(text)
175
176check_on_pr.check_on_pr("ObjC Binary Size", "```\n%s\n```" % text)
177