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