• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3# Copyright 2017 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 csv
19import glob
20import math
21import multiprocessing
22import os
23import pathlib
24import shutil
25import subprocess
26import sys
27
28sys.path.append(
29    os.path.join(
30        os.path.dirname(sys.argv[0]), "..", "..", "run_tests", "python_utils"
31    )
32)
33import check_on_pr
34
35argp = argparse.ArgumentParser(description="Perform diff on microbenchmarks")
36
37argp.add_argument(
38    "-d",
39    "--diff_base",
40    type=str,
41    help="Commit or branch to compare the current one to",
42)
43
44argp.add_argument("-j", "--jobs", type=int, default=multiprocessing.cpu_count())
45
46args = argp.parse_args()
47
48# the libraries for which check bloat difference is calculated
49LIBS = [
50    "libgrpc.so",
51    "libgrpc++.so",
52]
53
54
55def _build(output_dir):
56    """Perform the cmake build under the output_dir."""
57    shutil.rmtree(output_dir, ignore_errors=True)
58    subprocess.check_call("mkdir -p %s" % output_dir, shell=True, cwd=".")
59    subprocess.check_call(
60        [
61            "cmake",
62            "-DgRPC_BUILD_TESTS=OFF",
63            "-DCMAKE_CXX_STANDARD=17",
64            "-DBUILD_SHARED_LIBS=ON",
65            "-DCMAKE_BUILD_TYPE=RelWithDebInfo",
66            '-DCMAKE_C_FLAGS="-gsplit-dwarf"',
67            '-DCMAKE_CXX_FLAGS="-gsplit-dwarf"',
68            "..",
69        ],
70        cwd=output_dir,
71    )
72    subprocess.check_call("make -j%d" % args.jobs, shell=True, cwd=output_dir)
73
74
75def _rank_diff_bytes(diff_bytes):
76    """Determine how significant diff_bytes is, and return a simple integer representing that"""
77    mul = 1
78    if diff_bytes < 0:
79        mul = -1
80        diff_bytes = -diff_bytes
81    if diff_bytes < 2 * 1024:
82        return 0
83    if diff_bytes < 16 * 1024:
84        return 1 * mul
85    if diff_bytes < 128 * 1024:
86        return 2 * mul
87    return 3 * mul
88
89
90_build("bloat_diff_new")
91
92if args.diff_base:
93    where_am_i = (
94        subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])
95        .decode()
96        .strip()
97    )
98    # checkout the diff base (="old")
99    subprocess.check_call(["git", "checkout", args.diff_base])
100    subprocess.check_call(["git", "submodule", "update"])
101    try:
102        _build("bloat_diff_old")
103    finally:
104        # restore the original revision (="new")
105        subprocess.check_call(["git", "checkout", where_am_i])
106        subprocess.check_call(["git", "submodule", "update"])
107
108pathlib.Path("bloaty-build").mkdir(exist_ok=True)
109subprocess.check_call(
110    ["cmake", "-G", "Unix Makefiles", "../third_party/bloaty"],
111    cwd="bloaty-build",
112)
113subprocess.check_call("make -j%d" % args.jobs, shell=True, cwd="bloaty-build")
114
115text = ""
116diff_size = 0
117for lib in LIBS:
118    text += (
119        "****************************************************************\n\n"
120    )
121    text += lib + "\n\n"
122    old_version = glob.glob("bloat_diff_old/%s" % lib)
123    new_version = glob.glob("bloat_diff_new/%s" % lib)
124    for filename in [old_version, new_version]:
125        if filename:
126            subprocess.check_call(
127                "strip %s -o %s.stripped" % (filename[0], filename[0]),
128                shell=True,
129            )
130    assert len(new_version) == 1
131    cmd = "bloaty-build/bloaty -d compileunits,symbols"
132    if old_version:
133        assert len(old_version) == 1
134        text += subprocess.check_output(
135            "%s -n 0 --debug-file=%s --debug-file=%s %s.stripped -- %s.stripped"
136            % (
137                cmd,
138                new_version[0],
139                old_version[0],
140                new_version[0],
141                old_version[0],
142            ),
143            shell=True,
144        ).decode()
145        sections = [
146            x
147            for x in csv.reader(
148                subprocess.check_output(
149                    "bloaty-build/bloaty -n 0 --csv %s -- %s"
150                    % (new_version[0], old_version[0]),
151                    shell=True,
152                )
153                .decode()
154                .splitlines()
155            )
156        ]
157        print(sections)
158        for section in sections[1:]:
159            # skip debug sections for bloat severity calculation
160            if section[0].startswith(".debug"):
161                continue
162            # skip dynamic loader sections too
163            if section[0].startswith(".dyn"):
164                continue
165            diff_size += int(section[2])
166    else:
167        text += subprocess.check_output(
168            "%s %s.stripped -n 0 --debug-file=%s"
169            % (cmd, new_version[0], new_version[0]),
170            shell=True,
171        ).decode()
172    text += "\n\n"
173
174severity = _rank_diff_bytes(diff_size)
175print("SEVERITY: %d" % severity)
176
177print(text)
178check_on_pr.check_on_pr("Bloat Difference", "```\n%s\n```" % text)
179check_on_pr.label_significance_on_pr("bloat", severity)
180