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 "-DBUILD_SHARED_LIBS=ON", 64 "-DCMAKE_BUILD_TYPE=RelWithDebInfo", 65 '-DCMAKE_C_FLAGS="-gsplit-dwarf"', 66 '-DCMAKE_CXX_FLAGS="-gsplit-dwarf"', 67 "..", 68 ], 69 cwd=output_dir, 70 ) 71 subprocess.check_call("make -j%d" % args.jobs, shell=True, cwd=output_dir) 72 73 74def _rank_diff_bytes(diff_bytes): 75 """Determine how significant diff_bytes is, and return a simple integer representing that""" 76 mul = 1 77 if diff_bytes < 0: 78 mul = -1 79 diff_bytes = -diff_bytes 80 if diff_bytes < 2 * 1024: 81 return 0 82 if diff_bytes < 16 * 1024: 83 return 1 * mul 84 if diff_bytes < 128 * 1024: 85 return 2 * mul 86 return 3 * mul 87 88 89_build("bloat_diff_new") 90 91if args.diff_base: 92 where_am_i = ( 93 subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"]) 94 .decode() 95 .strip() 96 ) 97 # checkout the diff base (="old") 98 subprocess.check_call(["git", "checkout", args.diff_base]) 99 subprocess.check_call(["git", "submodule", "update"]) 100 try: 101 _build("bloat_diff_old") 102 finally: 103 # restore the original revision (="new") 104 subprocess.check_call(["git", "checkout", where_am_i]) 105 subprocess.check_call(["git", "submodule", "update"]) 106 107pathlib.Path("bloaty-build").mkdir(exist_ok=True) 108subprocess.check_call( 109 ["cmake", "-G", "Unix Makefiles", "../third_party/bloaty"], 110 cwd="bloaty-build", 111) 112subprocess.check_call("make -j%d" % args.jobs, shell=True, cwd="bloaty-build") 113 114text = "" 115diff_size = 0 116for lib in LIBS: 117 text += ( 118 "****************************************************************\n\n" 119 ) 120 text += lib + "\n\n" 121 old_version = glob.glob("bloat_diff_old/%s" % lib) 122 new_version = glob.glob("bloat_diff_new/%s" % lib) 123 for filename in [old_version, new_version]: 124 if filename: 125 subprocess.check_call( 126 "strip %s -o %s.stripped" % (filename[0], filename[0]), 127 shell=True, 128 ) 129 assert len(new_version) == 1 130 cmd = "bloaty-build/bloaty -d compileunits,symbols" 131 if old_version: 132 assert len(old_version) == 1 133 text += subprocess.check_output( 134 "%s -n 0 --debug-file=%s --debug-file=%s %s.stripped -- %s.stripped" 135 % ( 136 cmd, 137 new_version[0], 138 old_version[0], 139 new_version[0], 140 old_version[0], 141 ), 142 shell=True, 143 ).decode() 144 sections = [ 145 x 146 for x in csv.reader( 147 subprocess.check_output( 148 "bloaty-build/bloaty -n 0 --csv %s -- %s" 149 % (new_version[0], old_version[0]), 150 shell=True, 151 ) 152 .decode() 153 .splitlines() 154 ) 155 ] 156 print(sections) 157 for section in sections[1:]: 158 # skip debug sections for bloat severity calculation 159 if section[0].startswith(".debug"): 160 continue 161 # skip dynamic loader sections too 162 if section[0].startswith(".dyn"): 163 continue 164 diff_size += int(section[2]) 165 else: 166 text += subprocess.check_output( 167 "%s %s.stripped -n 0 --debug-file=%s" 168 % (cmd, new_version[0], new_version[0]), 169 shell=True, 170 ).decode() 171 text += "\n\n" 172 173severity = _rank_diff_bytes(diff_size) 174print("SEVERITY: %d" % severity) 175 176print(text) 177check_on_pr.check_on_pr("Bloat Difference", "```\n%s\n```" % text) 178check_on_pr.label_significance_on_pr("bloat", severity) 179