bloat_diff.py
Go to the documentation of this file.
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 
17 import argparse
18 import csv
19 import glob
20 import math
21 import multiprocessing
22 import os
23 import pathlib
24 import shutil
25 import subprocess
26 import sys
27 
28 sys.path.append(
29  os.path.join(os.path.dirname(sys.argv[0]), '..', '..', 'run_tests',
30  'python_utils'))
31 import check_on_pr
32 
33 argp = argparse.ArgumentParser(description='Perform diff on microbenchmarks')
34 
35 argp.add_argument('-d',
36  '--diff_base',
37  type=str,
38  help='Commit or branch to compare the current one to')
39 
40 argp.add_argument('-j', '--jobs', type=int, default=multiprocessing.cpu_count())
41 
42 args = argp.parse_args()
43 
44 # the libraries for which check bloat difference is calculated
45 LIBS = [
46  'libgrpc.so',
47  'libgrpc++.so',
48 ]
49 
50 
51 def _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 
64 def _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 
81 if 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 
94 pathlib.Path('bloaty-build').mkdir(exist_ok=True)
95 subprocess.check_call(
96  ['cmake', '-G', 'Unix Makefiles', '../third_party/bloaty'],
97  cwd='bloaty-build')
98 subprocess.check_call('make -j%d' % args.jobs, shell=True, cwd='bloaty-build')
99 
100 text = ''
101 diff_size = 0
102 for 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 
143 severity = _rank_diff_bytes(diff_size)
144 print("SEVERITY: %d" % severity)
145 
146 print(text)
147 check_on_pr.check_on_pr('Bloat Difference', '```\n%s\n```' % text)
148 check_on_pr.label_significance_on_pr('bloat', severity)
bloat_diff._build
def _build(output_dir)
Definition: bloat_diff.py:51
bloat_diff.int
int
Definition: bloat_diff.py:40
bloat_diff._rank_diff_bytes
def _rank_diff_bytes(diff_bytes)
Definition: bloat_diff.py:64
grpc._common.decode
def decode(b)
Definition: grpc/_common.py:75
len
int len
Definition: abseil-cpp/absl/base/internal/low_level_alloc_test.cc:46


grpc
Author(s):
autogenerated on Thu Mar 13 2025 02:58:39